Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.busybox.net/busybox.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/runit
diff options
context:
space:
mode:
Diffstat (limited to 'runit')
-rw-r--r--runit/Config.in66
-rw-r--r--runit/Kbuild12
-rw-r--r--runit/chpst.c373
-rw-r--r--runit/runit_lib.c982
-rw-r--r--runit/runit_lib.h403
-rw-r--r--runit/runsv.c613
-rw-r--r--runit/runsvdir.c306
-rw-r--r--runit/sv.c360
-rw-r--r--runit/svlogd.c878
9 files changed, 3993 insertions, 0 deletions
diff --git a/runit/Config.in b/runit/Config.in
new file mode 100644
index 000000000..8a7deea72
--- /dev/null
+++ b/runit/Config.in
@@ -0,0 +1,66 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Runit Utilities"
+
+config RUNSV
+ bool "runsv"
+ default n
+ help
+ runsv starts and monitors a service and optionally an appendant log
+ service.
+
+config RUNSVDIR
+ bool "runsvdir"
+ default n
+ help
+ runsvdir starts a runsv process for each subdirectory, or symlink to
+ a directory, in the services directory dir, up to a limit of 1000
+ subdirectories, and restarts a runsv process if it terminates.
+
+config SV
+ bool "sv"
+ default n
+ help
+ sv reports the current status and controls the state of services
+ monitored by the runsv supervisor.
+
+config SVLOGD
+ bool "svlogd"
+ default n
+ help
+ svlogd continuously reads log data from its standard input, optionally
+ filters log messages, and writes the data to one or more automatically
+ rotated logs.
+
+config CHPST
+ bool "chpst"
+ default n
+ help
+ chpst changes the process state according to the given options, and
+ execs specified program.
+
+config SETUIDGID
+ bool "setuidgid"
+ help
+ Sets soft resource limits as specified by options
+
+config ENVUIDGID
+ bool "envuidgid"
+ help
+ Sets $UID to account's uid and $GID to account's gid
+
+config ENVDIR
+ bool "envdir"
+ help
+ Sets various environment variables as specified by files
+ in the given directory
+
+config SOFTLIMIT
+ bool "softlimit"
+ help
+ Sets soft resource limits as specified by options
+
+endmenu
diff --git a/runit/Kbuild b/runit/Kbuild
new file mode 100644
index 000000000..ad1706cb6
--- /dev/null
+++ b/runit/Kbuild
@@ -0,0 +1,12 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_RUNSV) += runsv.o runit_lib.o
+lib-$(CONFIG_RUNSVDIR) += runsvdir.o runit_lib.o
+lib-$(CONFIG_SV) += sv.o runit_lib.o
+lib-$(CONFIG_SVLOGD) += svlogd.o runit_lib.o
+lib-$(CONFIG_CHPST) += chpst.o
diff --git a/runit/chpst.c b/runit/chpst.c
new file mode 100644
index 000000000..f8e63031f
--- /dev/null
+++ b/runit/chpst.c
@@ -0,0 +1,373 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* Dependencies on runit_lib.c removed */
+
+#include "busybox.h"
+
+#include <dirent.h>
+
+// Must match constants in chpst_main!
+#define OPT_verbose (option_mask32 & 0x2000)
+#define OPT_pgrp (option_mask32 & 0x4000)
+#define OPT_nostdin (option_mask32 & 0x8000)
+#define OPT_nostdout (option_mask32 & 0x10000)
+#define OPT_nostderr (option_mask32 & 0x20000)
+
+static char *set_user;
+static char *env_user;
+static const char *env_dir;
+static long limitd = -2;
+static long limits = -2;
+static long limitl = -2;
+static long limita = -2;
+static long limito = -2;
+static long limitp = -2;
+static long limitf = -2;
+static long limitc = -2;
+static long limitr = -2;
+static long limitt = -2;
+static int nicelvl;
+static const char *root;
+
+static void suidgid(char *user)
+{
+ struct bb_uidgid_t ugid;
+
+ if (!uidgid_get(&ugid, user)) {
+ bb_error_msg_and_die("unknown user/group: %s", user);
+ }
+ if (setgroups(1, &ugid.gid) == -1)
+ bb_perror_msg_and_die("setgroups");
+ xsetgid(ugid.gid);
+ xsetuid(ugid.uid);
+}
+
+static void euidgid(char *user)
+{
+ struct bb_uidgid_t ugid;
+
+ if (!uidgid_get(&ugid, user)) {
+ bb_error_msg_and_die("unknown user/group: %s", user);
+ }
+ xsetenv("GID", utoa(ugid.gid));
+ xsetenv("UID", utoa(ugid.uid));
+}
+
+static void edir(const char *directory_name)
+{
+ int wdir;
+ DIR *dir;
+ struct dirent *d;
+ int fd;
+
+ wdir = xopen(".", O_RDONLY | O_NDELAY);
+ xchdir(directory_name);
+ dir = opendir(".");
+ if (!dir)
+ bb_perror_msg_and_die("opendir %s", directory_name);
+ for (;;) {
+ errno = 0;
+ d = readdir(dir);
+ if (!d) {
+ if (errno)
+ bb_perror_msg_and_die("readdir %s",
+ directory_name);
+ break;
+ }
+ if (d->d_name[0] == '.') continue;
+ fd = open(d->d_name, O_RDONLY | O_NDELAY);
+ if (fd < 0) {
+ if ((errno == EISDIR) && env_dir) {
+ if (OPT_verbose)
+ bb_perror_msg("warning: %s/%s is a directory",
+ directory_name, d->d_name);
+ continue;
+ } else
+ bb_perror_msg_and_die("open %s/%s",
+ directory_name, d->d_name);
+ }
+ if (fd >= 0) {
+ char buf[256];
+ char *tail;
+ int size;
+
+ size = safe_read(fd, buf, sizeof(buf)-1);
+ if (size < 0)
+ bb_perror_msg_and_die("read %s/%s",
+ directory_name, d->d_name);
+ if (size == 0) {
+ unsetenv(d->d_name);
+ continue;
+ }
+ buf[size] = '\n';
+ tail = memchr(buf, '\n', sizeof(buf));
+ /* skip trailing whitespace */;
+ while (1) {
+ if (tail[0]==' ') tail[0] = '\0';
+ if (tail[0]=='\t') tail[0] = '\0';
+ if (tail[0]=='\n') tail[0] = '\0';
+ if (tail == buf) break;
+ tail--;
+ }
+ xsetenv(d->d_name, buf);
+ }
+ }
+ closedir(dir);
+ if (fchdir(wdir) == -1) bb_perror_msg_and_die("fchdir");
+ close(wdir);
+}
+
+static void limit(int what, long l)
+{
+ struct rlimit r;
+
+ if (getrlimit(what, &r) == -1) bb_perror_msg_and_die("getrlimit");
+ if ((l < 0) || (l > r.rlim_max))
+ r.rlim_cur = r.rlim_max;
+ else
+ r.rlim_cur = l;
+ if (setrlimit(what, &r) == -1) bb_perror_msg_and_die("setrlimit");
+}
+
+static void slimit(void)
+{
+ if (limitd >= -1) {
+#ifdef RLIMIT_DATA
+ limit(RLIMIT_DATA, limitd);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_DATA");
+#endif
+ }
+ if (limits >= -1) {
+#ifdef RLIMIT_STACK
+ limit(RLIMIT_STACK, limits);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_STACK");
+#endif
+ }
+ if (limitl >= -1) {
+#ifdef RLIMIT_MEMLOCK
+ limit(RLIMIT_MEMLOCK, limitl);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_MEMLOCK");
+#endif
+ }
+ if (limita >= -1) {
+#ifdef RLIMIT_VMEM
+ limit(RLIMIT_VMEM, limita);
+#else
+#ifdef RLIMIT_AS
+ limit(RLIMIT_AS, limita);
+#else
+ if (OPT_verbose)
+ bb_error_msg("system does not support %s",
+ "RLIMIT_VMEM");
+#endif
+#endif
+ }
+ if (limito >= -1) {
+#ifdef RLIMIT_NOFILE
+ limit(RLIMIT_NOFILE, limito);
+#else
+#ifdef RLIMIT_OFILE
+ limit(RLIMIT_OFILE, limito);
+#else
+ if (OPT_verbose)
+ bb_error_msg("system does not support %s",
+ "RLIMIT_NOFILE");
+#endif
+#endif
+ }
+ if (limitp >= -1) {
+#ifdef RLIMIT_NPROC
+ limit(RLIMIT_NPROC, limitp);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_NPROC");
+#endif
+ }
+ if (limitf >= -1) {
+#ifdef RLIMIT_FSIZE
+ limit(RLIMIT_FSIZE, limitf);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_FSIZE");
+#endif
+ }
+ if (limitc >= -1) {
+#ifdef RLIMIT_CORE
+ limit(RLIMIT_CORE, limitc);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_CORE");
+#endif
+ }
+ if (limitr >= -1) {
+#ifdef RLIMIT_RSS
+ limit(RLIMIT_RSS, limitr);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_RSS");
+#endif
+ }
+ if (limitt >= -1) {
+#ifdef RLIMIT_CPU
+ limit(RLIMIT_CPU, limitt);
+#else
+ if (OPT_verbose) bb_error_msg("system does not support %s",
+ "RLIMIT_CPU");
+#endif
+ }
+}
+
+/* argv[0] */
+static void setuidgid(int, char **);
+static void envuidgid(int, char **);
+static void envdir(int, char **);
+static void softlimit(int, char **);
+
+int chpst_main(int argc, char **argv)
+{
+ if (applet_name[3] == 'd') envdir(argc, argv);
+ if (applet_name[1] == 'o') softlimit(argc, argv);
+ if (applet_name[0] == 's') setuidgid(argc, argv);
+ if (applet_name[0] == 'e') envuidgid(argc, argv);
+ // otherwise we are chpst
+
+ {
+ char *m,*d,*o,*p,*f,*c,*r,*t,*n;
+ getopt32(argc, argv, "+u:U:e:m:d:o:p:f:c:r:t:/:n:vP012",
+ &set_user,&env_user,&env_dir,
+ &m,&d,&o,&p,&f,&c,&r,&t,&root,&n);
+ // if (option_mask32 & 0x1) // -u
+ // if (option_mask32 & 0x2) // -U
+ // if (option_mask32 & 0x4) // -e
+ if (option_mask32 & 0x8) limits = limitl = limita = limitd = xatoul(m); // -m
+ if (option_mask32 & 0x10) limitd = xatoul(d); // -d
+ if (option_mask32 & 0x20) limito = xatoul(o); // -o
+ if (option_mask32 & 0x40) limitp = xatoul(p); // -p
+ if (option_mask32 & 0x80) limitf = xatoul(f); // -f
+ if (option_mask32 & 0x100) limitc = xatoul(c); // -c
+ if (option_mask32 & 0x200) limitr = xatoul(r); // -r
+ if (option_mask32 & 0x400) limitt = xatoul(t); // -t
+ // if (option_mask32 & 0x800) // -/
+ if (option_mask32 & 0x1000) nicelvl = xatoi(n); // -n
+ // The below consts should match #defines at top!
+ //if (option_mask32 & 0x2000) OPT_verbose = 1; // -v
+ //if (option_mask32 & 0x4000) OPT_pgrp = 1; // -P
+ //if (option_mask32 & 0x8000) OPT_nostdin = 1; // -0
+ //if (option_mask32 & 0x10000) OPT_nostdout = 1; // -1
+ //if (option_mask32 & 0x20000) OPT_nostderr = 1; // -2
+ }
+ argv += optind;
+ if (!argv || !*argv) bb_show_usage();
+
+ if (OPT_pgrp) setsid();
+ if (env_dir) edir(env_dir);
+ if (root) {
+ xchdir(root);
+ if (chroot(".") == -1)
+ bb_perror_msg_and_die("chroot");
+ }
+ slimit();
+ if (nicelvl) {
+ errno = 0;
+ if (nice(nicelvl) == -1)
+ bb_perror_msg_and_die("nice");
+ }
+ if (env_user) euidgid(env_user);
+ if (set_user) suidgid(set_user);
+ if (OPT_nostdin) close(0);
+ if (OPT_nostdout) close(1);
+ if (OPT_nostderr) close(2);
+ execvp(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
+
+static void setuidgid(int argc, char **argv)
+{
+ const char *account;
+
+ account = *++argv;
+ if (!account) bb_show_usage();
+ if (!*++argv) bb_show_usage();
+ suidgid((char*)account);
+ execvp(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
+
+static void envuidgid(int argc, char **argv)
+{
+ const char *account;
+
+ account = *++argv;
+ if (!account) bb_show_usage();
+ if (!*++argv) bb_show_usage();
+ euidgid((char*)account);
+ execvp(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
+
+static void envdir(int argc, char **argv)
+{
+ const char *dir;
+
+ dir = *++argv;
+ if (!dir) bb_show_usage();
+ if (!*++argv) bb_show_usage();
+ edir(dir);
+ execvp(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
+
+static void softlimit(int argc, char **argv)
+{
+ char *a,*c,*d,*f,*l,*m,*o,*p,*r,*s,*t;
+ getopt32(argc, argv, "+a:c:d:f:l:m:o:p:r:s:t:",
+ &a,&c,&d,&f,&l,&m,&o,&p,&r,&s,&t);
+ if (option_mask32 & 0x001) limita = xatoul(a); // -a
+ if (option_mask32 & 0x002) limitc = xatoul(c); // -c
+ if (option_mask32 & 0x004) limitd = xatoul(d); // -d
+ if (option_mask32 & 0x008) limitf = xatoul(f); // -f
+ if (option_mask32 & 0x010) limitl = xatoul(l); // -l
+ if (option_mask32 & 0x020) limits = limitl = limita = limitd = xatoul(m); // -m
+ if (option_mask32 & 0x040) limito = xatoul(o); // -o
+ if (option_mask32 & 0x080) limitp = xatoul(p); // -p
+ if (option_mask32 & 0x100) limitr = xatoul(r); // -r
+ if (option_mask32 & 0x200) limits = xatoul(s); // -s
+ if (option_mask32 & 0x400) limitt = xatoul(t); // -t
+ argv += optind;
+ if (!argv[0]) bb_show_usage();
+ slimit();
+ execvp(argv[0], argv);
+ bb_perror_msg_and_die("exec %s", argv[0]);
+}
diff --git a/runit/runit_lib.c b/runit/runit_lib.c
new file mode 100644
index 000000000..5ebbc5840
--- /dev/null
+++ b/runit/runit_lib.c
@@ -0,0 +1,982 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* Collected into one file from runit's many tiny files */
+/* TODO: review, eliminate unneeded stuff, move good stuff to libbb */
+
+#include <sys/poll.h>
+#include <sys/file.h>
+#include "libbb.h"
+#include "runit_lib.h"
+
+/*** buffer.c ***/
+
+void buffer_init(buffer *s,int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len)
+{
+ s->x = buf;
+ s->fd = fd;
+ s->op = op;
+ s->p = 0;
+ s->n = len;
+}
+
+
+/*** buffer_get.c ***/
+
+static int oneread(int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len)
+{
+ int r;
+
+ for (;;) {
+ r = op(fd,buf,len);
+ if (r == -1) if (errno == EINTR) continue;
+ return r;
+ }
+}
+
+static int getthis(buffer *s,char *buf,unsigned len)
+{
+ if (len > s->p) len = s->p;
+ s->p -= len;
+ memcpy(buf,s->x + s->n,len);
+ s->n += len;
+ return len;
+}
+
+int buffer_feed(buffer *s)
+{
+ int r;
+
+ if (s->p) return s->p;
+ r = oneread(s->op,s->fd,s->x,s->n);
+ if (r <= 0) return r;
+ s->p = r;
+ s->n -= r;
+ if (s->n > 0) memmove(s->x + s->n,s->x,r);
+ return r;
+}
+
+int buffer_bget(buffer *s,char *buf,unsigned len)
+{
+ int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,s->n);
+ r = buffer_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+int buffer_get(buffer *s,char *buf,unsigned len)
+{
+ int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,len);
+ r = buffer_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+char *buffer_peek(buffer *s)
+{
+ return s->x + s->n;
+}
+
+void buffer_seek(buffer *s,unsigned len)
+{
+ s->n += len;
+ s->p -= len;
+}
+
+
+/*** buffer_put.c ***/
+
+static int allwrite(int (*op)(int fd,char *buf,unsigned len),int fd,const char *buf,unsigned len)
+{
+ int w;
+
+ while (len) {
+ w = op(fd,(char*)buf,len);
+ if (w == -1) {
+ if (errno == EINTR) continue;
+ return -1; /* note that some data may have been written */
+ }
+ if (w == 0) ; /* luser's fault */
+ buf += w;
+ len -= w;
+ }
+ return 0;
+}
+
+int buffer_flush(buffer *s)
+{
+ int p;
+
+ p = s->p;
+ if (!p) return 0;
+ s->p = 0;
+ return allwrite(s->op,s->fd,s->x,p);
+}
+
+int buffer_putalign(buffer *s,const char *buf,unsigned len)
+{
+ unsigned n;
+
+ while (len > (n = s->n - s->p)) {
+ memcpy(s->x + s->p,buf,n);
+ s->p += n;
+ buf += n;
+ len -= n;
+ if (buffer_flush(s) == -1) return -1;
+ }
+ /* now len <= s->n - s->p */
+ memcpy(s->x + s->p,buf,len);
+ s->p += len;
+ return 0;
+}
+
+int buffer_put(buffer *s,const char *buf,unsigned len)
+{
+ unsigned n;
+
+ n = s->n;
+ if (len > n - s->p) {
+ if (buffer_flush(s) == -1) return -1;
+ /* now s->p == 0 */
+ if (n < BUFFER_OUTSIZE) n = BUFFER_OUTSIZE;
+ while (len > s->n) {
+ if (n > len) n = len;
+ if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
+ buf += n;
+ len -= n;
+ }
+ }
+ /* now len <= s->n - s->p */
+ memcpy(s->x + s->p,buf,len);
+ s->p += len;
+ return 0;
+}
+
+int buffer_putflush(buffer *s,const char *buf,unsigned len)
+{
+ if (buffer_flush(s) == -1) return -1;
+ return allwrite(s->op,s->fd,buf,len);
+}
+
+int buffer_putsalign(buffer *s,const char *buf)
+{
+ return buffer_putalign(s,buf,strlen(buf));
+}
+
+int buffer_puts(buffer *s,const char *buf)
+{
+ return buffer_put(s,buf,strlen(buf));
+}
+
+int buffer_putsflush(buffer *s,const char *buf)
+{
+ return buffer_putflush(s,buf,strlen(buf));
+}
+
+
+/*** buffer_read.c ***/
+
+int buffer_unixread(int fd,char *buf,unsigned len)
+{
+ return read(fd,buf,len);
+}
+
+
+/*** buffer_write.c ***/
+
+int buffer_unixwrite(int fd,char *buf,unsigned len)
+{
+ return write(fd,buf,len);
+}
+
+
+/*** byte_chr.c ***/
+
+unsigned byte_chr(char *s,unsigned n,int c)
+{
+ char ch;
+ char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ }
+ return t - s;
+}
+
+
+/*** coe.c ***/
+
+int coe(int fd)
+{
+ return fcntl(fd,F_SETFD,FD_CLOEXEC);
+}
+
+
+/*** fd_copy.c ***/
+
+int fd_copy(int to,int from)
+{
+ if (to == from) return 0;
+ if (fcntl(from,F_GETFL,0) == -1) return -1;
+ close(to);
+ if (fcntl(from,F_DUPFD,to) == -1) return -1;
+ return 0;
+}
+
+
+/*** fd_move.c ***/
+
+int fd_move(int to,int from)
+{
+ if (to == from) return 0;
+ if (fd_copy(to,from) == -1) return -1;
+ close(from);
+ return 0;
+}
+
+
+/*** fifo.c ***/
+
+int fifo_make(const char *fn,int mode) { return mkfifo(fn,mode); }
+
+
+/*** fmt_ptime.c ***/
+
+unsigned fmt_ptime(char *s, struct taia *ta) {
+ struct tm *t;
+ unsigned long u;
+
+ if (ta->sec.x < 4611686018427387914ULL) return 0; /* impossible? */
+ u = ta->sec.x -4611686018427387914ULL;
+ if (!(t = gmtime((time_t*)&u))) return 0;
+ fmt_ulong(s, 1900 + t->tm_year);
+ s[4] = '-'; fmt_uint0(&s[5], t->tm_mon+1, 2);
+ s[7] = '-'; fmt_uint0(&s[8], t->tm_mday, 2);
+ s[10] = '_'; fmt_uint0(&s[11], t->tm_hour, 2);
+ s[13] = ':'; fmt_uint0(&s[14], t->tm_min, 2);
+ s[16] = ':'; fmt_uint0(&s[17], t->tm_sec, 2);
+ s[19] = '.'; fmt_uint0(&s[20], ta->nano, 9);
+ return 25;
+}
+
+unsigned fmt_taia(char *s, struct taia *t) {
+ static char hex[16] = "0123456789abcdef";
+ static char pack[TAIA_PACK];
+ int i;
+
+ taia_pack(pack, t);
+ s[0] = '@';
+ for (i = 0; i < 12; ++i) {
+ s[i*2+1] = hex[(pack[i] >> 4) &15];
+ s[i*2+2] = hex[pack[i] &15];
+ }
+ return 25;
+}
+
+
+/*** fmt_uint.c ***/
+
+unsigned fmt_uint(char *s,unsigned u)
+{
+ return fmt_ulong(s,u);
+}
+
+
+/*** fmt_uint0.c ***/
+
+unsigned fmt_uint0(char *s,unsigned u,unsigned n)
+{
+ unsigned len;
+ len = fmt_uint(FMT_LEN,u);
+ while (len < n) { if (s) *s++ = '0'; ++len; }
+ if (s) fmt_uint(s,u);
+ return len;
+}
+
+
+/*** fmt_ulong.c ***/
+
+unsigned fmt_ulong(char *s,unsigned long u)
+{
+ unsigned len; unsigned long q;
+ len = 1; q = u;
+ while (q > 9) { ++len; q /= 10; }
+ if (s) {
+ s += len;
+ do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
+ }
+ return len;
+}
+
+
+/*** tai_now.c ***/
+
+void tai_now(struct tai *t)
+{
+ tai_unix(t,time((time_t *) 0));
+}
+
+
+/*** tai_pack.c ***/
+
+void tai_pack(char *s,const struct tai *t)
+{
+ uint64_t x;
+
+ x = t->x;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x & 255; x >>= 8;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
+
+
+/*** tai_sub.c ***/
+
+void tai_sub(struct tai *t,const struct tai *u,const struct tai *v)
+{
+ t->x = u->x - v->x;
+}
+
+
+/*** tai_unpack.c ***/
+
+void tai_unpack(const char *s,struct tai *t)
+{
+ uint64_t x;
+
+ x = (unsigned char) s[0];
+ x <<= 8; x += (unsigned char) s[1];
+ x <<= 8; x += (unsigned char) s[2];
+ x <<= 8; x += (unsigned char) s[3];
+ x <<= 8; x += (unsigned char) s[4];
+ x <<= 8; x += (unsigned char) s[5];
+ x <<= 8; x += (unsigned char) s[6];
+ x <<= 8; x += (unsigned char) s[7];
+ t->x = x;
+}
+
+
+/*** taia_add.c ***/
+
+/* XXX: breaks tai encapsulation */
+
+void taia_add(struct taia *t,const struct taia *u,const struct taia *v)
+{
+ t->sec.x = u->sec.x + v->sec.x;
+ t->nano = u->nano + v->nano;
+ t->atto = u->atto + v->atto;
+ if (t->atto > 999999999UL) {
+ t->atto -= 1000000000UL;
+ ++t->nano;
+ }
+ if (t->nano > 999999999UL) {
+ t->nano -= 1000000000UL;
+ ++t->sec.x;
+ }
+}
+
+
+/*** taia_approx.c ***/
+
+double taia_approx(const struct taia *t)
+{
+ return tai_approx(&t->sec) + taia_frac(t);
+}
+
+
+/*** taia_frac.c ***/
+
+double taia_frac(const struct taia *t)
+{
+ return (t->atto * 0.000000001 + t->nano) * 0.000000001;
+}
+
+
+/*** taia_less.c ***/
+
+/* XXX: breaks tai encapsulation */
+
+int taia_less(const struct taia *t,const struct taia *u)
+{
+ if (t->sec.x < u->sec.x) return 1;
+ if (t->sec.x > u->sec.x) return 0;
+ if (t->nano < u->nano) return 1;
+ if (t->nano > u->nano) return 0;
+ return t->atto < u->atto;
+}
+
+
+/*** taia_now.c ***/
+
+void taia_now(struct taia *t)
+{
+ struct timeval now;
+ gettimeofday(&now,(struct timezone *) 0);
+ tai_unix(&t->sec,now.tv_sec);
+ t->nano = 1000 * now.tv_usec + 500;
+ t->atto = 0;
+}
+
+
+/*** taia_pack.c ***/
+
+void taia_pack(char *s,const struct taia *t)
+{
+ unsigned long x;
+
+ tai_pack(s,&t->sec);
+ s += 8;
+
+ x = t->atto;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x;
+ x = t->nano;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
+
+
+/*** taia_sub.c ***/
+
+/* XXX: breaks tai encapsulation */
+
+void taia_sub(struct taia *t,const struct taia *u,const struct taia *v)
+{
+ unsigned long unano = u->nano;
+ unsigned long uatto = u->atto;
+
+ t->sec.x = u->sec.x - v->sec.x;
+ t->nano = unano - v->nano;
+ t->atto = uatto - v->atto;
+ if (t->atto > uatto) {
+ t->atto += 1000000000UL;
+ --t->nano;
+ }
+ if (t->nano > unano) {
+ t->nano += 1000000000UL;
+ --t->sec.x;
+ }
+}
+
+
+/*** taia_uint.c ***/
+
+/* XXX: breaks tai encapsulation */
+
+void taia_uint(struct taia *t,unsigned s)
+{
+ t->sec.x = s;
+ t->nano = 0;
+ t->atto = 0;
+}
+
+
+/*** stralloc_cat.c ***/
+#if 0
+
+int stralloc_cat(stralloc *sato,const stralloc *safrom)
+{
+ return stralloc_catb(sato,safrom->s,safrom->len);
+}
+
+
+/*** stralloc_catb.c ***/
+
+int stralloc_catb(stralloc *sa,const char *s,unsigned n)
+{
+ if (!sa->s) return stralloc_copyb(sa,s,n);
+ if (!stralloc_readyplus(sa,n + 1)) return 0;
+ memcpy(sa->s + sa->len,s,n);
+ sa->len += n;
+ sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
+
+
+/*** stralloc_cats.c ***/
+
+int stralloc_cats(stralloc *sa,const char *s)
+{
+ return stralloc_catb(sa,s,strlen(s));
+}
+
+
+/*** stralloc_eady.c ***/
+
+GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready)
+GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus)
+
+
+/*** stralloc_opyb.c ***/
+
+int stralloc_copyb(stralloc *sa,const char *s,unsigned n)
+{
+ if (!stralloc_ready(sa,n + 1)) return 0;
+ memcpy(sa->s,s,n);
+ sa->len = n;
+ sa->s[n] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
+
+
+/*** stralloc_opys.c ***/
+
+int stralloc_copys(stralloc *sa,const char *s)
+{
+ return stralloc_copyb(sa,s,strlen(s));
+}
+
+
+/*** stralloc_pend.c ***/
+
+GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append)
+
+#endif /* stralloc */
+
+/*** iopause.c ***/
+
+void iopause(iopause_fd *x,unsigned len,struct taia *deadline,struct taia *stamp)
+{
+ struct taia t;
+ int millisecs;
+ double d;
+ int i;
+
+ if (taia_less(deadline,stamp))
+ millisecs = 0;
+ else {
+ t = *stamp;
+ taia_sub(&t,deadline,&t);
+ d = taia_approx(&t);
+ if (d > 1000.0) d = 1000.0;
+ millisecs = d * 1000.0 + 20.0;
+ }
+
+ for (i = 0;i < len;++i)
+ x[i].revents = 0;
+
+ poll(x,len,millisecs);
+ /* XXX: some kernels apparently need x[0] even if len is 0 */
+ /* XXX: how to handle EAGAIN? are kernels really this dumb? */
+ /* XXX: how to handle EINVAL? when exactly can this happen? */
+}
+
+
+/*** lock_ex.c ***/
+
+int lock_ex(int fd)
+{
+ return flock(fd,LOCK_EX);
+}
+
+
+/*** lock_exnb.c ***/
+
+int lock_exnb(int fd)
+{
+ return flock(fd,LOCK_EX | LOCK_NB);
+}
+
+
+/*** open_append.c ***/
+
+int open_append(const char *fn)
+{
+ return open(fn, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+}
+
+
+/*** open_read.c ***/
+
+int open_read(const char *fn)
+{
+ return open(fn, O_RDONLY|O_NDELAY);
+}
+
+
+/*** open_trunc.c ***/
+
+int open_trunc(const char *fn)
+{
+ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644);
+}
+
+
+/*** open_write.c ***/
+
+int open_write(const char *fn)
+{
+ return open(fn, O_WRONLY|O_NDELAY);
+}
+
+
+/*** openreadclose.c ***/
+#if 0
+int openreadclose(const char *fn,stralloc *sa,unsigned bufsize)
+{
+ int fd;
+ fd = open_read(fn);
+ if (fd == -1) {
+ if (errno == ENOENT) return 0;
+ return -1;
+ }
+ if (readclose(fd,sa,bufsize) == -1) return -1;
+ return 1;
+}
+#endif
+
+
+/*** pathexec_env.c ***/
+#if 0
+static stralloc plus;
+static stralloc tmp;
+
+int pathexec_env(const char *s,const char *t)
+{
+ if (!s) return 1;
+ if (!stralloc_copys(&tmp,s)) return 0;
+ if (t) {
+ if (!stralloc_cats(&tmp,"=")) return 0;
+ if (!stralloc_cats(&tmp,t)) return 0;
+ }
+ if (!stralloc_0(&tmp)) return 0;
+ return stralloc_cat(&plus,&tmp);
+}
+
+void pathexec(char **argv)
+{
+ char **e;
+ unsigned elen;
+ unsigned i;
+ unsigned j;
+ unsigned split;
+ unsigned t;
+
+ if (!stralloc_cats(&plus,"")) return;
+
+ elen = 0;
+ for (i = 0;environ[i];++i)
+ ++elen;
+ for (i = 0;i < plus.len;++i)
+ if (!plus.s[i])
+ ++elen;
+
+ e = malloc((elen + 1) * sizeof(char *));
+ if (!e) return;
+
+ elen = 0;
+ for (i = 0;environ[i];++i)
+ e[elen++] = environ[i];
+
+ j = 0;
+ for (i = 0;i < plus.len;++i)
+ if (!plus.s[i]) {
+ split = str_chr(plus.s + j,'=');
+ for (t = 0;t < elen;++t)
+ if (memcmp(plus.s + j,e[t],split) == 0)
+ if (e[t][split] == '=') {
+ --elen;
+ e[t] = e[elen];
+ break;
+ }
+ if (plus.s[j + split])
+ e[elen++] = plus.s + j;
+ j = i + 1;
+ }
+ e[elen] = 0;
+
+ pathexec_run(*argv,argv,e);
+ free(e);
+}
+#endif
+
+/*** pathexec_run.c ***/
+#if 0
+static stralloc tmp;
+
+void pathexec_run(const char *file,char *const *argv,char *const *envp)
+{
+ const char *path;
+ unsigned split;
+ int savederrno;
+
+ if (file[str_chr(file,'/')]) {
+ execve(file,argv,envp);
+ return;
+ }
+
+ path = getenv("PATH");
+ if (!path) path = "/bin:/usr/bin";
+
+ savederrno = 0;
+ for (;;) {
+ split = str_chr(path,':');
+ if (!stralloc_copyb(&tmp,path,split)) return;
+ if (!split)
+ if (!stralloc_cats(&tmp,".")) return;
+ if (!stralloc_cats(&tmp,"/")) return;
+ if (!stralloc_cats(&tmp,file)) return;
+ if (!stralloc_0(&tmp)) return;
+
+ execve(tmp.s,argv,envp);
+ if (errno != ENOENT) {
+ savederrno = errno;
+ if ((errno != EACCES) && (errno != EPERM) && (errno != EISDIR)) return;
+ }
+
+ if (!path[split]) {
+ if (savederrno) errno = savederrno;
+ return;
+ }
+ path += split;
+ path += 1;
+ }
+}
+#endif
+
+/*** pmatch.c ***/
+
+unsigned pmatch(const char *p, const char *s, unsigned len) {
+ for (;;) {
+ char c = *p++;
+ if (!c) return !len;
+ switch (c) {
+ case '*':
+ if (!(c = *p)) return 1;
+ for (;;) {
+ if (!len) return 0;
+ if (*s == c) break;
+ ++s; --len;
+ }
+ continue;
+ case '+':
+ if ((c = *p++) != *s) return 0;
+ for (;;) {
+ if (!len) return 1;
+ if (*s != c) break;
+ ++s; --len;
+ }
+ continue;
+ /*
+ case '?':
+ if (*p == '?') {
+ if (*s != '?') return 0;
+ ++p;
+ }
+ ++s; --len;
+ continue;
+ */
+ default:
+ if (!len) return 0;
+ if (*s != c) return 0;
+ ++s; --len;
+ continue;
+ }
+ }
+ return 0;
+}
+
+
+/*** prot.c ***/
+
+int prot_gid(int gid)
+{
+ gid_t x = gid;
+ if (setgroups(1,&x) == -1) return -1;
+ return setgid(gid); /* _should_ be redundant, but on some systems it isn't */
+}
+
+int prot_uid(int uid)
+{
+ return setuid(uid);
+}
+
+
+/*** readclose.c ***/
+#if 0
+int readclose_append(int fd,stralloc *sa,unsigned bufsize)
+{
+ int r;
+ for (;;) {
+ if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
+ r = read(fd,sa->s + sa->len,bufsize);
+ if (r == -1) if (errno == EINTR) continue;
+ if (r <= 0) { close(fd); return r; }
+ sa->len += r;
+ }
+}
+
+int readclose(int fd,stralloc *sa,unsigned bufsize)
+{
+ if (!stralloc_copys(sa,"")) { close(fd); return -1; }
+ return readclose_append(fd,sa,bufsize);
+}
+#endif
+
+/*** scan_ulong.c ***/
+
+unsigned scan_ulong(const char *s,unsigned long *u)
+{
+ unsigned pos = 0;
+ unsigned long result = 0;
+ unsigned long c;
+ while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) {
+ result = result * 10 + c;
+ ++pos;
+ }
+ *u = result;
+ return pos;
+}
+
+
+/*** seek_set.c ***/
+
+int seek_set(int fd,seek_pos pos)
+{
+ if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1; return 0;
+}
+
+
+/*** sig.c ***/
+
+int sig_alarm = SIGALRM;
+int sig_child = SIGCHLD;
+int sig_cont = SIGCONT;
+int sig_hangup = SIGHUP;
+int sig_int = SIGINT;
+int sig_pipe = SIGPIPE;
+int sig_term = SIGTERM;
+
+void (*sig_defaulthandler)(int) = SIG_DFL;
+void (*sig_ignorehandler)(int) = SIG_IGN;
+
+
+/*** sig_block.c ***/
+
+void sig_block(int sig)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss,sig);
+ sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0);
+}
+
+void sig_unblock(int sig)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss,sig);
+ sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0);
+}
+
+void sig_blocknone(void)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+}
+
+
+/*** sig_catch.c ***/
+
+void sig_catch(int sig,void (*f)(int))
+{
+ struct sigaction sa;
+ sa.sa_handler = f;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(sig,&sa,(struct sigaction *) 0);
+}
+
+
+/*** sig_pause.c ***/
+
+void sig_pause(void)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigsuspend(&ss);
+}
+
+
+/*** str_chr.c ***/
+
+unsigned str_chr(const char *s,int c)
+{
+ char ch;
+ const char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ }
+ return t - s;
+}
+
+
+/*** wait_nohang.c ***/
+
+int wait_nohang(int *wstat)
+{
+ return waitpid(-1,wstat,WNOHANG);
+}
+
+
+/*** wait_pid.c ***/
+
+int wait_pid(int *wstat, int pid)
+{
+ int r;
+
+ do
+ r = waitpid(pid,wstat,0);
+ while ((r == -1) && (errno == EINTR));
+ return r;
+}
diff --git a/runit/runit_lib.h b/runit/runit_lib.h
new file mode 100644
index 000000000..1f911919d
--- /dev/null
+++ b/runit/runit_lib.h
@@ -0,0 +1,403 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*** buffer.h ***/
+
+typedef struct buffer {
+ char *x;
+ unsigned p;
+ unsigned n;
+ int fd;
+ int (*op)(int fd,char *buf,unsigned len);
+} buffer;
+
+#define BUFFER_INIT(op,fd,buf,len) { (buf), 0, (len), (fd), (op) }
+#define BUFFER_INSIZE 8192
+#define BUFFER_OUTSIZE 8192
+
+extern void buffer_init(buffer *,int (*)(int fd,char *buf,unsigned len),int,char *,unsigned);
+
+extern int buffer_flush(buffer *);
+extern int buffer_put(buffer *,const char *,unsigned);
+extern int buffer_putalign(buffer *,const char *,unsigned);
+extern int buffer_putflush(buffer *,const char *,unsigned);
+extern int buffer_puts(buffer *,const char *);
+extern int buffer_putsalign(buffer *,const char *);
+extern int buffer_putsflush(buffer *,const char *);
+
+#define buffer_PUTC(s,c) \
+ ( ((s)->n != (s)->p) \
+ ? ( (s)->x[(s)->p++] = (c), 0 ) \
+ : buffer_put((s),&(c),1) \
+ )
+
+extern int buffer_get(buffer *,char *,unsigned);
+extern int buffer_bget(buffer *,char *,unsigned);
+extern int buffer_feed(buffer *);
+
+extern char *buffer_peek(buffer *);
+extern void buffer_seek(buffer *,unsigned);
+
+#define buffer_PEEK(s) ( (s)->x + (s)->n )
+#define buffer_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) )
+
+#define buffer_GETC(s,c) \
+ ( ((s)->p > 0) \
+ ? ( *(c) = (s)->x[(s)->n], buffer_SEEK((s),1), 1 ) \
+ : buffer_get((s),(c),1) \
+ )
+
+extern int buffer_copy(buffer *,buffer *);
+
+extern int buffer_unixread(int,char *,unsigned);
+/* Actually, int buffer_unixwrite(int,const char *,unsigned),
+ but that 'const' will produce warnings... oh well */
+extern int buffer_unixwrite(int,char *,unsigned);
+
+
+/*** byte.h ***/
+
+extern unsigned byte_chr(char *s,unsigned n,int c);
+
+
+/*** coe.h ***/
+
+extern int coe(int);
+
+
+/*** direntry.h ***/
+
+#define direntry struct dirent
+
+
+/*** fd.h ***/
+
+extern int fd_copy(int,int);
+extern int fd_move(int,int);
+
+
+/*** fifo.h ***/
+
+extern int fifo_make(const char *,int);
+
+
+/*** fmt.h ***/
+
+#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
+#define FMT_LEN ((char *) 0) /* convenient abbreviation */
+
+extern unsigned fmt_uint(char *,unsigned);
+extern unsigned fmt_uint0(char *,unsigned,unsigned);
+extern unsigned fmt_xint(char *,unsigned);
+extern unsigned fmt_nbbint(char *,unsigned,unsigned,unsigned,unsigned);
+extern unsigned fmt_ushort(char *,unsigned short);
+extern unsigned fmt_xshort(char *,unsigned short);
+extern unsigned fmt_nbbshort(char *,unsigned,unsigned,unsigned,unsigned short);
+extern unsigned fmt_ulong(char *,unsigned long);
+extern unsigned fmt_xlong(char *,unsigned long);
+extern unsigned fmt_nbblong(char *,unsigned,unsigned,unsigned,unsigned long);
+
+extern unsigned fmt_plusminus(char *,int);
+extern unsigned fmt_minus(char *,int);
+extern unsigned fmt_0x(char *,int);
+
+extern unsigned fmt_str(char *,const char *);
+extern unsigned fmt_strn(char *,const char *,unsigned);
+
+
+/*** tai.h ***/
+
+struct tai {
+ uint64_t x;
+} ;
+
+#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64_t) (u)))
+
+extern void tai_now(struct tai *);
+
+#define tai_approx(t) ((double) ((t)->x))
+
+extern void tai_add(struct tai *,const struct tai *,const struct tai *);
+extern void tai_sub(struct tai *,const struct tai *,const struct tai *);
+#define tai_less(t,u) ((t)->x < (u)->x)
+
+#define TAI_PACK 8
+extern void tai_pack(char *,const struct tai *);
+extern void tai_unpack(const char *,struct tai *);
+
+extern void tai_uint(struct tai *,unsigned);
+
+
+/*** taia.h ***/
+
+struct taia {
+ struct tai sec;
+ unsigned long nano; /* 0...999999999 */
+ unsigned long atto; /* 0...999999999 */
+} ;
+
+extern void taia_tai(const struct taia *,struct tai *);
+
+extern void taia_now(struct taia *);
+
+extern double taia_approx(const struct taia *);
+extern double taia_frac(const struct taia *);
+
+extern void taia_add(struct taia *,const struct taia *,const struct taia *);
+extern void taia_addsec(struct taia *,const struct taia *,int);
+extern void taia_sub(struct taia *,const struct taia *,const struct taia *);
+extern void taia_half(struct taia *,const struct taia *);
+extern int taia_less(const struct taia *,const struct taia *);
+
+#define TAIA_PACK 16
+extern void taia_pack(char *,const struct taia *);
+extern void taia_unpack(const char *,struct taia *);
+
+#define TAIA_FMTFRAC 19
+extern unsigned taia_fmtfrac(char *,const struct taia *);
+
+extern void taia_uint(struct taia *,unsigned);
+
+
+/*** fmt_ptime.h ***/
+
+#define FMT_PTIME 30
+
+extern unsigned fmt_ptime(char *, struct taia *);
+extern unsigned fmt_taia(char *, struct taia *);
+
+
+/*** gen_alloc.h ***/
+
+#define GEN_ALLOC_typedef(ta,type,field,len,a) \
+ typedef struct ta { type *field; unsigned len; unsigned a; } ta;
+
+
+/*** gen_allocdefs.h ***/
+
+#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \
+int ta_ready(ta *x,unsigned n) \
+{ unsigned i; \
+ if (x->field) { \
+ i = x->a; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ x->field = realloc(x->field,x->a * sizeof(type)); \
+ if (x->field) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = malloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \
+int ta_rplus(ta *x,unsigned n) \
+{ unsigned i; \
+ if (x->field) { \
+ i = x->a; n += x->len; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ x->field = realloc(x->field,x->a * sizeof(type)); \
+ if (x->field) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = malloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \
+int ta_append(ta *x,const type *i) \
+{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; }
+
+
+/*** stralloc.h ***/
+#if 0
+GEN_ALLOC_typedef(stralloc,char,s,len,a)
+
+extern int stralloc_ready(stralloc *,unsigned);
+extern int stralloc_readyplus(stralloc *,unsigned);
+extern int stralloc_copy(stralloc *,const stralloc *);
+extern int stralloc_cat(stralloc *,const stralloc *);
+extern int stralloc_copys(stralloc *,const char *);
+extern int stralloc_cats(stralloc *,const char *);
+extern int stralloc_copyb(stralloc *,const char *,unsigned);
+extern int stralloc_catb(stralloc *,const char *,unsigned);
+extern int stralloc_append(stralloc *,const char *); /* beware: this takes a pointer to 1 char */
+extern int stralloc_starts(stralloc *,const char *);
+
+#define stralloc_0(sa) stralloc_append(sa,"")
+
+extern int stralloc_catulong0(stralloc *,unsigned long,unsigned);
+extern int stralloc_catlong0(stralloc *,long,unsigned);
+
+#define stralloc_catlong(sa,l) (stralloc_catlong0((sa),(l),0))
+#define stralloc_catuint0(sa,i,n) (stralloc_catulong0((sa),(i),(n)))
+#define stralloc_catint0(sa,i,n) (stralloc_catlong0((sa),(i),(n)))
+#define stralloc_catint(sa,i) (stralloc_catlong0((sa),(i),0))
+#endif
+
+/*** iopause.h ***/
+
+typedef struct pollfd iopause_fd;
+#define IOPAUSE_READ POLLIN
+#define IOPAUSE_WRITE POLLOUT
+
+extern void iopause(iopause_fd *,unsigned,struct taia *,struct taia *);
+
+
+/*** lock.h ***/
+
+extern int lock_ex(int);
+extern int lock_un(int);
+extern int lock_exnb(int);
+
+
+/*** ndelay.h ***/
+
+extern int ndelay_on(int);
+extern int ndelay_off(int);
+
+
+/*** open.h ***/
+
+extern int open_read(const char *);
+extern int open_excl(const char *);
+extern int open_append(const char *);
+extern int open_trunc(const char *);
+extern int open_write(const char *);
+
+
+/*** openreadclose.h ***/
+#if 0
+extern int openreadclose(const char *,stralloc *,unsigned);
+#endif
+
+/*** pathexec.h ***/
+
+extern void pathexec_run(const char *,char *const *,char *const *);
+extern int pathexec_env(const char *,const char *);
+extern void pathexec(char **);
+
+
+/*** pmatch.h ***/
+
+extern unsigned pmatch(const char *, const char *, unsigned);
+
+
+/*** prot.h ***/
+
+extern int prot_gid(int);
+extern int prot_uid(int);
+
+
+/*** readclose.h ***/
+#if 0
+extern int readclose_append(int,stralloc *,unsigned);
+extern int readclose(int,stralloc *,unsigned);
+#endif
+
+/*** scan.h ***/
+
+extern unsigned scan_uint(const char *,unsigned *);
+extern unsigned scan_xint(const char *,unsigned *);
+extern unsigned scan_nbbint(const char *,unsigned,unsigned,unsigned,unsigned *);
+extern unsigned scan_ushort(const char *,unsigned short *);
+extern unsigned scan_xshort(const char *,unsigned short *);
+extern unsigned scan_nbbshort(const char *,unsigned,unsigned,unsigned,unsigned short *);
+extern unsigned scan_ulong(const char *,unsigned long *);
+extern unsigned scan_xlong(const char *,unsigned long *);
+extern unsigned scan_nbblong(const char *,unsigned,unsigned,unsigned,unsigned long *);
+
+extern unsigned scan_plusminus(const char *,int *);
+extern unsigned scan_0x(const char *,unsigned *);
+
+extern unsigned scan_whitenskip(const char *,unsigned);
+extern unsigned scan_nonwhitenskip(const char *,unsigned);
+extern unsigned scan_charsetnskip(const char *,const char *,unsigned);
+extern unsigned scan_noncharsetnskip(const char *,const char *,unsigned);
+
+extern unsigned scan_strncmp(const char *,const char *,unsigned);
+extern unsigned scan_memcmp(const char *,const char *,unsigned);
+
+extern unsigned scan_long(const char *,long *);
+extern unsigned scan_8long(const char *,unsigned long *);
+
+
+/*** seek.h ***/
+
+typedef unsigned long seek_pos;
+
+extern seek_pos seek_cur(int);
+
+extern int seek_set(int,seek_pos);
+extern int seek_end(int);
+
+extern int seek_trunc(int,seek_pos);
+
+#define seek_begin(fd) (seek_set((fd),(seek_pos) 0))
+
+
+/*** sig.h ***/
+
+extern int sig_alarm;
+extern int sig_child;
+extern int sig_cont;
+extern int sig_hangup;
+extern int sig_int;
+extern int sig_pipe;
+extern int sig_term;
+
+extern void (*sig_defaulthandler)(int);
+extern void (*sig_ignorehandler)(int);
+
+extern void sig_catch(int,void (*)(int));
+#define sig_ignore(s) (sig_catch((s),sig_ignorehandler))
+#define sig_uncatch(s) (sig_catch((s),sig_defaulthandler))
+
+extern void sig_block(int);
+extern void sig_unblock(int);
+extern void sig_blocknone(void);
+extern void sig_pause(void);
+
+extern void sig_dfl(int);
+
+
+/*** str.h ***/
+
+extern unsigned str_chr(const char *,int); /* never returns NULL */
+
+#define str_diff(s,t) strcmp((s),(t))
+#define str_equal(s,t) (!strcmp((s),(t)))
+
+
+/*** wait.h ***/
+
+extern int wait_pid(int *wstat, int pid);
+extern int wait_nohang(int *wstat);
+
+#define wait_crashed(w) ((w) & 127)
+#define wait_exitcode(w) ((w) >> 8)
+#define wait_stopsig(w) ((w) >> 8)
+#define wait_stopped(w) (((w) & 127) == 127)
diff --git a/runit/runsv.c b/runit/runsv.c
new file mode 100644
index 000000000..e1b5459fb
--- /dev/null
+++ b/runit/runsv.c
@@ -0,0 +1,613 @@
+/* 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"
+
+static 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 islog;
+};
+static struct svdir svd[2];
+
+static int sigterm = 0;
+static int haslog = 0;
+static int pidchanged = 1;
+static int logpipe[2];
+static char *dir;
+
+#define usage() bb_show_usage()
+
+static void fatal2_cannot(char *m1, char *m2)
+{
+ bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
+ /* was exiting 111 */
+}
+static void fatal_cannot(char *m)
+{
+ fatal2_cannot(m, "");
+ /* was exiting 111 */
+}
+static void fatal2x_cannot(char *m1, char *m2)
+{
+ bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
+ /* was exiting 111 */
+}
+static void warn_cannot(char *m)
+{
+ bb_perror_msg("%s: warning: cannot %s", dir, m);
+}
+static void warnx_cannot(char *m)
+{
+ bb_error_msg("%s: warning: cannot %s", dir, m);
+}
+
+static void stopservice(struct svdir *);
+
+static void s_child(int sig_no)
+{
+ write(selfpipe[1], "", 1);
+}
+
+static void s_term(int sig_no)
+{
+ sigterm = 1;
+ write(selfpipe[1], "", 1); /* XXX */
+}
+
+static char *add_str(char *p, const char *to_add)
+{
+ while ((*p = *to_add) != '\0') {
+ p++;
+ to_add++;
+ }
+ return p;
+}
+
+static int open_trunc_or_warn(const char *name)
+{
+ int fd = open_trunc(name);
+ if (fd < 0)
+ bb_perror_msg("%s: warning: cannot open %s",
+ dir, name);
+ return fd;
+}
+
+static int rename_or_warn(const char *old, const char *new)
+{
+ if (rename(old, new) == -1) {
+ bb_perror_msg("%s: warning: cannot rename %s to %s",
+ dir, old, new);
+ return -1;
+ }
+ return 0;
+}
+
+static void update_status(struct svdir *s)
+{
+ unsigned long l;
+ int fd;
+ char status[20];
+
+ /* pid */
+ if (pidchanged) {
+ fd = open_trunc_or_warn("supervise/pid.new");
+ if (fd < 0)
+ return;
+ if (s->pid) {
+ char spid[sizeof(s->pid)*3 + 2];
+ int size = sprintf(spid, "%d\n", s->pid);
+ write(fd, spid, size);
+ }
+ close(fd);
+ if (s->islog) {
+ if (rename_or_warn("supervise/pid.new", "log/supervise/pid"))
+ return;
+ } else if (rename_or_warn("supervise/pid.new", "supervise/pid")) {
+ return;
+ }
+ pidchanged = 0;
+ }
+
+ /* stat */
+ fd = open_trunc_or_warn("supervise/stat.new");
+ if (fd < -1)
+ return;
+
+ {
+ char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
+ char *p = stat_buf;
+ switch (s->state) {
+ case S_DOWN:
+ p = add_str(p, "down");
+ break;
+ case S_RUN:
+ p = add_str(p, "run");
+ break;
+ case S_FINISH:
+ p = add_str(p, "finish");
+ break;
+ }
+ if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
+ if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
+ if (s->state != S_DOWN)
+ switch(s->want) {
+ case W_DOWN:
+ p = add_str(p, ", want down");
+ break;
+ case W_EXIT:
+ p = add_str(p, ", want exit");
+ break;
+ }
+ *p++ = '\n';
+ write(fd, stat_buf, p - stat_buf);
+ close(fd);
+ }
+
+ if (s->islog) {
+ rename_or_warn("supervise/stat.new", "log/supervise/stat");
+ } else {
+ rename_or_warn("supervise/stat.new", "log/supervise/stat"+4);
+ }
+
+ /* 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;
+ status[19] = s->state;
+ fd = open_trunc_or_warn("supervise/status.new");
+ if (fd < 0)
+ return;
+ l = write(fd, status, sizeof status);
+ if (l < 0) {
+ warn_cannot("write supervise/status.new");
+ close(fd);
+ unlink("supervise/status.new");
+ return;
+ }
+ close(fd);
+ if (l < sizeof status) {
+ warnx_cannot("write supervise/status.new: partial write");
+ return;
+ }
+ if (s->islog) {
+ rename_or_warn("supervise/status.new", "log/supervise/status");
+ } else {
+ rename_or_warn("supervise/status.new", "log/supervise/status"+4);
+ }
+}
+
+static unsigned custom(struct svdir *s, char c)
+{
+ int pid;
+ int w;
+ char a[10];
+ struct stat st;
+ char *prog[2];
+
+ if (s->islog) return 0;
+ memcpy(a, "control/?", 10);
+ a[8] = c;
+ if (stat(a, &st) == 0) {
+ if (st.st_mode & S_IXUSR) {
+ pid = fork();
+ if (pid == -1) {
+ warn_cannot("fork for control/?");
+ return 0;
+ }
+ if (!pid) {
+ if (haslog && fd_copy(1, logpipe[1]) == -1)
+ warn_cannot("setup stdout for control/?");
+ prog[0] = a;
+ prog[1] = 0;
+ execve(a, prog, environ);
+ fatal_cannot("run control/?");
+ }
+ while (wait_pid(&w, pid) == -1) {
+ if (errno == EINTR) continue;
+ warn_cannot("wait for child control/?");
+ return 0;
+ }
+ return !wait_exitcode(w);
+ }
+ }
+ else {
+ if (errno == ENOENT) return 0;
+ warn_cannot("stat control/?");
+ }
+ return 0;
+}
+
+static void stopservice(struct svdir *s)
+{
+ if (s->pid && ! custom(s, 't')) {
+ kill(s->pid, SIGTERM);
+ s->ctrl |=C_TERM;
+ update_status(s);
+ }
+ if (s->want == W_DOWN) {
+ kill(s->pid, SIGCONT);
+ custom(s, 'd'); return;
+ }
+ if (s->want == W_EXIT) {
+ kill(s->pid, SIGCONT);
+ custom(s, 'x');
+ }
+}
+
+static void startservice(struct svdir *s)
+{
+ int p;
+ char *run[2];
+
+ if (s->state == S_FINISH)
+ run[0] = "./finish";
+ else {
+ run[0] = "./run";
+ custom(s, 'u');
+ }
+ run[1] = 0;
+
+ if (s->pid != 0) stopservice(s); /* should never happen */
+ while ((p = fork()) == -1) {
+ warn_cannot("fork, sleeping");
+ sleep(5);
+ }
+ if (p == 0) {
+ /* child */
+ if (haslog) {
+ if (s->islog) {
+ if (fd_copy(0, logpipe[0]) == -1)
+ fatal_cannot("setup filedescriptor for ./log/run");
+ close(logpipe[1]);
+ if (chdir("./log") == -1)
+ fatal_cannot("change directory to ./log");
+ } else {
+ if (fd_copy(1, logpipe[1]) == -1)
+ fatal_cannot("setup filedescriptor for ./run");
+ close(logpipe[0]);
+ }
+ }
+ sig_uncatch(sig_child);
+ sig_unblock(sig_child);
+ sig_uncatch(sig_term);
+ sig_unblock(sig_term);
+ execve(*run, run, environ);
+ if (s->islog)
+ fatal2_cannot("start log/", *run);
+ else
+ fatal2_cannot("start ", *run);
+ }
+ if (s->state != S_FINISH) {
+ taia_now(&s->start);
+ s->state = S_RUN;
+ }
+ s->pid = p;
+ pidchanged = 1;
+ s->ctrl = C_NOOP;
+ update_status(s);
+}
+
+static int ctrl(struct svdir *s, char c)
+{
+ switch(c) {
+ case 'd': /* down */
+ s->want = W_DOWN;
+ update_status(s);
+ if (s->pid && s->state != S_FINISH) stopservice(s);
+ break;
+ case 'u': /* up */
+ s->want = W_UP;
+ update_status(s);
+ if (s->pid == 0) startservice(s);
+ break;
+ case 'x': /* exit */
+ if (s->islog) break;
+ s->want = W_EXIT;
+ update_status(s);
+ 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 && ! custom(s, c)) kill(s->pid, SIGKILL);
+ s->state = S_DOWN;
+ break;
+ case 'p': /* sig pause */
+ if (s->pid && ! custom(s, c)) kill(s->pid, SIGSTOP);
+ s->ctrl |=C_PAUSE;
+ update_status(s);
+ break;
+ case 'c': /* sig cont */
+ if (s->pid && ! custom(s, c)) kill(s->pid, SIGCONT);
+ if (s->ctrl & C_PAUSE) s->ctrl &=~C_PAUSE;
+ update_status(s);
+ break;
+ case 'o': /* once */
+ s->want = W_DOWN;
+ update_status(s);
+ if (!s->pid) startservice(s);
+ break;
+ case 'a': /* sig alarm */
+ if (s->pid && ! custom(s, c)) kill(s->pid, SIGALRM);
+ break;
+ case 'h': /* sig hup */
+ if (s->pid && ! custom(s, c)) kill(s->pid, SIGHUP);
+ break;
+ case 'i': /* sig int */
+ if (s->pid && ! custom(s, c)) kill(s->pid, SIGINT);
+ break;
+ case 'q': /* sig quit */
+ if (s->pid && ! custom(s, c)) kill(s->pid, SIGQUIT);
+ break;
+ case '1': /* sig usr1 */
+ if (s->pid && ! custom(s, c)) kill(s->pid, SIGUSR1);
+ break;
+ case '2': /* sig usr2 */
+ if (s->pid && ! custom(s, c)) kill(s->pid, SIGUSR2);
+ break;
+ }
+ return 1;
+}
+
+int runsv_main(int argc, char **argv)
+{
+ struct stat s;
+ int fd;
+ int r;
+ char buf[256];
+
+ if (!argv[1] || argv[2]) usage();
+ dir = argv[1];
+
+ if (pipe(selfpipe) == -1) fatal_cannot("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);
+
+ xchdir(dir);
+ svd[0].pid = 0;
+ svd[0].state = S_DOWN;
+ svd[0].ctrl = C_NOOP;
+ svd[0].want = W_UP;
+ svd[0].islog = 0;
+ svd[1].pid = 0;
+ taia_now(&svd[0].start);
+ if (stat("down", &s) != -1) svd[0].want = W_DOWN;
+
+ if (stat("log", &s) == -1) {
+ if (errno != ENOENT)
+ warn_cannot("stat ./log");
+ } else {
+ if (!S_ISDIR(s.st_mode))
+ warnx_cannot("stat log/down: log is not a directory");
+ else {
+ haslog = 1;
+ 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 (pipe(logpipe) == -1)
+ fatal_cannot("create log pipe");
+ coe(logpipe[0]);
+ coe(logpipe[1]);
+ }
+ }
+
+ if (mkdir("supervise", 0700) == -1) {
+ r = readlink("supervise", buf, 256);
+ if (r != -1) {
+ if (r == 256)
+ fatal2x_cannot("readlink ./supervise: ", "name too long");
+ buf[r] = 0;
+ mkdir(buf, 0700);
+ } else {
+ if ((errno != ENOENT) && (errno != EINVAL))
+ fatal_cannot("readlink ./supervise");
+ }
+ }
+ svd[0].fdlock = xopen3("log/supervise/lock"+4,
+ O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+ if (lock_exnb(svd[0].fdlock) == -1)
+ fatal_cannot("lock supervise/lock");
+ coe(svd[0].fdlock);
+ if (haslog) {
+ if (mkdir("log/supervise", 0700) == -1) {
+ r = readlink("log/supervise", buf, 256);
+ if (r != -1) {
+ if (r == 256)
+ fatal2x_cannot("readlink ./log/supervise: ", "name too long");
+ buf[r] = 0;
+ fd = xopen(".", O_RDONLY|O_NDELAY);
+ xchdir("./log");
+ mkdir(buf, 0700);
+ if (fchdir(fd) == -1)
+ fatal_cannot("change back to service directory");
+ close(fd);
+ }
+ else {
+ if ((errno != ENOENT) && (errno != EINVAL))
+ fatal_cannot("readlink ./log/supervise");
+ }
+ }
+ svd[1].fdlock = xopen3("log/supervise/lock",
+ O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+ if (lock_ex(svd[1].fdlock) == -1)
+ fatal_cannot("lock log/supervise/lock");
+ coe(svd[1].fdlock);
+ }
+
+ fifo_make("log/supervise/control"+4, 0600);
+ svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
+ coe(svd[0].fdcontrol);
+ svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
+ coe(svd[0].fdcontrolwrite);
+ update_status(&svd[0]);
+ if (haslog) {
+ fifo_make("log/supervise/control", 0600);
+ svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
+ coe(svd[1].fdcontrol);
+ svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
+ coe(svd[1].fdcontrolwrite);
+ update_status(&svd[1]);
+ }
+ fifo_make("log/supervise/ok"+4, 0600);
+ fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
+ coe(fd);
+ if (haslog) {
+ fifo_make("log/supervise/ok", 0600);
+ fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
+ 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)
+ if (svd[0].want == W_UP || svd[0].state == S_FINISH)
+ 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 != EINTR)) break;
+ if (child == svd[0].pid) {
+ svd[0].pid = 0;
+ pidchanged = 1;
+ svd[0].ctrl &=~C_TERM;
+ if (svd[0].state != S_FINISH)
+ fd = open_read("finish");
+ if (fd != -1) {
+ close(fd);
+ svd[0].state = S_FINISH;
+ update_status(&svd[0]);
+ continue;
+ }
+ svd[0].state = S_DOWN;
+ taia_uint(&deadline, 1);
+ taia_add(&deadline, &svd[0].start, &deadline);
+ taia_now(&svd[0].start);
+ update_status(&svd[0]);
+ if (taia_less(&svd[0].start, &deadline)) sleep(1);
+ }
+ if (haslog) {
+ if (child == svd[1].pid) {
+ svd[1].pid = 0;
+ pidchanged = 1;
+ svd[1].state = S_DOWN;
+ svd[1].ctrl &=~C_TERM;
+ taia_uint(&deadline, 1);
+ taia_add(&deadline, &svd[1].start, &deadline);
+ taia_now(&svd[1].start);
+ update_status(&svd[1]);
+ if (taia_less(&svd[1].start, &deadline)) sleep(1);
+ }
+ }
+ }
+ 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 (sigterm) {
+ ctrl(&svd[0], 'x');
+ sigterm = 0;
+ }
+
+ if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
+ if (svd[1].pid == 0)
+ _exit(0);
+ if (svd[1].want != W_EXIT) {
+ svd[1].want = W_EXIT;
+ /* stopservice(&svd[1]); */
+ update_status(&svd[1]);
+ close(logpipe[1]);
+ close(logpipe[0]);
+ //if (close(logpipe[1]) == -1)
+ // warn_cannot("close logpipe[1]");
+ //if (close(logpipe[0]) == -1)
+ // warn_cannot("close logpipe[0]");
+ }
+ }
+ }
+ /* not reached */
+ return 0;
+}
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;
+}
diff --git a/runit/sv.c b/runit/sv.c
new file mode 100644
index 000000000..819f31419
--- /dev/null
+++ b/runit/sv.c
@@ -0,0 +1,360 @@
+/* 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"
+
+static char *action;
+static char *acts;
+static char *varservice = "/var/service/";
+static char **service;
+static char **servicex;
+static unsigned services;
+static unsigned rc = 0;
+static unsigned verbose = 0;
+static unsigned long waitsec = 7;
+static unsigned kll = 0;
+static struct taia tstart, tnow, tdiff;
+static struct tai tstatus;
+
+static int (*act)(char*) = 0;
+static int (*cbk)(char*) = 0;
+
+static int curdir, fd, r;
+static char svstatus[20];
+
+#define usage() bb_show_usage()
+
+static void fatal_cannot(char *m1)
+{
+ bb_perror_msg("fatal: cannot %s", m1);
+ _exit(151);
+}
+
+static void out(char *p, char *m1)
+{
+ printf("%s%s: %s", p, *service, m1);
+ if (errno) {
+ printf(": %s", strerror(errno));
+ }
+ puts(""); /* will also flush the output */
+}
+
+#define FAIL "fail: "
+#define WARN "warning: "
+#define OK "ok: "
+#define RUN "run: "
+#define FINISH "finish: "
+#define DOWN "down: "
+#define TIMEOUT "timeout: "
+#define KILL "kill: "
+
+static void fail(char *m1) { ++rc; out(FAIL, m1); }
+static void failx(char *m1) { errno = 0; fail(m1); }
+static void warn_cannot(char *m1) { ++rc; out("warning: cannot ", m1); }
+static void warnx_cannot(char *m1) { errno = 0; warn_cannot(m1); }
+static void ok(char *m1) { errno = 0; out(OK, m1); }
+
+static int svstatus_get(void)
+{
+ if ((fd = open_write("supervise/ok")) == -1) {
+ if (errno == ENODEV) {
+ *acts == 'x' ? ok("runsv not running")
+ : failx("runsv not running");
+ return 0;
+ }
+ warn_cannot("open supervise/ok");
+ return -1;
+ }
+ close(fd);
+ if ((fd = open_read("supervise/status")) == -1) {
+ warn_cannot("open supervise/status");
+ return -1;
+ }
+ r = read(fd, svstatus, 20);
+ close(fd);
+ switch(r) {
+ case 20: break;
+ case -1: warn_cannot("read supervise/status"); return -1;
+ default: warnx_cannot("read supervise/status: bad format"); return -1;
+ }
+ return 1;
+}
+
+static unsigned svstatus_print(char *m)
+{
+ int pid;
+ int normallyup = 0;
+ struct stat s;
+
+ if (stat("down", &s) == -1) {
+ if (errno != ENOENT) {
+ bb_perror_msg(WARN"cannot stat %s/down", *service);
+ return 0;
+ }
+ normallyup = 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: printf(RUN); break;
+ case 2: printf(FINISH); break;
+ }
+ printf("%s: (pid %d) ", m, pid);
+ }
+ else {
+ printf(DOWN"%s: ", m);
+ }
+ printf("%lus", (unsigned long)(tnow.sec.x < tstatus.x ? 0 : tnow.sec.x-tstatus.x));
+ if (pid && !normallyup) printf(", normally down");
+ if (!pid && normallyup) printf(", normally up");
+ if (pid && svstatus[16]) printf(", paused");
+ if (!pid && (svstatus[17] == 'u')) printf(", want up");
+ if (pid && (svstatus[17] == 'd')) printf(", want down");
+ if (pid && svstatus[18]) printf(", got TERM");
+ return pid ? 1 : 2;
+}
+
+static int status(char *unused)
+{
+ r = svstatus_get();
+ switch(r) { case -1: case 0: return 0; }
+ r = svstatus_print(*service);
+ if (chdir("log") == -1) {
+ if (errno != ENOENT) {
+ printf("; log: "WARN"cannot change to log service directory: %s",
+ strerror(errno));
+ }
+ } else if (svstatus_get()) {
+ printf("; ");
+ svstatus_print("log");
+ }
+ puts(""); /* will also flush the output */
+ return r;
+}
+
+static int checkscript(void)
+{
+ char *prog[2];
+ struct stat s;
+ int pid, w;
+
+ if (stat("check", &s) == -1) {
+ if (errno == ENOENT) return 1;
+ bb_perror_msg(WARN"cannot stat %s/check", *service);
+ return 0;
+ }
+ /* if (!(s.st_mode & S_IXUSR)) return 1; */
+ if ((pid = fork()) == -1) {
+ bb_perror_msg(WARN"cannot fork for %s/check", *service);
+ return 0;
+ }
+ if (!pid) {
+ prog[0] = "./check";
+ prog[1] = 0;
+ close(1);
+ execve("check", prog, environ);
+ bb_perror_msg(WARN"cannot run %s/check", *service);
+ _exit(0);
+ }
+ while (wait_pid(&w, pid) == -1) {
+ if (errno == EINTR) continue;
+ bb_perror_msg(WARN"cannot wait for child %s/check", *service);
+ return 0;
+ }
+ return !wait_exitcode(w);
+}
+
+static int check(char *a)
+{
+ unsigned pid;
+
+ if ((r = svstatus_get()) == -1) return -1;
+ if (r == 0) { if (*a == 'x') return 1; 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 (*a) {
+ case 'x': return 0;
+ case 'u':
+ if (!pid || svstatus[19] != 1) return 0;
+ if (!checkscript()) return 0;
+ break;
+ case 'd': if (pid) return 0; break;
+ case 'c': if (pid) if (!checkscript()) return 0; break;
+ case 't':
+ if (!pid && svstatus[17] == 'd') break;
+ tai_unpack(svstatus, &tstatus);
+ if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript())
+ return 0;
+ break;
+ case 'o':
+ tai_unpack(svstatus, &tstatus);
+ if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd'))
+ return 0;
+ }
+ printf(OK); svstatus_print(*service); puts(""); /* will also flush the output */
+ return 1;
+}
+
+static int control(char *a)
+{
+ if (svstatus_get() <= 0) return -1;
+ if (svstatus[17] == *a) return 0;
+ if ((fd = open_write("supervise/control")) == -1) {
+ if (errno != ENODEV)
+ warn_cannot("open supervise/control");
+ else
+ *a == 'x' ? ok("runsv not running") : failx("runsv not running");
+ return -1;
+ }
+ r = write(fd, a, strlen(a));
+ close(fd);
+ if (r != strlen(a)) {
+ warn_cannot("write to supervise/control");
+ return -1;
+ }
+ return 1;
+}
+
+int sv_main(int argc, char **argv)
+{
+ unsigned opt;
+ unsigned i, want_exit;
+ char *x;
+
+ for (i = strlen(*argv); i; --i)
+ if ((*argv)[i-1] == '/')
+ break;
+ *argv += i;
+ service = argv;
+ services = 1;
+ if ((x = getenv("SVDIR"))) varservice = x;
+ if ((x = getenv("SVWAIT"))) waitsec = xatoul(x);
+ /* TODO: V can be handled internally by getopt_ulflags */
+ opt = getopt32(argc, argv, "w:vV", &x);
+ if (opt & 1) waitsec = xatoul(x);
+ if (opt & 2) verbose = 1;
+ if (opt & 4) usage();
+ if (!(action = *argv++)) usage();
+ --argc;
+ service = argv; services = argc;
+ if (!*service) usage();
+
+ taia_now(&tnow); tstart = tnow;
+ if ((curdir = open_read(".")) == -1)
+ fatal_cannot("open current directory");
+
+ act = &control; acts = "s";
+ if (verbose) cbk = &check;
+ switch (*action) {
+ case 'x': case 'e':
+ acts = "x"; break;
+ case 'X': case 'E':
+ acts = "x"; kll = 1; cbk = &check; break;
+ case 'D':
+ acts = "d"; kll = 1; cbk = &check; break;
+ case 'T':
+ acts = "tc"; kll = 1; cbk = &check; break;
+ case 'c':
+ if (!str_diff(action, "check")) {
+ act = 0;
+ acts = "c";
+ cbk = &check;
+ break;
+ }
+ case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
+ case 'a': case 'i': case 'k': case 'q': case '1': case '2':
+ action[1] = 0; acts = action; break;
+ case 's':
+ if (!str_diff(action, "shutdown")) {
+ acts = "x";
+ cbk = &check;
+ break;
+ }
+ if (!str_diff(action, "start")) {
+ acts = "u";
+ cbk = &check;
+ break;
+ }
+ if (!str_diff(action, "stop")) {
+ acts = "d";
+ cbk = &check;
+ break;
+ }
+ act = &status; cbk = 0; break;
+ case 'r':
+ if (!str_diff(action, "restart")) {
+ acts = "tcu";
+ cbk = &check;
+ break;
+ }
+ usage();
+ case 'f':
+ if (!str_diff(action, "force-reload"))
+ { acts = "tc"; kll = 1; cbk = &check; break; }
+ if (!str_diff(action, "force-restart"))
+ { acts = "tcu"; kll = 1; cbk = &check; break; }
+ if (!str_diff(action, "force-shutdown"))
+ { acts = "x"; kll = 1; cbk = &check; break; }
+ if (!str_diff(action, "force-stop"))
+ { acts = "d"; kll = 1; cbk = &check; break; }
+ default:
+ usage();
+ }
+
+ servicex = service;
+ for (i = 0; i < services; ++i) {
+ if ((**service != '/') && (**service != '.')) {
+ if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {
+ fail("cannot change to service directory");
+ *service = 0;
+ }
+ } else if (chdir(*service) == -1) {
+ fail("cannot change to service directory");
+ *service = 0;
+ }
+ if (*service) if (act && (act(acts) == -1)) *service = 0;
+ if (fchdir(curdir) == -1) fatal_cannot("change to original directory");
+ service++;
+ }
+
+ if (*cbk)
+ for (;;) {
+ taia_sub(&tdiff, &tnow, &tstart);
+ service = servicex; want_exit = 1;
+ for (i = 0; i < services; ++i, ++service) {
+ if (!*service) continue;
+ if ((**service != '/') && (**service != '.')) {
+ if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {
+ fail("cannot change to service directory");
+ *service = 0;
+ }
+ } else if (chdir(*service) == -1) {
+ fail("cannot change to service directory");
+ *service = 0;
+ }
+ if (*service) { if (cbk(acts) != 0) *service = 0; else want_exit = 0; }
+ if (*service && taia_approx(&tdiff) > waitsec) {
+ kll ? printf(KILL) : printf(TIMEOUT);
+ if (svstatus_get() > 0) { svstatus_print(*service); ++rc; }
+ puts(""); /* will also flush the output */
+ if (kll) control("k");
+ *service = 0;
+ }
+ if (fchdir(curdir) == -1)
+ fatal_cannot("change to original directory");
+ }
+ if (want_exit) break;
+ usleep(420000);
+ taia_now(&tnow);
+ }
+ return rc > 99 ? 99 : rc;
+}
diff --git a/runit/svlogd.c b/runit/svlogd.c
new file mode 100644
index 000000000..7024c3db4
--- /dev/null
+++ b/runit/svlogd.c
@@ -0,0 +1,878 @@
+/*
+Copyright (c) 2001-2006, Gerrit Pape
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* 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"
+
+static unsigned verbose;
+static int linemax = 1000;
+static int buflen = 1024;
+static int linelen;
+
+static char **fndir;
+static int fdwdir;
+static int wstat;
+static struct taia trotate;
+
+static char *line;
+static unsigned exitasap;
+static unsigned rotateasap;
+static unsigned reopenasap;
+static unsigned linecomplete = 1;
+static unsigned tmaxflag;
+static iopause_fd in;
+
+static const char *replace = "";
+static char repl;
+
+static struct logdir {
+ char *btmp;
+ /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
+ char *inst;
+ char *processor;
+ char *name;
+ unsigned size;
+ unsigned sizemax;
+ unsigned nmax;
+ unsigned nmin;
+ /* int (not long) because of taia_uint() usage: */
+ unsigned tmax;
+ int ppid;
+ int fddir;
+ int fdcur;
+ int fdlock;
+ struct taia trotate;
+ char fnsave[FMT_PTIME];
+ char match;
+ char matcherr;
+} *dir;
+static unsigned dirn = 0;
+
+#define FATAL "fatal: "
+#define WARNING "warning: "
+#define PAUSE "pausing: "
+#define INFO "info: "
+
+#define usage() bb_show_usage()
+static void fatalx(char *m0)
+{
+ bb_error_msg_and_die(FATAL"%s", m0);
+}
+static void warn(char *m0) {
+ bb_perror_msg(WARNING"%s", m0);
+}
+static void warn2(char *m0, char *m1)
+{
+ bb_perror_msg(WARNING"%s: %s", m0, m1);
+}
+static void warnx(char *m0, char *m1)
+{
+ bb_error_msg(WARNING"%s: %s", m0, m1);
+}
+static void pause_nomem(void)
+{
+ bb_error_msg(PAUSE"out of memory"); sleep(3);
+}
+static void pause1cannot(char *m0)
+{
+ bb_perror_msg(PAUSE"cannot %s", m0); sleep(3);
+}
+static void pause2cannot(char *m0, char *m1)
+{
+ bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
+ sleep(3);
+}
+
+static char* wstrdup(const char *str)
+{
+ char *s;
+ while (!(s = strdup(str))) pause_nomem();
+ return s;
+}
+
+static unsigned processorstart(struct logdir *ld)
+{
+ int pid;
+
+ if (!ld->processor) return 0;
+ if (ld->ppid) {
+ warnx("processor already running", ld->name);
+ return 0;
+ }
+ while ((pid = fork()) == -1)
+ pause2cannot("fork for processor", ld->name);
+ if (!pid) {
+ char *prog[4];
+ int fd;
+
+ /* child */
+ sig_uncatch(sig_term);
+ sig_uncatch(sig_alarm);
+ sig_uncatch(sig_hangup);
+ sig_unblock(sig_term);
+ sig_unblock(sig_alarm);
+ sig_unblock(sig_hangup);
+
+ if (verbose)
+ bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
+ fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
+ if (fd_move(0, fd) == -1)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
+ ld->fnsave[26] = 't';
+ fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
+ if (fd_move(1, fd) == -1)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
+ fd = open_read("state");
+ if (fd == -1) {
+ if (errno != ENOENT)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
+ close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
+ fd = xopen("state", O_RDONLY|O_NDELAY);
+ }
+ if (fd_move(4, fd) == -1)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
+ fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
+ if (fd_move(5, fd) == -1)
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name);
+
+ prog[0] = "sh";
+ prog[1] = "-c";
+ prog[2] = ld->processor;
+ prog[3] = '\0';
+ execve("/bin/sh", prog, environ);
+ bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
+ }
+ ld->ppid = pid;
+ return 1;
+}
+
+static unsigned processorstop(struct logdir *ld)
+{
+ char f[28];
+
+ if (ld->ppid) {
+ sig_unblock(sig_hangup);
+ while (wait_pid(&wstat, ld->ppid) == -1)
+ pause2cannot("wait for processor", ld->name);
+ sig_block(sig_hangup);
+ ld->ppid = 0;
+ }
+ if (ld->fddir == -1) return 1;
+ while (fchdir(ld->fddir) == -1)
+ pause2cannot("change directory, want processor", ld->name);
+ if (wait_exitcode(wstat) != 0) {
+ warnx("processor failed, restart", ld->name);
+ ld->fnsave[26] = 't';
+ unlink(ld->fnsave);
+ ld->fnsave[26] = 'u';
+ processorstart(ld);
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return ld->processor ? 0 : 1;
+ }
+ ld->fnsave[26] = 't';
+ memcpy(f, ld->fnsave, 26);
+ f[26] = 's';
+ f[27] = '\0';
+ while (rename(ld->fnsave, f) == -1)
+ pause2cannot("rename processed", ld->name);
+ while (chmod(f, 0744) == -1)
+ pause2cannot("set mode of processed", ld->name);
+ ld->fnsave[26] = 'u';
+ if (unlink(ld->fnsave) == -1)
+ bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
+ while (rename("newstate", "state") == -1)
+ pause2cannot("rename state", ld->name);
+ if (verbose) bb_error_msg(INFO"processed: %s/%s", ld->name, f);
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 1;
+}
+
+static void rmoldest(struct logdir *ld)
+{
+ DIR *d;
+ struct dirent *f;
+ char oldest[FMT_PTIME];
+ int n = 0;
+
+ oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
+ while (!(d = opendir(".")))
+ pause2cannot("open directory, want rotate", ld->name);
+ errno = 0;
+ while ((f = readdir(d))) {
+ if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
+ if (f->d_name[26] == 't') {
+ if (unlink(f->d_name) == -1)
+ warn2("cannot unlink processor leftover", f->d_name);
+ } else {
+ ++n;
+ if (strcmp(f->d_name, oldest) < 0)
+ memcpy(oldest, f->d_name, 27);
+ }
+ errno = 0;
+ }
+ }
+ if (errno) warn2("cannot read directory", ld->name);
+ closedir(d);
+
+ if (ld->nmax && (n > ld->nmax)) {
+ if (verbose) bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
+ if ((*oldest == '@') && (unlink(oldest) == -1))
+ warn2("cannot unlink oldest logfile", ld->name);
+ }
+}
+
+static unsigned rotate(struct logdir *ld)
+{
+ struct stat st;
+ struct taia now;
+
+ if (ld->fddir == -1) {
+ ld->tmax = 0;
+ return 0;
+ }
+ if (ld->ppid)
+ while(!processorstop(ld))
+ /* wait */;
+
+ while (fchdir(ld->fddir) == -1)
+ pause2cannot("change directory, want rotate", ld->name);
+
+ /* create new filename */
+ ld->fnsave[25] = '.';
+ ld->fnsave[26] = 's';
+ if (ld->processor)
+ ld->fnsave[26] = 'u';
+ ld->fnsave[27] = '\0';
+ do {
+ taia_now(&now);
+ fmt_taia(ld->fnsave, &now);
+ errno = 0;
+ } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT));
+
+ if (ld->tmax && taia_less(&ld->trotate, &now)) {
+ taia_uint(&ld->trotate, ld->tmax);
+ taia_add(&ld->trotate, &now, &ld->trotate);
+ if (taia_less(&ld->trotate, &trotate))
+ trotate = ld->trotate;
+ }
+
+ if (ld->size > 0) {
+ while (fsync(ld->fdcur) == -1)
+ pause2cannot("fsync current logfile", ld->name);
+ while (fchmod(ld->fdcur, 0744) == -1)
+ pause2cannot("set mode of current", ld->name);
+ close(ld->fdcur);
+ if (verbose) {
+ bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
+ ld->fnsave, ld->size);
+ }
+ while (rename("current", ld->fnsave) == -1)
+ pause2cannot("rename current", ld->name);
+ while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
+ pause2cannot("create new current", ld->name);
+ coe(ld->fdcur);
+ ld->size = 0;
+ while (fchmod(ld->fdcur, 0644) == -1)
+ pause2cannot("set mode of current", ld->name);
+ rmoldest(ld);
+ processorstart(ld);
+ }
+
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 1;
+}
+
+static int buffer_pwrite(int n, char *s, unsigned len)
+{
+ int i;
+ struct logdir *ld = &dir[n];
+
+ if (ld->sizemax) {
+ if (ld->size >= ld->sizemax)
+ rotate(ld);
+ if (len > (ld->sizemax - ld->size))
+ len = ld->sizemax - ld->size;
+ }
+ while ((i = write(ld->fdcur, s, len)) == -1) {
+ if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
+ DIR *d;
+ struct dirent *f;
+ char oldest[FMT_PTIME];
+ int j = 0;
+
+ while (fchdir(ld->fddir) == -1)
+ pause2cannot("change directory, want remove old logfile",
+ ld->name);
+ oldest[0] = 'A';
+ oldest[1] = oldest[27] = '\0';
+ while (!(d = opendir(".")))
+ pause2cannot("open directory, want remove old logfile",
+ ld->name);
+ errno = 0;
+ while ((f = readdir(d)))
+ if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
+ ++j;
+ if (strcmp(f->d_name, oldest) < 0)
+ memcpy(oldest, f->d_name, 27);
+ }
+ if (errno) warn2("cannot read directory, want remove old logfile",
+ ld->name);
+ closedir(d);
+ errno = ENOSPC;
+ if (j > ld->nmin) {
+ if (*oldest == '@') {
+ bb_error_msg(WARNING"out of disk space, delete: %s/%s",
+ ld->name, oldest);
+ errno = 0;
+ if (unlink(oldest) == -1) {
+ warn2("cannot unlink oldest logfile", ld->name);
+ errno = ENOSPC;
+ }
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ }
+ }
+ }
+ if (errno) pause2cannot("write to current", ld->name);
+ }
+
+ ld->size += i;
+ if (ld->sizemax)
+ if (s[i-1] == '\n')
+ if (ld->size >= (ld->sizemax - linemax))
+ rotate(ld);
+ return i;
+}
+
+static void logdir_close(struct logdir *ld)
+{
+ if (ld->fddir == -1)
+ return;
+ if (verbose)
+ bb_error_msg(INFO"close: %s", ld->name);
+ close(ld->fddir);
+ ld->fddir = -1;
+ if (ld->fdcur == -1)
+ return; /* impossible */
+ while (fsync(ld->fdcur) == -1)
+ pause2cannot("fsync current logfile", ld->name);
+ while (fchmod(ld->fdcur, 0744) == -1)
+ pause2cannot("set mode of current", ld->name);
+ close(ld->fdcur);
+ ld->fdcur = -1;
+ if (ld->fdlock == -1)
+ return; /* impossible */
+ close(ld->fdlock);
+ ld->fdlock = -1;
+ free(ld->processor);
+ ld->processor = NULL;
+}
+
+static unsigned logdir_open(struct logdir *ld, const char *fn)
+{
+ char buf[128];
+ struct taia now;
+ char *new, *s, *np;
+ int i;
+ struct stat st;
+
+ ld->fddir = open(fn, O_RDONLY|O_NDELAY);
+ if (ld->fddir == -1) {
+ warn2("cannot open log directory", (char*)fn);
+ return 0;
+ }
+ coe(ld->fddir);
+ if (fchdir(ld->fddir) == -1) {
+ logdir_close(ld);
+ warn2("cannot change directory", (char*)fn);
+ return 0;
+ }
+ ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+ if ((ld->fdlock == -1)
+ || (lock_exnb(ld->fdlock) == -1)
+ ) {
+ logdir_close(ld);
+ warn2("cannot lock directory", (char*)fn);
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 0;
+ }
+ coe(ld->fdlock);
+
+ ld->size = 0;
+ ld->sizemax = 1000000;
+ ld->nmax = ld->nmin = 10;
+ ld->tmax = 0;
+ ld->name = (char*)fn;
+ ld->ppid = 0;
+ ld->match = '+';
+ free(ld->inst); ld->inst = NULL;
+ free(ld->processor); ld->processor = NULL;
+
+ /* read config */
+ i = open_read_close("config", buf, sizeof(buf));
+ if (i < 0)
+ warn2("cannot read config", ld->name);
+ if (i > 0) {
+ if (verbose) bb_error_msg(INFO"read: %s/config", ld->name);
+ s = buf;
+ while (s) {
+ np = strchr(s, '\n');
+ if (np) *np++ = '\0';
+ switch (s[0]) {
+ case '+':
+ case '-':
+ case 'e':
+ case 'E':
+ while (1) {
+ int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
+ if (l >= 0 && new) break;
+ pause_nomem();
+ }
+ free(ld->inst);
+ ld->inst = new;
+ break;
+ case 's': {
+ static const struct suffix_mult km_suffixes[] = {
+ { "k", 1024 },
+ { "m", 1024*1024 },
+ { NULL, 0 }
+ };
+ ld->sizemax = xatou_sfx(&s[1], km_suffixes);
+ break;
+ }
+ case 'n':
+ ld->nmax = xatoi_u(&s[1]);
+ break;
+ case 'N':
+ ld->nmin = xatoi_u(&s[1]);
+ break;
+ case 't': {
+ static const struct suffix_mult mh_suffixes[] = {
+ { "m", 60 },
+ { "h", 60*60 },
+ /*{ "d", 24*60*60 },*/
+ { NULL, 0 }
+ };
+ ld->tmax = xatou_sfx(&s[1], mh_suffixes);
+ if (ld->tmax) {
+ taia_uint(&ld->trotate, ld->tmax);
+ taia_add(&ld->trotate, &now, &ld->trotate);
+ if (!tmaxflag || taia_less(&ld->trotate, &trotate))
+ trotate = ld->trotate;
+ tmaxflag = 1;
+ }
+ break;
+ }
+ case '!':
+ if (s[1]) {
+ free(ld->processor);
+ ld->processor = wstrdup(s);
+ }
+ break;
+ }
+ s = np;
+ }
+ /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
+ s = ld->inst;
+ while (s) {
+ np = strchr(s, '\n');
+ if (np) *np++ = '\0';
+ s = np;
+ }
+ }
+
+ /* open current */
+ i = stat("current", &st);
+ if (i != -1) {
+ if (st.st_size && ! (st.st_mode & S_IXUSR)) {
+ ld->fnsave[25] = '.';
+ ld->fnsave[26] = 'u';
+ ld->fnsave[27] = '\0';
+ do {
+ taia_now(&now);
+ fmt_taia(ld->fnsave, &now);
+ errno = 0;
+ } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT));
+ while (rename("current", ld->fnsave) == -1)
+ pause2cannot("rename current", ld->name);
+ rmoldest(ld);
+ i = -1;
+ } else {
+ /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
+ /* (bug in original svlogd. remove this comment when fixed there) */
+ ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
+ }
+ } else {
+ if (errno != ENOENT) {
+ logdir_close(ld);
+ warn2("cannot stat current", ld->name);
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 0;
+ }
+ }
+ while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
+ pause2cannot("open current", ld->name);
+ coe(ld->fdcur);
+ while (fchmod(ld->fdcur, 0644) == -1)
+ pause2cannot("set mode of current", ld->name);
+
+ if (verbose) {
+ if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
+ else bb_error_msg(INFO"new: %s/current", ld->name);
+ }
+
+ while (fchdir(fdwdir) == -1)
+ pause1cannot("change to initial working directory");
+ return 1;
+}
+
+static void logdirs_reopen(void)
+{
+ struct taia now;
+ int l;
+ int ok = 0;
+
+ tmaxflag = 0;
+ taia_now(&now);
+ for (l = 0; l < dirn; ++l) {
+ logdir_close(&dir[l]);
+ if (logdir_open(&dir[l], fndir[l])) ok = 1;
+ }
+ if (!ok) fatalx("no functional log directories");
+}
+
+/* Used for reading stdin */
+static int buffer_pread(int fd, char *s, unsigned len)
+{
+ struct taia now;
+ int i;
+
+ if (rotateasap) {
+ for (i = 0; i < dirn; ++i)
+ rotate(dir+i);
+ rotateasap = 0;
+ }
+ if (exitasap) {
+ if (linecomplete)
+ return 0;
+ len = 1;
+ }
+ if (reopenasap) {
+ logdirs_reopen();
+ reopenasap = 0;
+ }
+ taia_now(&now);
+ taia_uint(&trotate, 2744);
+ taia_add(&trotate, &now, &trotate);
+ for (i = 0; i < dirn; ++i)
+ if (dir[i].tmax) {
+ if (taia_less(&dir[i].trotate, &now))
+ rotate(dir+i);
+ if (taia_less(&dir[i].trotate, &trotate))
+ trotate = dir[i].trotate;
+ }
+
+ while (1) {
+ /* Comment? */
+ sig_unblock(sig_term);
+ sig_unblock(sig_child);
+ sig_unblock(sig_alarm);
+ sig_unblock(sig_hangup);
+ iopause(&in, 1, &trotate, &now);
+ sig_block(sig_term);
+ sig_block(sig_child);
+ sig_block(sig_alarm);
+ sig_block(sig_hangup);
+ i = safe_read(fd, s, len);
+ if (i >= 0) break;
+ if (errno != EAGAIN) {
+ warn("cannot read standard input");
+ break;
+ }
+ /* else: EAGAIN - normal, repeat silently */
+ }
+
+ if (i > 0) {
+ int cnt;
+ linecomplete = (s[i-1] == '\n');
+ if (!repl) return i;
+
+ cnt = i;
+ while (--cnt >= 0) {
+ char ch = *s;
+ if (ch != '\n') {
+ if (ch < 32 || ch > 126)
+ *s = repl;
+ else {
+ int j;
+ for (j = 0; replace[j]; ++j) {
+ if (ch == replace[j]) {
+ *s = repl;
+ break;
+ }
+ }
+ }
+ }
+ s++;
+ }
+ }
+ return i;
+}
+
+
+static void sig_term_handler(int sig_no)
+{
+ if (verbose)
+ bb_error_msg(INFO"sig%s received", "term");
+ exitasap = 1;
+}
+
+static void sig_child_handler(int sig_no)
+{
+ int pid, l;
+
+ if (verbose)
+ bb_error_msg(INFO"sig%s received", "child");
+ while ((pid = wait_nohang(&wstat)) > 0)
+ for (l = 0; l < dirn; ++l)
+ if (dir[l].ppid == pid) {
+ dir[l].ppid = 0;
+ processorstop(&dir[l]);
+ break;
+ }
+}
+
+static void sig_alarm_handler(int sig_no)
+{
+ if (verbose)
+ bb_error_msg(INFO"sig%s received", "alarm");
+ rotateasap = 1;
+}
+
+static void sig_hangup_handler(int sig_no)
+{
+ if (verbose)
+ bb_error_msg(INFO"sig%s received", "hangup");
+ reopenasap = 1;
+}
+
+static void logmatch(struct logdir *ld)
+{
+ char *s;
+
+ ld->match = '+';
+ ld->matcherr = 'E';
+ s = ld->inst;
+ while (s && s[0]) {
+ switch (s[0]) {
+ case '+':
+ case '-':
+ if (pmatch(s+1, line, linelen))
+ ld->match = s[0];
+ break;
+ case 'e':
+ case 'E':
+ if (pmatch(s+1, line, linelen))
+ ld->matcherr = s[0];
+ break;
+ }
+ s += strlen(s) + 1;
+ }
+}
+
+int svlogd_main(int argc, char **argv)
+{
+ struct taia now;
+ char *r,*l,*b;
+ ssize_t stdin_cnt = 0;
+ int i;
+ unsigned opt;
+ unsigned timestamp = 0;
+
+ opt_complementary = "tt:vv";
+ opt = getopt32(argc, argv, "r:R:l:b:tv",
+ &r, &replace, &l, &b, &timestamp, &verbose);
+ if (opt & 1) { // -r
+ repl = r[0];
+ if (!repl || r[1]) usage();
+ }
+ if (opt & 2) if (!repl) repl = '_'; // -R
+ if (opt & 4) { // -l
+ linemax = xatou_range(l, 0, 1000);
+ if (linemax == 0) linemax = 1000;
+ if (linemax < 256) linemax = 256;
+ }
+ if (opt & 8) { // -b
+ buflen = xatoi_u(b);
+ if (buflen == 0) buflen = 1024;
+ }
+ //if (opt & 0x10) timestamp++; // -t
+ //if (opt & 0x20) verbose++; // -v
+ if (timestamp > 2) timestamp = 2;
+ argv += optind;
+ argc -= optind;
+
+ dirn = argc;
+ if (dirn <= 0) usage();
+ if (buflen <= linemax) usage();
+ fdwdir = xopen(".", O_RDONLY|O_NDELAY);
+ coe(fdwdir);
+ dir = xmalloc(dirn * sizeof(struct logdir));
+ for (i = 0; i < dirn; ++i) {
+ dir[i].fddir = -1;
+ dir[i].fdcur = -1;
+ dir[i].btmp = xmalloc(buflen);
+ dir[i].ppid = 0;
+ }
+ line = xmalloc(linemax + (timestamp ? 26 : 0));
+ fndir = argv;
+ in.fd = 0;
+ in.events = IOPAUSE_READ;
+ ndelay_on(in.fd);
+
+ sig_block(sig_term);
+ sig_block(sig_child);
+ sig_block(sig_alarm);
+ sig_block(sig_hangup);
+ sig_catch(sig_term, sig_term_handler);
+ sig_catch(sig_child, sig_child_handler);
+ sig_catch(sig_alarm, sig_alarm_handler);
+ sig_catch(sig_hangup, sig_hangup_handler);
+
+ logdirs_reopen();
+
+ /* Each iteration processes one line */
+ while (1) {
+ int printlen;
+ char *lineptr = line;
+ char *np;
+ char ch;
+
+ /* Prepare timestamp if needed */
+ if (timestamp) {
+ char stamp[FMT_PTIME];
+ taia_now(&now);
+ switch (timestamp) {
+ case 1:
+ fmt_taia(stamp, &now);
+ break;
+ default: /* case 2: */
+ fmt_ptime(stamp, &now);
+ break;
+ }
+ memcpy(line, stamp, 25);
+ line[25] = ' ';
+ lineptr += 26;
+ }
+
+ /* lineptr[0..linemax-1] - buffer for stdin */
+ /* (possibly has some unprocessed data from prev loop) */
+
+ /* Refill the buffer if needed */
+ np = memchr(lineptr, '\n', stdin_cnt);
+ i = linemax - stdin_cnt; /* avail. bytes at tail */
+ if (i >= 128 && !exitasap && !np) {
+ int sz = buffer_pread(0, lineptr + stdin_cnt, i);
+ if (sz <= 0) /* EOF or error on stdin */
+ exitasap = 1;
+ else {
+ stdin_cnt += sz;
+ np = memchr(lineptr, '\n', stdin_cnt);
+ }
+ }
+ if (stdin_cnt <= 0 && exitasap)
+ break;
+
+ /* Search for '\n' (in fact, np already holds the result) */
+ linelen = stdin_cnt;
+ if (np) linelen = np - lineptr + 1;
+ /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
+ ch = lineptr[linelen-1];
+
+ printlen = linelen + (timestamp ? 26 : 0);
+ /* write out line[0..printlen-1] to each log destination */
+ for (i = 0; i < dirn; ++i) {
+ struct logdir *ld = &dir[i];
+ if (ld->fddir == -1) continue;
+ if (ld->inst)
+ logmatch(ld);
+ if (ld->matcherr == 'e')
+ full_write(2, line, printlen);
+ if (ld->match != '+') continue;
+ buffer_pwrite(i, line, printlen);
+ }
+
+ /* If we didn't see '\n' (long input line), */
+ /* read/write repeatedly until we see it */
+ while (ch != '\n') {
+ /* lineptr is emptied now, safe to use as buffer */
+ stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax);
+ if (stdin_cnt <= 0) { /* EOF or error on stdin */
+ lineptr[0] = ch = '\n';
+ linelen = 1;
+ exitasap = 1;
+ stdin_cnt = 1;
+ } else {
+ linelen = stdin_cnt;
+ np = memchr(lineptr, '\n', stdin_cnt);
+ if (np) linelen = np - lineptr + 1;
+ ch = lineptr[linelen-1];
+ }
+ /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
+ for (i = 0; i < dirn; ++i) {
+ if (dir[i].fddir == -1) continue;
+ if (dir[i].matcherr == 'e')
+ full_write(2, lineptr, linelen);
+ if (dir[i].match != '+') continue;
+ buffer_pwrite(i, lineptr, linelen);
+ }
+ }
+
+ /* Move unprocessed data to the front of line */
+ stdin_cnt -= linelen;
+ if (stdin_cnt > 0) /* TODO: slow if buffer is big */
+ memmove(lineptr, &lineptr[linelen], stdin_cnt);
+ }
+
+ for (i = 0; i < dirn; ++i) {
+ if (dir[i].ppid)
+ while (!processorstop(&dir[i]))
+ /* repeat */;
+ logdir_close(&dir[i]);
+ }
+ _exit(0);
+}