summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/daemontools-extras/deps-exe/s6-envdir1
-rw-r--r--src/daemontools-extras/deps-exe/s6-envuidgid1
-rw-r--r--src/daemontools-extras/deps-exe/s6-fghack1
-rw-r--r--src/daemontools-extras/deps-exe/s6-log2
-rw-r--r--src/daemontools-extras/deps-exe/s6-notifywhenup3
-rw-r--r--src/daemontools-extras/deps-exe/s6-setlock2
-rw-r--r--src/daemontools-extras/deps-exe/s6-setsid1
-rw-r--r--src/daemontools-extras/deps-exe/s6-setuidgid1
-rw-r--r--src/daemontools-extras/deps-exe/s6-softlimit1
-rw-r--r--src/daemontools-extras/deps-exe/s6-tai64n2
-rw-r--r--src/daemontools-extras/deps-exe/s6-tai64nlocal1
-rw-r--r--src/daemontools-extras/deps-exe/ucspilogd1
-rw-r--r--src/daemontools-extras/s6-envdir.c40
-rw-r--r--src/daemontools-extras/s6-envuidgid.c44
-rw-r--r--src/daemontools-extras/s6-fghack.c68
-rw-r--r--src/daemontools-extras/s6-log.c1265
-rw-r--r--src/daemontools-extras/s6-notifywhenup.c86
-rw-r--r--src/daemontools-extras/s6-setlock.c87
-rw-r--r--src/daemontools-extras/s6-setsid.c35
-rw-r--r--src/daemontools-extras/s6-setuidgid.c30
-rw-r--r--src/daemontools-extras/s6-softlimit.c117
-rw-r--r--src/daemontools-extras/s6-tai64n.c35
-rw-r--r--src/daemontools-extras/s6-tai64nlocal.c47
-rw-r--r--src/daemontools-extras/ucspilogd.c117
-rw-r--r--src/include/s6/ftrigr.h94
-rw-r--r--src/include/s6/ftrigw.h11
-rw-r--r--src/include/s6/s6-supervise.h40
-rw-r--r--src/include/s6/s6.h11
-rw-r--r--src/include/s6/s6lock.h75
-rwxr-xr-xsrc/libs6/deps-exe/s6-ftrigrd5
-rwxr-xr-xsrc/libs6/deps-exe/s6lockd3
-rwxr-xr-xsrc/libs6/deps-exe/s6lockd-helper1
-rwxr-xr-xsrc/libs6/deps-lib/s633
-rw-r--r--src/libs6/ftrig1.h23
-rw-r--r--src/libs6/ftrig1_free.c25
-rw-r--r--src/libs6/ftrig1_make.c65
-rw-r--r--src/libs6/ftrigr1_zero.c5
-rw-r--r--src/libs6/ftrigr_check.c40
-rw-r--r--src/libs6/ftrigr_end.c14
-rw-r--r--src/libs6/ftrigr_start.c10
-rw-r--r--src/libs6/ftrigr_startf.c12
-rw-r--r--src/libs6/ftrigr_subscribe.c43
-rw-r--r--src/libs6/ftrigr_unsubscribe.c36
-rw-r--r--src/libs6/ftrigr_update.c43
-rw-r--r--src/libs6/ftrigr_wait_and.c28
-rw-r--r--src/libs6/ftrigr_wait_or.c31
-rw-r--r--src/libs6/ftrigr_zero.c5
-rw-r--r--src/libs6/ftrigw_clean.c39
-rw-r--r--src/libs6/ftrigw_fifodir_make.c26
-rw-r--r--src/libs6/ftrigw_notify.c8
-rw-r--r--src/libs6/ftrigw_notifyb.c67
-rw-r--r--src/libs6/s6-ftrigrd.c266
-rw-r--r--src/libs6/s6_supervise_lock.c10
-rw-r--r--src/libs6/s6_supervise_lock_mode.c54
-rw-r--r--src/libs6/s6_svc_main.c40
-rw-r--r--src/libs6/s6_svc_write.c22
-rw-r--r--src/libs6/s6_svstatus_pack.c13
-rw-r--r--src/libs6/s6_svstatus_read.c17
-rw-r--r--src/libs6/s6_svstatus_unpack.c29
-rw-r--r--src/libs6/s6_svstatus_write.c16
-rw-r--r--src/libs6/s6lock_acquire.c38
-rw-r--r--src/libs6/s6lock_check.c25
-rw-r--r--src/libs6/s6lock_end.c14
-rw-r--r--src/libs6/s6lock_release.c28
-rw-r--r--src/libs6/s6lock_start.c12
-rw-r--r--src/libs6/s6lock_startf.c14
-rw-r--r--src/libs6/s6lock_update.c31
-rw-r--r--src/libs6/s6lock_wait_and.c27
-rw-r--r--src/libs6/s6lock_wait_or.c30
-rw-r--r--src/libs6/s6lock_zero.c5
-rw-r--r--src/libs6/s6lockd-helper.c27
-rw-r--r--src/libs6/s6lockd.c316
-rw-r--r--src/pipe-tools/deps-exe/s6-cleanfifodir2
-rw-r--r--src/pipe-tools/deps-exe/s6-ftrig-listen4
-rw-r--r--src/pipe-tools/deps-exe/s6-ftrig-listen13
-rw-r--r--src/pipe-tools/deps-exe/s6-ftrig-notify2
-rw-r--r--src/pipe-tools/deps-exe/s6-ftrig-wait3
-rw-r--r--src/pipe-tools/deps-exe/s6-mkfifodir2
-rw-r--r--src/pipe-tools/s6-cleanfifodir.c15
-rw-r--r--src/pipe-tools/s6-ftrig-listen.c122
-rw-r--r--src/pipe-tools/s6-ftrig-listen1.c101
-rw-r--r--src/pipe-tools/s6-ftrig-notify.c20
-rw-r--r--src/pipe-tools/s6-ftrig-wait.c48
-rw-r--r--src/pipe-tools/s6-mkfifodir.c39
-rw-r--r--src/supervision/deps-exe/s6-supervise3
-rw-r--r--src/supervision/deps-exe/s6-svc2
-rw-r--r--src/supervision/deps-exe/s6-svok1
-rw-r--r--src/supervision/deps-exe/s6-svscan3
-rw-r--r--src/supervision/deps-exe/s6-svscanctl2
-rw-r--r--src/supervision/deps-exe/s6-svstat3
-rw-r--r--src/supervision/deps-exe/s6-svwait3
-rw-r--r--src/supervision/s6-supervise.c508
-rw-r--r--src/supervision/s6-svc.c12
-rw-r--r--src/supervision/s6-svok.c32
-rw-r--r--src/supervision/s6-svscan.c498
-rw-r--r--src/supervision/s6-svscanctl.c12
-rw-r--r--src/supervision/s6-svstat.c70
-rw-r--r--src/supervision/s6-svwait.c104
98 files changed, 5429 insertions, 0 deletions
diff --git a/src/daemontools-extras/deps-exe/s6-envdir b/src/daemontools-extras/deps-exe/s6-envdir
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-envdir
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/daemontools-extras/deps-exe/s6-envuidgid b/src/daemontools-extras/deps-exe/s6-envuidgid
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-envuidgid
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/daemontools-extras/deps-exe/s6-fghack b/src/daemontools-extras/deps-exe/s6-fghack
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-fghack
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/daemontools-extras/deps-exe/s6-log b/src/daemontools-extras/deps-exe/s6-log
new file mode 100644
index 0000000..1840bc1
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-log
@@ -0,0 +1,2 @@
+-lskarnet
+${TAINNOW_LIB}
diff --git a/src/daemontools-extras/deps-exe/s6-notifywhenup b/src/daemontools-extras/deps-exe/s6-notifywhenup
new file mode 100644
index 0000000..58a34e0
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-notifywhenup
@@ -0,0 +1,3 @@
+-ls6
+-lskarnet
+${TAINNOW_LIB}
diff --git a/src/daemontools-extras/deps-exe/s6-setlock b/src/daemontools-extras/deps-exe/s6-setlock
new file mode 100644
index 0000000..1840bc1
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-setlock
@@ -0,0 +1,2 @@
+-lskarnet
+${TAINNOW_LIB}
diff --git a/src/daemontools-extras/deps-exe/s6-setsid b/src/daemontools-extras/deps-exe/s6-setsid
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-setsid
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/daemontools-extras/deps-exe/s6-setuidgid b/src/daemontools-extras/deps-exe/s6-setuidgid
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-setuidgid
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/daemontools-extras/deps-exe/s6-softlimit b/src/daemontools-extras/deps-exe/s6-softlimit
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-softlimit
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/daemontools-extras/deps-exe/s6-tai64n b/src/daemontools-extras/deps-exe/s6-tai64n
new file mode 100644
index 0000000..a11a5f4
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-tai64n
@@ -0,0 +1,2 @@
+-lskarnet
+${SYSCLOCK_LIB}
diff --git a/src/daemontools-extras/deps-exe/s6-tai64nlocal b/src/daemontools-extras/deps-exe/s6-tai64nlocal
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/s6-tai64nlocal
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/daemontools-extras/deps-exe/ucspilogd b/src/daemontools-extras/deps-exe/ucspilogd
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/daemontools-extras/deps-exe/ucspilogd
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/daemontools-extras/s6-envdir.c b/src/daemontools-extras/s6-envdir.c
new file mode 100644
index 0000000..394253e
--- /dev/null
+++ b/src/daemontools-extras/s6-envdir.c
@@ -0,0 +1,40 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "s6-envdir [ -I | -i ] [ -n ] [ -f ] [ -c nullchar ] dir prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ stralloc modifs = STRALLOC_ZERO ;
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ int insist = 1 ;
+ unsigned int options = 0 ;
+ char nullis = '\n' ;
+ PROG = "s6-envdir" ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "Iinfc:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'I' : insist = 0 ; break ;
+ case 'i' : insist = 1 ; break ;
+ case 'n' : options |= SKALIBS_ENVDIR_NOCHOMP ; break ;
+ case 'f' : options |= SKALIBS_ENVDIR_VERBATIM ; break ;
+ case 'c' : nullis = *l.arg ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (argc < 2) strerr_dieusage(100, USAGE) ;
+ if ((envdir_internal(*argv++, &modifs, options, nullis) < 0) && (insist || (errno != ENOENT)))
+ strerr_diefu1sys(111, "envdir") ;
+ pathexec_r(argv, envp, env_len(envp), modifs.s, modifs.len) ;
+ strerr_dieexec(111, argv[0]) ;
+}
diff --git a/src/daemontools-extras/s6-envuidgid.c b/src/daemontools-extras/s6-envuidgid.c
new file mode 100644
index 0000000..64521b0
--- /dev/null
+++ b/src/daemontools-extras/s6-envuidgid.c
@@ -0,0 +1,44 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <limits.h>
+#include <skalibs/gidstuff.h>
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "s6-envuidgid username prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ PROG = "s6-envuidgid" ;
+ if (argc < 3) strerr_dieusage(100, USAGE) ;
+ {
+ char fmt[UINT64_FMT] ;
+ struct passwd *pw = getpwnam(argv[1]) ;
+ if (!pw) strerr_dief2x(1, "unknown user: ", argv[1]) ;
+ fmt[gid_fmt(fmt, pw->pw_gid)] = 0 ;
+ if (!pathexec_env("GID", fmt))
+ strerr_diefu1sys(111, "update environment") ;
+ fmt[uint64_fmt(fmt, pw->pw_uid)] = 0 ;
+ if (!pathexec_env("UID", fmt))
+ strerr_diefu1sys(111, "update environment") ;
+ }
+
+ {
+ gid_t tab[NGROUPS_MAX] ;
+ int n = prot_readgroups(argv[1], tab, NGROUPS_MAX) ;
+ if (n < 0)
+ strerr_diefu2sys(111, "get supplementary groups for ", argv[1]) ;
+ {
+ char fmt[GID_FMT * n] ;
+ fmt[gid_fmtlist(fmt, tab, n)] = 0 ;
+ if (!pathexec_env("GIDLIST", fmt))
+ strerr_diefu1sys(111, "update environment") ;
+ }
+ }
+ pathexec_fromenv(argv+2, envp, env_len(envp)) ;
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/daemontools-extras/s6-fghack.c b/src/daemontools-extras/s6-fghack.c
new file mode 100644
index 0000000..1415706
--- /dev/null
+++ b/src/daemontools-extras/s6-fghack.c
@@ -0,0 +1,68 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "s6-fghack prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ int p[2] ;
+ int pcoe[2] ;
+ pid_t pid ;
+ char dummy ;
+ PROG = "s6-fghack" ;
+ if (argc < 2) strerr_dieusage(100, USAGE) ;
+ if (pipe(p) < 0) strerr_diefu1sys(111, "create hackpipe") ;
+ if (pipe(pcoe) < 0) strerr_diefu1sys(111, "create coepipe") ;
+
+ switch (pid = fork())
+ {
+ case -1 : strerr_diefu1sys(111, "fork") ;
+ case 0 :
+ {
+ int i = 0 ;
+ fd_close(p[0]) ;
+ fd_close(pcoe[0]) ;
+ if (coe(pcoe[1]) < 0) _exit(111) ;
+ for (; i < 30 ; i++) dup(p[1]) ; /* hack. gcc's warning is justified. */
+ pathexec_run(argv[1], argv+1, envp) ;
+ i = errno ;
+ if (fd_write(pcoe[1], "", 1) < 1) _exit(111) ;
+ _exit(i) ;
+ }
+ }
+
+ fd_close(p[1]) ;
+ fd_close(pcoe[1]) ;
+
+ switch (fd_read(pcoe[0], &dummy, 1))
+ {
+ case -1 : strerr_diefu1sys(111, "read on coepipe") ;
+ case 1 :
+ {
+ int wstat ;
+ if (wait_pid(pid, &wstat) < 0) strerr_diefu1sys(111, "wait_pid") ;
+ errno = WEXITSTATUS(wstat) ;
+ strerr_dieexec(111, argv[1]) ;
+ }
+ }
+
+ fd_close(pcoe[0]) ;
+
+ p[1] = fd_read(p[0], &dummy, 1) ;
+ if (p[1] < 0) strerr_diefu1sys(111, "read on hackpipe") ;
+ if (p[1]) strerr_dief2x(102, argv[1], " wrote on hackpipe") ;
+
+ {
+ int wstat ;
+ if (wait_pid(pid, &wstat) < 0) strerr_diefu1sys(111, "wait_pid") ;
+ if (WIFSIGNALED(wstat)) strerr_dief2x(111, argv[2], " crashed") ;
+ return WEXITSTATUS(wstat) ;
+ }
+}
diff --git a/src/daemontools-extras/s6-log.c b/src/daemontools-extras/s6-log.c
new file mode 100644
index 0000000..e788016
--- /dev/null
+++ b/src/daemontools-extras/s6-log.c
@@ -0,0 +1,1265 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h> /* for rename() */
+#include <stdlib.h> /* for qsort() */
+#include <regex.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/uint.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/bufalloc.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/tai.h>
+#include <skalibs/error.h>
+#include <skalibs/iopause.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/direntry.h>
+#include <skalibs/sig.h>
+#include <skalibs/selfpipe.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/environ.h>
+
+#define USAGE "s6-log [ -q | -v ] [ -b ] [ -p ] [ -t ] [ -e ] logging_script"
+#define dienomem() strerr_diefu1sys(111, "stralloc_catb")
+
+static int flagstampalert = 0 ;
+static int flagstamp = 0 ;
+static int flagprotect = 0 ;
+static int flagexiting = 0 ;
+static unsigned int verbosity = 1 ;
+
+static stralloc indata = STRALLOC_ZERO ;
+
+
+/* Begin datatypes. Get ready for some lulz. */
+
+typedef int qcmpfunc_t (void const *, void const *) ;
+typedef qcmpfunc_t *qcmpfunc_t_ref ;
+
+typedef enum rotstate_e rotstate_t, *rotstate_t_ref ;
+enum rotstate_e
+{
+ ROTSTATE_WRITABLE,
+ ROTSTATE_START,
+ ROTSTATE_RENAME,
+ ROTSTATE_NEWCURRENT,
+ ROTSTATE_CHMODPREVIOUS,
+ ROTSTATE_FINISHPREVIOUS,
+ ROTSTATE_RUNPROCESSOR,
+ ROTSTATE_WAITPROCESSOR,
+ ROTSTATE_SYNCPROCESSED,
+ ROTSTATE_SYNCNEWSTATE,
+ ROTSTATE_UNLINKPREVIOUS,
+ ROTSTATE_RENAMESTATE,
+ ROTSTATE_FINISHPROCESSED,
+ ROTSTATE_ENDFCHMOD,
+ ROTSTATE_END
+} ;
+
+typedef enum seltype_e seltype_t, *seltype_t_ref ;
+enum seltype_e
+{
+ SELTYPE_DEFAULT,
+ SELTYPE_PLUS,
+ SELTYPE_MINUS,
+ SELTYPE_PHAIL
+} ;
+
+typedef struct sel_s sel_t, *sel_t_ref ;
+struct sel_s
+{
+ seltype_t type ;
+ regex_t re ;
+} ;
+
+#define SEL_ZERO { .type = SELTYPE_PHAIL }
+
+static void sel_free (sel_t_ref s)
+{
+ if (s->type != SELTYPE_DEFAULT) regfree(&s->re) ;
+ s->type = SELTYPE_PHAIL ;
+}
+
+typedef enum acttype_e acttype_t, *acttype_t_ref ;
+enum acttype_e
+{
+ ACTTYPE_FD2,
+ ACTTYPE_STATUS,
+ ACTTYPE_DIR,
+ ACTTYPE_PHAIL
+} ;
+
+typedef struct as_fd2_s as_fd2_t, *as_fd2_t_ref ;
+struct as_fd2_s
+{
+ unsigned int size ;
+} ;
+
+typedef struct as_status_s as_status_t, *as_status_t_ref ;
+struct as_status_s
+{
+ stralloc content ;
+ char const *file ;
+} ;
+
+static void as_status_free (as_status_t_ref ap)
+{
+ stralloc_free(&ap->content) ;
+ ap->file = 0 ;
+}
+
+typedef struct as_dir_s as_dir_t, *as_dir_t_ref ;
+struct as_dir_s
+{
+ unsigned int lindex ;
+} ;
+
+typedef union actstuff_u actstuff_t, *actstuff_t_ref ;
+union actstuff_u
+{
+ as_fd2_t fd2 ;
+ as_status_t status ;
+ as_dir_t dir ;
+} ;
+
+typedef struct act_s act_t, *act_t_ref ;
+struct act_s
+{
+ acttype_t type ;
+ actstuff_t data ;
+} ;
+
+static void act_free (act_t_ref ap)
+{
+ switch (ap->type)
+ {
+ case ACTTYPE_FD2 :
+ break ;
+ case ACTTYPE_STATUS :
+ as_status_free(&ap->data.status) ;
+ break ;
+ case ACTTYPE_DIR :
+ break ;
+ default : break ;
+ }
+ ap->type = ACTTYPE_PHAIL ;
+}
+
+typedef struct scriptelem_s scriptelem_t, *scriptelem_t_ref ;
+struct scriptelem_s
+{
+ genalloc selections ; /* array of sel_t */
+ genalloc actions ; /* array of act_t */
+} ;
+
+#define SCRIPTELEM_ZERO { .selections = GENALLOC_ZERO, .actions = GENALLOC_ZERO }
+
+static void scriptelem_free (scriptelem_t_ref se)
+{
+ scriptelem_t zero = SCRIPTELEM_ZERO ;
+ genalloc_deepfree(sel_t, &se->selections, &sel_free) ;
+ genalloc_deepfree(act_t, &se->actions, &act_free) ;
+ *se = zero ;
+}
+
+typedef void inputprocfunc_t (scriptelem_t const *, unsigned int) ;
+typedef inputprocfunc_t *inputprocfunc_t_ref ;
+
+typedef struct logdir_s logdir_t, *logdir_t_ref ;
+struct logdir_s
+{
+ bufalloc out ;
+ tain_t retrytto ;
+ tain_t deadline ;
+ uint64 maxdirsize ;
+ uint32 b ;
+ uint32 n ;
+ uint32 s ;
+ uint32 tolerance ;
+ unsigned int pid ;
+ char const *dir ;
+ char const *processor ;
+ int fd ;
+ int fdlock ;
+ rotstate_t rstate ;
+} ;
+
+#define LOGDIR_ZERO { \
+ .out = BUFALLOC_ZERO, \
+ .retrytto = TAIN_ZERO, \
+ .deadline = TAIN_ZERO, \
+ .maxdirsize = 0, \
+ .b = 0, \
+ .n = 0, \
+ .s = 0, \
+ .tolerance = 0, \
+ .pid = 0, \
+ .dir = 0, \
+ .processor = 0, \
+ .fd = -1, \
+ .fdlock = -1, \
+ .rstate = ROTSTATE_WRITABLE \
+}
+
+ /* If freeing a logdir before exiting is ever needed:
+static void logdir_free (logdir_t_ref ldp)
+{
+ bufalloc_free(&ldp->out) ;
+ fd_close(ldp->fd) ; ldp->fd = -1 ;
+ fd_close(ldp->fdlock) ; ldp->fdlock = -1 ;
+}
+ */
+
+/* End datatypes. All of this was just to optimize the script interpretation. :-) */
+
+static genalloc logdirs = GENALLOC_ZERO ; /* array of logdir_t */
+
+typedef struct filesize_s filesize_t, *filesize_t_ref ;
+struct filesize_s
+{
+ uint64 size ;
+ char name[28] ;
+} ;
+
+static int filesize_cmp (filesize_t const *a, filesize_t const *b)
+{
+ return byte_diff(a->name+1, 26, b->name+1) ;
+}
+
+static int name_is_relevant (char const *name)
+{
+ if (name[0] != '@') return 0 ;
+ if (str_len(name) != 27) return 0 ;
+ {
+ char tmp[12] ;
+ if (!ucharn_scan(name+1, tmp, 12)) return 0 ;
+ }
+ if (name[25] != '.') return 0 ;
+ if ((name[26] != 's') && (name[26] != 'u')) return 0 ;
+ return 1 ;
+}
+
+static inline int logdir_trim (logdir_t_ref ldp)
+{
+ unsigned int n = 0 ;
+ DIR *dir = opendir(ldp->dir) ;
+ if (!dir) return -1 ;
+ for (;;)
+ {
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (name_is_relevant(d->d_name)) n++ ;
+ }
+ if (errno)
+ {
+ register int e = errno ;
+ dir_close(dir) ;
+ errno = e ;
+ return -1 ;
+ }
+ rewinddir(dir) ;
+ {
+ filesize_t blurgh[n] ;
+ uint64 totalsize = 0 ;
+ unsigned int dirlen = str_len(ldp->dir) ;
+ unsigned int i = 0 ;
+ char fullname[dirlen + 29] ;
+ byte_copy(fullname, dirlen, ldp->dir) ;
+ fullname[dirlen] = '/' ;
+ for (;;)
+ {
+ struct stat st ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (!name_is_relevant(d->d_name)) continue ;
+ if (i >= n) { errno = EBUSY ; break ; }
+ byte_copy(fullname + dirlen + 1, 28, d->d_name) ;
+ if (stat(fullname, &st) < 0)
+ {
+ if (verbosity) strerr_warnwu2sys("stat ", fullname) ;
+ continue ;
+ }
+ byte_copy(blurgh[i].name, 28, d->d_name) ;
+ blurgh[i].size = st.st_size ;
+ totalsize += st.st_size ;
+ i++ ;
+ }
+ if (errno)
+ {
+ register int e = errno ;
+ dir_close(dir) ;
+ errno = e ;
+ return -1 ;
+ }
+ dir_close(dir) ;
+ if ((i <= ldp->n) && (!ldp->maxdirsize || (totalsize <= ldp->maxdirsize)))
+ return 0 ;
+ qsort(blurgh, i, sizeof(filesize_t), (qcmpfunc_t_ref)&filesize_cmp) ;
+ n = 0 ;
+ while ((i > ldp->n + n) || (ldp->maxdirsize && (totalsize > ldp->maxdirsize)))
+ {
+ byte_copy(fullname + dirlen + 1, 28, blurgh[n].name) ;
+ if (unlink(fullname) < 0)
+ {
+ if (errno == ENOENT) totalsize -= blurgh[n].size ;
+ if (verbosity) strerr_warnwu2sys("unlink ", fullname) ;
+ }
+ else totalsize -= blurgh[n].size ;
+ n++ ;
+ }
+ }
+ return n ;
+}
+
+static int finish (logdir_t *ldp, char const *name, char suffix)
+{
+ struct stat st ;
+ unsigned int dirlen = str_len(ldp->dir) ;
+ unsigned int namelen = str_len(name) ;
+ char x[dirlen + namelen + 2] ;
+ byte_copy(x, dirlen, ldp->dir) ;
+ x[dirlen] = '/' ;
+ byte_copy(x + dirlen + 1, namelen + 1, name) ;
+ if (stat(x, &st) < 0) return (errno == ENOENT) ;
+ if (st.st_nlink == 1)
+ {
+ char y[dirlen + 29] ;
+ byte_copy(y, dirlen, ldp->dir) ;
+ y[dirlen] = '/' ;
+ timestamp_g(y + dirlen + 1) ;
+ y[dirlen + 26] = '.' ;
+ y[dirlen + 27] = suffix ;
+ y[dirlen + 28] = 0 ;
+ if (link(x, y) < 0) return 0 ;
+ }
+ if (unlink(x) < 0) return 0 ;
+ return logdir_trim(ldp) ;
+}
+
+static inline void exec_processor (logdir_t_ref ldp)
+{
+ char const *cargv[4] = { "execlineb", "-Pc", ldp->processor, 0 } ;
+ unsigned int dirlen = str_len(ldp->dir) ;
+ int fd ;
+ char x[dirlen + 10] ;
+ PROG = "s6-log (processor child)" ;
+ byte_copy(x, dirlen, ldp->dir) ;
+ byte_copy(x + dirlen, 10, "/previous") ;
+ fd = open_readb(x) ;
+ if (fd < 0) strerr_diefu2sys(111, "open_readb ", x) ;
+ if (fd_move(0, fd) < 0) strerr_diefu2sys(111, "fd_move ", x) ;
+ byte_copy(x + dirlen + 1, 10, "processed") ;
+ fd = open_trunc(x) ;
+ if (fd < 0) strerr_diefu2sys(111, "open_trunc ", x) ;
+ if (fd_move(1, fd) < 0) strerr_diefu2sys(111, "fd_move ", x) ;
+ byte_copy(x + dirlen + 1, 6, "state") ;
+ fd = open_readb(x) ;
+ if (fd < 0) strerr_diefu2sys(111, "open_readb ", x) ;
+ if (fd_move(4, fd) < 0) strerr_diefu2sys(111, "fd_move ", x) ;
+ byte_copy(x + dirlen + 1, 9, "newstate") ;
+ fd = open_trunc(x) ;
+ if (fd < 0) strerr_diefu2sys(111, "open_trunc ", x) ;
+ if (fd_move(5, fd) < 0) strerr_diefu2sys(111, "fd_move ", x) ;
+ selfpipe_finish() ;
+ sig_restore(SIGPIPE) ;
+ pathexec_run(cargv[0], cargv, (char const *const *)environ) ;
+ strerr_dieexec(111, cargv[0]) ;
+}
+
+static int rotator (logdir_t_ref ldp)
+{
+ unsigned int dirlen = str_len(ldp->dir) ;
+ switch (ldp->rstate)
+ {
+ case ROTSTATE_START :
+ {
+ if (fd_sync(ldp->fd) < 0)
+ {
+ if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ;
+ goto fail ;
+ }
+ tain_now_g() ;
+ ldp->rstate = ROTSTATE_RENAME ;
+ }
+ case ROTSTATE_RENAME :
+ {
+ char current[dirlen + 9] ;
+ char previous[dirlen + 10] ;
+ byte_copy(current, dirlen, ldp->dir) ;
+ byte_copy(current + dirlen, 9, "/current") ;
+ byte_copy(previous, dirlen, ldp->dir) ;
+ byte_copy(previous + dirlen, 10, "/previous") ;
+ if (rename(current, previous) < 0)
+ {
+ if (verbosity) strerr_warnwu4sys("rename ", current, " to ", previous) ;
+ goto fail ;
+ }
+ ldp->rstate = ROTSTATE_NEWCURRENT ;
+ }
+ case ROTSTATE_NEWCURRENT :
+ {
+ int fd ;
+ char x[dirlen + 9] ;
+ byte_copy(x, dirlen, ldp->dir) ;
+ byte_copy(x + dirlen, 9, "/current") ;
+ fd = open_append(x) ;
+ if (fd < 0)
+ {
+ if (verbosity) strerr_warnwu2sys("open_append ", x) ;
+ goto fail ;
+ }
+ if (coe(fd) < 0)
+ {
+ register int e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ if (verbosity) strerr_warnwu2sys("coe ", x) ;
+ goto fail ;
+ }
+ if (fd_chmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0)
+ {
+ register int e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ if (verbosity) strerr_warnwu3sys("fchmod ", x, " to 0644") ;
+ goto fail ;
+ }
+ fd_close(ldp->fd) ;
+ ldp->fd = fd ;
+ ldp->b = 0 ;
+ ldp->rstate = ROTSTATE_CHMODPREVIOUS ;
+ }
+ case ROTSTATE_CHMODPREVIOUS :
+ {
+ char x[dirlen + 10] ;
+ byte_copy(x, dirlen, ldp->dir) ;
+ byte_copy(x + dirlen, 10, "/previous") ;
+ if (chmod(x, S_IRWXU | S_IRGRP | S_IROTH) < 0)
+ {
+ if (verbosity) strerr_warnwu3sys("chmod ", x, " to 0744") ;
+ goto fail ;
+ }
+ if (ldp->processor) goto runprocessor ;
+ ldp->rstate = ROTSTATE_FINISHPREVIOUS ;
+ }
+ case ROTSTATE_FINISHPREVIOUS :
+ {
+ if (finish(ldp, "previous", 's') < 0)
+ {
+ if (verbosity) strerr_warnwu2sys("finish previous .s to logdir ", ldp->dir) ;
+ goto fail ;
+ }
+ tain_copynow(&ldp->deadline) ;
+ ldp->rstate = ROTSTATE_WRITABLE ;
+ break ;
+ }
+ runprocessor :
+ ldp->rstate = ROTSTATE_RUNPROCESSOR ;
+ case ROTSTATE_RUNPROCESSOR :
+ {
+ int pid = fork() ;
+ if (pid < 0)
+ {
+ if (verbosity) strerr_warnwu2sys("fork processor for logdir ", ldp->dir) ;
+ goto fail ;
+ }
+ else if (!pid) exec_processor(ldp) ;
+ ldp->pid = (unsigned int)pid ;
+ tain_add_g(&ldp->deadline, &tain_infinite_relative) ;
+ ldp->rstate = ROTSTATE_WAITPROCESSOR ;
+ }
+ case ROTSTATE_WAITPROCESSOR :
+ {
+ return (errno = EAGAIN, 0) ;
+ }
+ case ROTSTATE_SYNCPROCESSED :
+ {
+ int fd ;
+ char x[dirlen + 11] ;
+ byte_copy(x, dirlen, ldp->dir) ;
+ byte_copy(x + dirlen, 11, "/processed") ;
+ fd = open_append(x) ;
+ if (fd < 0)
+ {
+ if (verbosity) strerr_warnwu2sys("open_append ", x) ;
+ goto fail ;
+ }
+ if (fd_sync(fd) < 0)
+ {
+ register int e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ if (verbosity) strerr_warnwu2sys("fd_sync ", x) ;
+ goto fail ;
+ }
+ tain_now_g() ;
+ if (fd_chmod(fd, S_IRWXU | S_IRGRP | S_IROTH) < 0)
+ {
+ register int e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ if (verbosity) strerr_warnwu3sys("fd_chmod ", x, " to 0744") ;
+ goto fail ;
+ }
+ fd_close(fd) ;
+ ldp->rstate = ROTSTATE_SYNCNEWSTATE ;
+ }
+ case ROTSTATE_SYNCNEWSTATE :
+ {
+ int fd ;
+ char x[dirlen + 10] ;
+ byte_copy(x, dirlen, ldp->dir) ;
+ byte_copy(x + dirlen, 10, "/newstate") ;
+ fd = open_append(x) ;
+ if (ldp->fd < 0)
+ {
+ if (verbosity) strerr_warnwu2sys("open_append ", x) ;
+ goto fail ;
+ }
+ if (fd_sync(fd) < 0)
+ {
+ if (verbosity) strerr_warnwu2sys("fd_sync ", x) ;
+ goto fail ;
+ }
+ tain_now_g() ;
+ fd_close(fd) ;
+ ldp->rstate = ROTSTATE_UNLINKPREVIOUS ;
+ }
+ case ROTSTATE_UNLINKPREVIOUS :
+ {
+ char x[dirlen + 10] ;
+ byte_copy(x, dirlen, ldp->dir) ;
+ byte_copy(x + dirlen, 10, "/previous") ;
+ if ((unlink(x) < 0) && (errno != ENOENT))
+ {
+ if (verbosity) strerr_warnwu2sys("open_append ", x) ;
+ goto fail ;
+ }
+ ldp->rstate = ROTSTATE_RENAMESTATE ;
+ }
+ case ROTSTATE_RENAMESTATE :
+ {
+ char newstate[dirlen + 10] ;
+ char state[dirlen + 7] ;
+ byte_copy(newstate, dirlen, ldp->dir) ;
+ byte_copy(state, dirlen, ldp->dir) ;
+ byte_copy(newstate + dirlen, 10, "/newstate") ;
+ byte_copy(state + dirlen, 7, "/state") ;
+ if (rename(newstate, state) < 0)
+ {
+ if (verbosity) strerr_warnwu4sys("rename ", newstate, " to ", state) ;
+ goto fail ;
+ }
+ ldp->rstate = ROTSTATE_FINISHPROCESSED ;
+ }
+ case ROTSTATE_FINISHPROCESSED :
+ {
+ if (finish(ldp, "processed", 's') < 0)
+ {
+ if (verbosity) strerr_warnwu2sys("finish processed .s to logdir ", ldp->dir) ;
+ goto fail ;
+ }
+ tain_copynow(&ldp->deadline) ;
+ ldp->rstate = ROTSTATE_WRITABLE ;
+ break ;
+ }
+ default : strerr_dief1x(101, "inconsistent state in rotator()") ;
+ }
+ return 1 ;
+ fail:
+ tain_add_g(&ldp->deadline, &ldp->retrytto) ;
+ return 0 ;
+}
+
+static int logdir_write (int i, char const *s, unsigned int len)
+{
+ logdir_t_ref ldp = genalloc_s(logdir_t, &logdirs) + (unsigned int)i ;
+ int r ;
+ unsigned int n = len ;
+ {
+ unsigned int m = byte_rchr(s, n, '\n') ;
+ if (m < n) n = m+1 ;
+ }
+ r = fd_write(ldp->fd, s, n) ;
+ if (r < 0)
+ {
+ if (!error_isagain(errno))
+ {
+ tain_add_g(&ldp->deadline, &ldp->retrytto) ;
+ if (verbosity) strerr_warnwu3sys("write to ", ldp->dir, "/current") ;
+ }
+ return r ;
+ }
+ ldp->b += r ;
+ if ((ldp->b + ldp->tolerance >= ldp->s) && (s[r-1] == '\n'))
+ {
+ ldp->rstate = ROTSTATE_START ;
+ rotator(ldp) ;
+ }
+ return r ;
+}
+
+static inline void rotate_or_flush (logdir_t *ldp)
+{
+ if ((ldp->rstate != ROTSTATE_WRITABLE) && !rotator(ldp)) return ;
+ if (ldp->b >= ldp->s)
+ {
+ ldp->rstate = ROTSTATE_START ;
+ if (!rotator(ldp)) return ;
+ }
+ bufalloc_flush(&ldp->out) ;
+}
+
+static inline void logdir_init (logdir_t *ap, uint32 s, uint32 n, uint32 tolerance, uint64 maxdirsize, tain_t const *retrytto, char const *processor, char const *name, unsigned int index)
+{
+ struct stat st ;
+ unsigned int dirlen = str_len(name) ;
+ int r ;
+ char x[dirlen + 11] ;
+ ap->s = s ;
+ ap->n = n ;
+ ap->pid = 0 ;
+ ap->tolerance = tolerance ;
+ ap->maxdirsize = maxdirsize ;
+ ap->retrytto = *retrytto ;
+ ap->processor = processor ;
+ ap->dir = name ;
+ ap->fd = -1 ;
+ ap->rstate = ROTSTATE_WRITABLE ;
+ r = mkdir(ap->dir, S_IRWXU | S_ISGID) ;
+ if ((r < 0) && (errno != EEXIST)) strerr_diefu2sys(111, "mkdir ", name) ;
+ byte_copy(x, dirlen, name) ;
+ byte_copy(x + dirlen, 6, "/lock") ;
+ ap->fdlock = open_append(x) ;
+ if ((ap->fdlock) < 0) strerr_diefu2sys(111, "open_append ", x) ;
+ if (lock_exnb(ap->fdlock) < 0) strerr_diefu2sys(111, "lock_exnb ", x) ;
+ if (coe(ap->fdlock) < 0) strerr_diefu2sys(111, "coe ", x) ;
+ byte_copy(x + dirlen + 1, 8, "current") ;
+ if (stat(x, &st) < 0)
+ {
+ if (errno != ENOENT) strerr_diefu2sys(111, "stat ", x) ;
+ }
+ else if (st.st_mode & S_IXUSR) goto opencurrent ;
+ byte_copy(x + dirlen + 1, 6, "state") ;
+ unlink(x) ;
+ byte_copy(x + dirlen + 1, 9, "newstate") ;
+ unlink(x) ;
+ {
+ int flagprocessed = 0 ;
+ byte_copy(x + dirlen + 1, 10, "processed") ;
+ if (stat(x, &st) < 0)
+ {
+ if (errno != ENOENT) strerr_diefu2sys(111, "stat ", x) ;
+ }
+ else if (st.st_mode & S_IXUSR) flagprocessed = 1 ;
+ if (flagprocessed)
+ {
+ byte_copy(x + dirlen + 1, 9, "previous") ;
+ unlink(x) ;
+ if (finish(ap, "processed", 's') < 0)
+ strerr_diefu2sys(111, "finish processed .s for logdir ", ap->dir) ;
+ }
+ else
+ {
+ unlink(x) ;
+ if (finish(ap, "previous", 'u') < 0)
+ strerr_diefu2sys(111, "finish previous .u for logdir ", ap->dir) ;
+ }
+ }
+ if (finish(ap, "current", 'u') < 0)
+ strerr_diefu2sys(111, "finish current .u for logdir ", ap->dir) ;
+ byte_copy(x + dirlen + 1, 6, "state") ;
+ ap->fd = open_trunc(x) ;
+ if (ap->fd < 0) strerr_diefu2sys(111, "open_trunc ", x) ;
+ fd_close(ap->fd) ;
+ st.st_size = 0 ;
+ byte_copy(x + dirlen + 1, 8, "current") ;
+ opencurrent:
+ ap->fd = open_append(x) ;
+ if (ap->fd < 0) strerr_diefu2sys(111, "open_append ", x) ;
+ if (fd_chmod(ap->fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1)
+ strerr_diefu2sys(111, "fd_chmod ", x) ;
+ if (coe(ap->fd) < 0) strerr_diefu2sys(111, "coe ", x) ;
+ ap->b = st.st_size ;
+ tain_copynow(&ap->deadline) ;
+ bufalloc_init(&ap->out, &logdir_write, (int)index) ;
+}
+
+
+ /* Script */
+
+static int script_update (genalloc *sc, genalloc *sa, genalloc *aa)
+{
+ scriptelem_t foo ;
+ genalloc_shrink(sel_t, sa) ;
+ genalloc_shrink(act_t, aa) ;
+ foo.selections = *sa ;
+ foo.actions = *aa ;
+ if (!genalloc_append(scriptelem_t, sc, &foo)) return 0 ;
+ *sa = genalloc_zero ;
+ *aa = genalloc_zero ;
+ return 1 ;
+}
+
+static inline int script_init (genalloc *sc, char const *const *argv)
+{
+ tain_t cur_retrytto ;
+ unsigned int cur_fd2_size = 200 ;
+ unsigned int cur_status_size = 1001 ;
+ uint32 cur_s = 99999 ;
+ uint32 cur_n = 10 ;
+ uint32 cur_tolerance = 2000 ;
+ uint64 cur_maxdirsize = 0 ;
+ genalloc cur_selections = GENALLOC_ZERO ; /* sel_t */
+ genalloc cur_actions = GENALLOC_ZERO ; /* act_t */
+ char const *cur_processor = 0 ;
+ int flagacted = 0 ;
+ tain_uint(&cur_retrytto, 2) ;
+
+ for (; *argv ; argv++)
+ {
+ switch (**argv)
+ {
+ case 'f' :
+ {
+ sel_t selitem ;
+ if (flagacted)
+ {
+ if (!script_update(sc, &cur_selections, &cur_actions)) return 0 ;
+ flagacted = 0 ;
+ }
+ selitem.type = SELTYPE_DEFAULT ;
+ if (!genalloc_append(sel_t, &cur_selections, &selitem)) return 0 ;
+ break ;
+ }
+ case '+' :
+ case '-' :
+ {
+ sel_t selitem ;
+ int r ;
+ if (flagacted)
+ {
+ if (!script_update(sc, &cur_selections, &cur_actions)) return 0 ;
+ flagacted = 0 ;
+ }
+ selitem.type = (**argv == '+') ? SELTYPE_PLUS : SELTYPE_MINUS ;
+ r = regcomp(&selitem.re, *argv+1, REG_EXTENDED | REG_NOSUB | REG_NEWLINE) ;
+ if (r == REG_ESPACE) return (errno = ENOMEM, 0) ;
+ if (r) goto fail ;
+ if (!genalloc_append(sel_t, &cur_selections, &selitem)) return 0 ;
+ break ;
+ }
+ case 'n' :
+ {
+ if (!uint320_scan(*argv + 1, &cur_n)) goto fail ;
+ break ;
+ }
+ case 's' :
+ {
+ if (!uint320_scan(*argv + 1, &cur_s)) goto fail ;
+ if (cur_s < 4096) cur_s = 4096 ;
+ if (cur_s > 16777215) cur_s = 16777215 ;
+ break ;
+ }
+ case 'S' :
+ {
+ if (!uint640_scan(*argv + 1, &cur_maxdirsize)) goto fail ;
+ break ;
+ }
+ case 'l' :
+ {
+ if (!uint320_scan(*argv + 1, &cur_tolerance)) goto fail ;
+ if (cur_tolerance > (cur_s >> 1))
+ strerr_dief3x(100, "directive ", *argv, " conflicts with previous s directive") ;
+ break ;
+ }
+ case 'r' :
+ {
+ uint32 t ;
+ if (!uint320_scan(*argv + 1, &t)) goto fail ;
+ if (!tain_from_millisecs(&cur_retrytto, (int)t)) return (errno = EINVAL, 0) ;
+ break ;
+ }
+ case 'E' :
+ {
+ if (!uint0_scan(*argv + 1, &cur_fd2_size)) goto fail ;
+ break ;
+ }
+ case '^' :
+ {
+ if (!uint0_scan(*argv + 1, &cur_status_size)) goto fail ;
+ break ;
+ }
+ case '!' :
+ {
+ cur_processor = (*argv)[1] ? *argv + 1 : 0 ;
+ break ;
+ }
+ case 'e' :
+ {
+ act_t a ;
+ flagacted = 1 ;
+ a.type = ACTTYPE_FD2 ;
+ a.data.fd2.size = cur_fd2_size ;
+ if (!genalloc_append(act_t, &cur_actions, &a)) return 0 ;
+ break ;
+ }
+ case '=' :
+ {
+ act_t a ;
+ flagacted = 1 ;
+ a.type = ACTTYPE_STATUS ;
+ a.data.status.file = *argv + 1 ;
+ a.data.status.content = stralloc_zero ;
+ if (cur_status_size && !stralloc_ready_tuned(&a.data.status.content, cur_status_size, 0, 0, 1)) return 0 ;
+ a.data.status.content.len = cur_status_size ;
+ if (!genalloc_append(act_t, &cur_actions, &a)) return 0 ;
+ break ;
+ }
+ case '.' :
+ case '/' :
+ {
+ act_t a ;
+ logdir_t ld = LOGDIR_ZERO ;
+ flagacted = 1 ;
+ a.type = ACTTYPE_DIR ;
+ a.data.dir.lindex = genalloc_len(logdir_t, &logdirs) ;
+ if (!genalloc_append(act_t, &cur_actions, &a)) return 0 ;
+ logdir_init(&ld, cur_s, cur_n, cur_tolerance, cur_maxdirsize, &cur_retrytto, cur_processor, *argv, genalloc_len(logdir_t, &logdirs)) ;
+ if (!genalloc_append(logdir_t, &logdirs, &ld)) return 0 ;
+ break ;
+ }
+ default : goto fail ;
+ }
+ }
+ if (flagacted)
+ {
+ if (!script_update(sc, &cur_selections, &cur_actions)) return 0 ;
+ }
+ else
+ {
+ genalloc_deepfree(sel_t, &cur_selections, &sel_free) ;
+ if (verbosity) strerr_warnw1x("ignoring extraneous non-action directives") ;
+ }
+ genalloc_shrink(logdir_t, &logdirs) ;
+ genalloc_shrink(scriptelem_t, sc) ;
+ if (!genalloc_len(scriptelem_t, sc))
+ strerr_dief1x(100, "no action directive specified") ;
+ return 1 ;
+ fail:
+ strerr_dief2x(100, "unrecognized directive: ", *argv) ;
+}
+
+static inline void doit_fd2 (as_fd2_t const *ap, char const *s, unsigned int len)
+{
+ if (flagstampalert)
+ {
+ char fmt[TIMESTAMP+1] ;
+ tain_now_g() ;
+ timestamp_g(fmt) ;
+ fmt[TIMESTAMP] = ' ' ;
+ buffer_put(buffer_2, fmt, TIMESTAMP+1) ;
+ }
+ buffer_puts(buffer_2, PROG) ;
+ buffer_puts(buffer_2, ": alert: ") ;
+ if (ap->size && len > ap->size) len = ap->size ;
+ buffer_put(buffer_2, s, len) ;
+ if (len == ap->size) buffer_puts(buffer_2, "...") ;
+ buffer_putflush(buffer_2, "\n", 1) ;
+}
+
+static inline void doit_status (as_status_t const *ap, char const *s, unsigned int len)
+{
+ if (ap->content.len)
+ {
+ register unsigned int i ;
+ if (len > ap->content.len) len = ap->content.len ;
+ byte_copy(ap->content.s, len, s) ;
+ for (i = len ; i < ap->content.len ; i++) ap->content.s[i] = '\n' ;
+ if (!openwritenclose_suffix_sync(ap->file, ap->content.s, ap->content.len, ".new"))
+ strerr_warnwu2sys("openwritenclose ", ap->file) ;
+ }
+ else if (!openwritenclose_suffix_sync(ap->file, s, len, ".new"))
+ strerr_warnwu2sys("openwritenclose ", ap->file) ;
+}
+
+static inline void doit_dir (as_dir_t const *ap, char const *s, unsigned int len)
+{
+ logdir_t_ref ldp = genalloc_s(logdir_t, &logdirs) + ap->lindex ;
+ if (!bufalloc_put(&ldp->out, s, len) || !bufalloc_put(&ldp->out, "\n", 1))
+ strerr_diefu1sys(111, "bufalloc_put") ;
+}
+
+
+ /* The script interpreter. */
+
+static inline void doit (scriptelem_t const *se, unsigned int n, char const *s, unsigned int len)
+{
+ int flagselected = 1 ;
+ int flagacted = 0 ;
+ unsigned int i = 0 ;
+ for (; i < n ; i++)
+ {
+ unsigned int sellen = genalloc_len(sel_t, &se[i].selections) ;
+ sel_t *sels = genalloc_s(sel_t, &se[i].selections) ;
+ unsigned int j = 0 ;
+ for (; j < sellen ; j++)
+ {
+ switch (sels[j].type)
+ {
+ case SELTYPE_DEFAULT :
+ flagselected = !flagacted ;
+ break ;
+ case SELTYPE_PLUS :
+ if (!flagselected && !regexec(&sels[j].re, flagstamp ? s+TIMESTAMP+1 : s, 0, 0, 0)) flagselected = 1 ;
+ break ;
+ case SELTYPE_MINUS :
+ if (flagselected && !regexec(&sels[j].re, flagstamp ? s+TIMESTAMP+1 : s, 0, 0, 0)) flagselected = 0 ;
+ break ;
+ default :
+ strerr_dief2x(101, "internal consistency error in ", "selection type") ;
+ }
+ }
+ if (flagselected)
+ {
+ unsigned int actlen = genalloc_len(act_t, &se[i].actions) ;
+ act_t *acts = genalloc_s(act_t, &se[i].actions) ;
+ flagacted = 1 ;
+ for (j = 0 ; j < actlen ; j++)
+ {
+ switch (acts[j].type)
+ {
+ case ACTTYPE_FD2 :
+ doit_fd2(&acts[j].data.fd2, s, len) ;
+ break ;
+ case ACTTYPE_STATUS :
+ doit_status(&acts[j].data.status, s, len) ;
+ break ;
+ case ACTTYPE_DIR :
+ doit_dir(&acts[j].data.dir, s, len) ;
+ break ;
+ default :
+ strerr_dief2x(101, "internal consistency error in ", "action type") ;
+ }
+ }
+ }
+ }
+ if (flagstamp) tain_now_g() ;
+}
+
+static inline void processor_died (logdir_t_ref ldp, int wstat)
+{
+ ldp->pid = 0 ;
+ if (WIFSIGNALED(wstat))
+ {
+ if (verbosity) strerr_warnw2x("processor crashed in ", ldp->dir) ;
+ tain_add_g(&ldp->deadline, &ldp->retrytto) ;
+ ldp->rstate = ROTSTATE_RUNPROCESSOR ;
+ }
+ else if (WEXITSTATUS(wstat))
+ {
+ if (verbosity) strerr_warnw2x("processor failed in ", ldp->dir) ;
+ tain_add_g(&ldp->deadline, &ldp->retrytto) ;
+ ldp->rstate = ROTSTATE_RUNPROCESSOR ;
+ }
+ else
+ {
+ ldp->rstate = ROTSTATE_SYNCPROCESSED ;
+ rotator(ldp) ;
+ }
+}
+
+static void prepare_to_exit (void)
+{
+ fd_close(0) ;
+ flagexiting = 1 ;
+}
+
+static void stampanddoit (scriptelem_t const *se, unsigned int n)
+{
+ if (flagstamp) indata.s[timestamp_g(indata.s)] = ' ' ;
+ indata.s[indata.len] = 0 ;
+ doit(se, n, indata.s, indata.len-1) ;
+ indata.len = flagstamp ? TIMESTAMP+1 : 0 ;
+}
+
+static void normal_stdin (scriptelem_t const *se, unsigned int selen)
+{
+ int r = sanitize_read(buffer_fill(buffer_0)) ;
+ if (r < 0)
+ {
+ if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ;
+ prepare_to_exit() ;
+ }
+ else if (r)
+ while (skagetln_nofill(buffer_0, &indata, '\n') > 0)
+ stampanddoit(se, selen) ;
+}
+
+static void last_stdin (scriptelem_t const *se, unsigned int selen)
+{
+ int cont = 1 ;
+ while (cont)
+ {
+ char c ;
+ switch (sanitize_read(fd_read(0, &c, 1)))
+ {
+ case 0 :
+ cont = 0 ;
+ break ;
+ case -1 :
+ if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ;
+ if (indata.len <= (flagstamp ? TIMESTAMP+1 : 0))
+ {
+ prepare_to_exit() ;
+ cont = 0 ;
+ break ;
+ }
+ c = '\n' ;
+ case 1 :
+ if (!stralloc_catb(&indata, &c, 1)) dienomem() ;
+ if (c == '\n')
+ {
+ stampanddoit(se, selen) ;
+ prepare_to_exit() ;
+ cont = 0 ;
+ }
+ break ;
+ }
+ }
+}
+
+static inputprocfunc_t_ref handle_stdin = &normal_stdin ;
+
+static inline void handle_signals (void)
+{
+ for (;;)
+ {
+ switch (selfpipe_read())
+ {
+ case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
+ case 0 : return ;
+ case SIGALRM :
+ {
+ unsigned int llen = genalloc_len(logdir_t, &logdirs) ;
+ logdir_t *ls = genalloc_s(logdir_t, &logdirs) ;
+ register unsigned int i = 0 ;
+ for (i = 0 ; i < llen ; i++)
+ if ((ls[i].rstate == ROTSTATE_WRITABLE) && ls[i].b)
+ {
+ ls[i].rstate = ROTSTATE_START ;
+ rotator(ls + i) ;
+ }
+ break ;
+ }
+ case SIGTERM :
+ {
+ if (flagprotect) break ;
+ handle_stdin = &last_stdin ;
+ if (indata.len <= (flagstamp ? TIMESTAMP+1 : 0)) prepare_to_exit() ;
+ break ;
+ }
+ case SIGCHLD :
+ {
+ unsigned int llen = genalloc_len(logdir_t, &logdirs) ;
+ logdir_t *ls = genalloc_s(logdir_t, &logdirs) ;
+ for (;;)
+ {
+ int wstat ;
+ register unsigned int i = 0 ;
+ register int r = wait_nohang(&wstat) ;
+ if (r <= 0) break ;
+ for (; i < llen ; i++) if ((unsigned int)r == ls[i].pid) break ;
+ if (i < llen) processor_died(ls + i, wstat) ;
+ }
+ break ;
+ }
+ default : strerr_dief1x(101, "internal consistency error with signal handling") ;
+ }
+ }
+}
+
+static inline int logdir_finalize (logdir_t_ref ldp)
+{
+ switch (ldp->rstate)
+ {
+ case ROTSTATE_WRITABLE :
+ {
+ if (fd_sync(ldp->fd) < 0)
+ {
+ if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ;
+ goto fail ;
+ }
+ tain_now_g() ;
+ ldp->rstate = ROTSTATE_ENDFCHMOD ;
+ }
+ case ROTSTATE_ENDFCHMOD :
+ {
+ if (fd_chmod(ldp->fd, S_IRWXU | S_IRGRP | S_IROTH) < 0)
+ {
+ if (verbosity) strerr_warnwu3sys("fd_chmod ", ldp->dir, "/current to 0744") ;
+ goto fail ;
+ }
+ ldp->rstate = ROTSTATE_END ;
+ break ;
+ }
+ default : strerr_dief1x(101, "inconsistent state in logdir_finalize()") ;
+ }
+ return 1 ;
+ fail:
+ tain_add_g(&ldp->deadline, &ldp->retrytto) ;
+ return 0 ;
+}
+
+static inline void finalize (void)
+{
+ unsigned int llen = genalloc_len(logdir_t, &logdirs) ;
+ logdir_t *ls = genalloc_s(logdir_t, &logdirs) ;
+ unsigned int n = llen ;
+ for (;;)
+ {
+ unsigned int i = 0 ;
+ tain_t deadline ;
+ tain_addsec_g(&deadline, 2) ;
+ for (; i < llen ; i++)
+ if (ls[i].rstate != ROTSTATE_END)
+ {
+ if (logdir_finalize(ls + i)) n-- ;
+ else if (tain_less(&ls[i].deadline, &deadline))
+ deadline = ls[i].deadline ;
+ }
+ if (!n) break ;
+ {
+ iopause_fd x ;
+ iopause_g(&x, 0, &deadline) ;
+ }
+ }
+}
+
+int main (int argc, char const *const *argv)
+{
+ genalloc logscript = GENALLOC_ZERO ; /* array of scriptelem_t */
+ int flagblock = 0 ;
+ PROG = "s6-log" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "qvbpte", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'q' : if (verbosity) verbosity-- ; break ;
+ case 'v' : verbosity++ ; break ;
+ case 'b' : flagblock = 1 ; break ;
+ case 'p' : flagprotect = 1 ; break ;
+ case 't' : flagstamp = 1 ; break ;
+ case 'e' : flagstampalert = 1 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (argc < 1) strerr_dieusage(100, USAGE) ;
+
+ fd_close(1) ;
+ {
+ int r = tain_now_g() ;
+ if (flagstamp)
+ {
+ char fmt[TIMESTAMP+1] ;
+ if (!stralloc_catb(&indata, fmt, TIMESTAMP+1)) dienomem() ;
+ if (!r) strerr_warnwu1sys("read current time - timestamps may be wrong for a while") ;
+ }
+ }
+ if (!script_init(&logscript, argv)) strerr_diefu1sys(111, "initialize logging script") ;
+ if (ndelay_on(0) < 0) strerr_diefu1sys(111, "ndelay_on(0)") ;
+
+ {
+ unsigned int llen = genalloc_len(logdir_t, &logdirs) ;
+ logdir_t *ls = genalloc_s(logdir_t, &logdirs) ;
+ iopause_fd x[2 + llen] ;
+ unsigned int active[llen] ;
+ x[0].fd = 0 ;
+ x[1].fd = selfpipe_init() ;
+ if (x[1].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "sig_ignore(SIGPIPE)") ;
+ {
+ sigset_t set ;
+ sigemptyset(&set) ;
+ sigaddset(&set, SIGTERM) ; sigaddset(&set, SIGALRM) ; sigaddset(&set, SIGCHLD) ;
+ if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "selfpipe_trapset") ;
+ }
+ x[1].events = IOPAUSE_READ ;
+
+ for (;;)
+ {
+ tain_t deadline ;
+ int r ;
+ unsigned int j = 0 ;
+ unsigned int i = 0 ;
+ int allflushed = 1 ;
+ tain_add_g(&deadline, &tain_infinite_relative) ;
+ for (; i < llen ; i++)
+ {
+ if (bufalloc_len(&ls[i].out) || (ls[i].rstate != ROTSTATE_WRITABLE))
+ {
+ allflushed = 0 ;
+ if (!tain_future(&ls[i].deadline))
+ {
+ x[2+j].fd = ls[i].fd ;
+ x[2+j].events = IOPAUSE_WRITE ;
+ active[j++] = i ;
+ }
+ else if (tain_less(&ls[i].deadline, &deadline))
+ deadline = ls[i].deadline ;
+ }
+ }
+ if (flagexiting && allflushed) break ;
+ x[0].events = (allflushed || !flagblock) ? IOPAUSE_READ : 0 ;
+ r = iopause_g(x + flagexiting, 2 - flagexiting + j, &deadline) ;
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+ else if (r)
+ {
+ if (x[1].revents & IOPAUSE_READ) handle_signals() ;
+ else if (x[1].revents & IOPAUSE_EXCEPT) strerr_dief1sys(111, "trouble with selfpipe") ;
+ for (i = 0 ; i < j ; i++)
+ if (x[2+i].revents & IOPAUSE_WRITE)
+ rotate_or_flush(ls + active[i]) ;
+ if (!flagexiting)
+ {
+ if (x[0].revents & IOPAUSE_READ)
+ (*handle_stdin)(genalloc_s(scriptelem_t, &logscript), genalloc_len(scriptelem_t, &logscript)) ;
+ else if (x[0].revents & IOPAUSE_EXCEPT)
+ {
+ prepare_to_exit() ;
+ if (indata.len > (flagstamp ? TIMESTAMP+1 : 0))
+ {
+ if (!stralloc_0(&indata)) dienomem() ;
+ stampanddoit(genalloc_s(scriptelem_t, &logscript), genalloc_len(scriptelem_t, &logscript)) ;
+ }
+ }
+ }
+ }
+ }
+ }
+ genalloc_deepfree(scriptelem_t, &logscript, &scriptelem_free) ;
+ finalize() ;
+ return 0 ;
+}
diff --git a/src/daemontools-extras/s6-notifywhenup.c b/src/daemontools-extras/s6-notifywhenup.c
new file mode 100644
index 0000000..a4be329
--- /dev/null
+++ b/src/daemontools-extras/s6-notifywhenup.c
@@ -0,0 +1,86 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <skalibs/uint.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/djbunix.h>
+#include <s6/ftrigw.h>
+
+#define USAGE "s6-notifywhenup [ -d fd ] [ -e fifodir ] [ -f ] [ -t timeout ] prog..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static int run_child (int fd, char const *fifodir, unsigned int timeout)
+{
+ char dummy[4096] ;
+ iopause_fd x = { .fd = fd, .events = IOPAUSE_READ } ;
+ tain_t deadline ;
+ int haswritten = 0 ;
+ register int r = 0 ;
+ if (!tain_now_g()) strerr_diefu1sys(111, "tain_now") ;
+ tain_from_millisecs(&deadline, timeout) ;
+ tain_add_g(&deadline, &deadline) ;
+ while (!r)
+ {
+ register int r = iopause_g(&x, 1, &deadline) ;
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+ if (!r) return 99 ;
+ while (r > 0)
+ {
+ r = sanitize_read(fd_read(fd, dummy, 4096)) ;
+ if (r > 0) haswritten = 1 ;
+ }
+ }
+ if (errno != EPIPE) strerr_diefu1sys(111, "read from parent") ;
+ if (haswritten) ftrigw_notify(fifodir, 'U') ;
+ return 0 ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int fd = 1 ;
+ char const *fifodir = "event" ;
+ int df = 1 ;
+ unsigned int timeout = 0 ;
+ PROG = "s6-notifywhenup" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "d:e:ft:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'd' : if (!uint0_scan(l.arg, &fd)) dieusage() ; break ;
+ case 'e' : fifodir = l.arg ; break ;
+ case 'f' : df = 0 ; break ;
+ case 't' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc) dieusage() ;
+
+ {
+ int p[2] ;
+ pid_t pid ;
+ if (pipe(p) < 0) strerr_diefu1sys(111, "pipe") ;
+ pid = df ? doublefork() : fork() ;
+ if (pid < 0) strerr_diefu1sys(111, df ? "doublefork" : "fork") ;
+ else if (pid)
+ {
+ PROG = "s6-notifywhenup (child)" ;
+ fd_close(p[1]) ;
+ return run_child(p[0], fifodir, timeout) ;
+ }
+ fd_close(p[0]) ;
+ if (fd_move((int)fd, p[1]) < 0) strerr_diefu1sys(111, "fd_move") ;
+ }
+ pathexec_run(argv[0], argv, envp) ;
+ strerr_dieexec(111, argv[1]) ;
+}
diff --git a/src/daemontools-extras/s6-setlock.c b/src/daemontools-extras/s6-setlock.c
new file mode 100644
index 0000000..2fb6f12
--- /dev/null
+++ b/src/daemontools-extras/s6-setlock.c
@@ -0,0 +1,87 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/uint.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/djbunix.h>
+#include <s6/config.h>
+
+#define USAGE "s6-setlock [ -r | -w ] [ -n | -N | -t timeout ] lockfile prog..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+typedef int lockfunc_t (int) ;
+typedef lockfunc_t *lockfunc_t_ref ;
+
+static lockfunc_t_ref f[2][2] = { { &lock_sh, &lock_shnb }, { &lock_ex, &lock_exnb } } ;
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int nb = 0, ex = 1 ;
+ unsigned int timeout = 0 ;
+ PROG = "s6-setlock" ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "nNrwt:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n' : nb = 1 ; break ;
+ case 'N' : nb = 0 ; break ;
+ case 'r' : ex = 0 ; break ;
+ case 'w' : ex = 1 ; break ;
+ case 't' : if (!uint0_scan(subgetopt_here.arg, &timeout)) dieusage() ;
+ nb = 2 ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (argc < 2) dieusage() ;
+
+ if (nb < 2)
+ {
+ int fd = open_create(argv[0]) ;
+ if (fd == -1) strerr_diefu2sys(111, "open_create ", argv[0]) ;
+ if ((*f[ex][nb])(fd) == -1) strerr_diefu2sys(1, "lock ", argv[0]) ;
+ }
+ else
+ {
+ char const *cargv[3] = { "s6lockd-helper", argv[0], 0 } ;
+ char const *cenvp[2] = { ex ? "S6LOCK_EX=1" : 0, 0 } ;
+ iopause_fd x = { .events = IOPAUSE_READ } ;
+ tain_t deadline ;
+ int p[2] ;
+ unsigned int pid ;
+ char c ;
+ if (!tain_now_g()) strerr_diefu1sys(111, "tain_now") ;
+ tain_from_millisecs(&deadline, timeout) ;
+ tain_add_g(&deadline, &deadline) ;
+ pid = child_spawn(S6_BINPREFIX "s6lockd-helper", cargv, cenvp, p, 2) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", S6_BINPREFIX "s6lockd-helper") ;
+ x.fd = p[0] ;
+ for (;;)
+ {
+ register int r = iopause_g(&x, 1, &deadline) ;
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+ if (!r)
+ {
+ kill(pid, SIGTERM) ;
+ errno = ETIMEDOUT ;
+ strerr_diefu1sys(1, "acquire lock") ;
+ }
+ r = sanitize_read(fd_read(p[0], &c, 1)) ;
+ if (r < 0) strerr_diefu1sys(111, "read ack from helper") ;
+ if (r) break ;
+ }
+ if (c != '!') strerr_dief1x(111, "helper sent garbage ack") ;
+ fd_close(p[0]) ;
+ if (uncoe(p[1]) < 0) strerr_diefu1sys(111, "uncoe fd to helper") ;
+ }
+ pathexec_run(argv[1], argv+1, envp) ;
+ strerr_dieexec(111, argv[1]) ;
+}
diff --git a/src/daemontools-extras/s6-setsid.c b/src/daemontools-extras/s6-setsid.c
new file mode 100644
index 0000000..efd3832
--- /dev/null
+++ b/src/daemontools-extras/s6-setsid.c
@@ -0,0 +1,35 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "s6-setsid [ -i | -I ] prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ int insist = 0 ;
+ PROG = "s6-setsid" ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "iI") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'i' : insist = 1 ; break ;
+ case 'I' : insist = 0 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (!argc) strerr_dieusage(100, USAGE) ;
+
+ if (setsid() < 0)
+ {
+ if (insist) strerr_diefu1sys(111, "setsid") ;
+ else strerr_warnwu1sys("setsid") ;
+ }
+ pathexec_run(argv[0], argv, envp) ;
+ strerr_dieexec(111, argv[0]) ;
+}
diff --git a/src/daemontools-extras/s6-setuidgid.c b/src/daemontools-extras/s6-setuidgid.c
new file mode 100644
index 0000000..d2e7361
--- /dev/null
+++ b/src/daemontools-extras/s6-setuidgid.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "s6-setuidgid username prog..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int pos ;
+ PROG = "s6-setuidgid" ;
+ if (argc < 3) dieusage() ;
+ pos = str_chr(argv[1], ':') ;
+ if (argv[1][pos])
+ {
+ unsigned int uid = 0, gid = 0, len = uint_scan(argv[1], &uid) ;
+ if (len != pos) dieusage() ;
+ if (argv[1][pos+1] && !uint0_scan(argv[1]+pos+1, &gid)) dieusage() ;
+ if (gid && setgid(gid)) strerr_diefu1sys(111, "setgid") ;
+ if (uid && setuid(uid)) strerr_diefu1sys(111, "setuid") ;
+ }
+ else if (!prot_setuidgid(argv[1]))
+ strerr_diefu2sys(111, "change identity to ", argv[1]) ;
+ pathexec_run(argv[2], argv+2, envp) ;
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/daemontools-extras/s6-softlimit.c b/src/daemontools-extras/s6-softlimit.c
new file mode 100644
index 0000000..61e0e4d
--- /dev/null
+++ b/src/daemontools-extras/s6-softlimit.c
@@ -0,0 +1,117 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint64.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "s6-softlimit [ -a allbytes ] [ -c corebytes ] [ -d databytes ] [ -f filebytes ] [ -l lockbytes ] [ -m membytes ] [ -o openfiles ] [ -p processes ] [ -r residentbytes ] [ -s stackbytes ] [ -t cpusecs ] prog..."
+
+static void doit (int res, char const *arg)
+{
+ struct rlimit r ;
+ if (getrlimit(res, &r) < 0) strerr_diefu1sys(111, "getrlimit") ;
+ if ((arg[0] == '=') && !arg[1]) r.rlim_cur = r.rlim_max ;
+ else
+ {
+ uint64 n ;
+ if (!uint640_scan(arg, &n)) strerr_dieusage(100, USAGE) ;
+ if (n > (uint64)r.rlim_max) n = (uint64)r.rlim_max ;
+ r.rlim_cur = (rlim_t)n ;
+ }
+ if (setrlimit(res, &r) == -1) strerr_diefu1sys(111, "setrlimit") ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ PROG = "s6-softlimit" ;
+ for (;;)
+ {
+ register int opt = sgetopt(argc, argv, "a:c:d:f:l:m:o:p:r:s:t:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'a' :
+#ifdef RLIMIT_AS
+ doit(RLIMIT_AS, subgetopt_here.arg) ;
+#endif
+#ifdef RLIMIT_VMEM
+ doit(RLIMIT_VMEM, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 'c' :
+#ifdef RLIMIT_CORE
+ doit(RLIMIT_CORE, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 'd' :
+#ifdef RLIMIT_DATA
+ doit(RLIMIT_DATA, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 'f' :
+#ifdef RLIMIT_FSIZE
+ doit(RLIMIT_FSIZE, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 'l' :
+#ifdef RLIMIT_MEMLOCK
+ doit(RLIMIT_MEMLOCK, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 'm' :
+#ifdef RLIMIT_DATA
+ doit(RLIMIT_DATA, subgetopt_here.arg) ;
+#endif
+#ifdef RLIMIT_STACK
+ doit(RLIMIT_STACK, subgetopt_here.arg) ;
+#endif
+#ifdef RLIMIT_MEMLOCK
+ doit(RLIMIT_MEMLOCK, subgetopt_here.arg) ;
+#endif
+#ifdef RLIMIT_VMEM
+ doit(RLIMIT_VMEM, subgetopt_here.arg) ;
+#endif
+#ifdef RLIMIT_AS
+ doit(RLIMIT_AS, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 'o' :
+#ifdef RLIMIT_NOFILE
+ doit(RLIMIT_NOFILE, subgetopt_here.arg) ;
+#endif
+#ifdef RLIMIT_OFILE
+ doit(RLIMIT_OFILE, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 'p' :
+#ifdef RLIMIT_NPROC
+ doit(RLIMIT_NPROC, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 'r' :
+#ifdef RLIMIT_RSS
+ doit(RLIMIT_RSS, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 's' :
+#ifdef RLIMIT_STACK
+ doit(RLIMIT_STACK, subgetopt_here.arg) ;
+#endif
+ break ;
+ case 't' :
+#ifdef RLIMIT_CPU
+ doit(RLIMIT_CPU, subgetopt_here.arg) ;
+#endif
+ break ;
+ }
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (!argc) strerr_dieusage(100, USAGE) ;
+ pathexec_run(argv[0], argv, envp) ;
+ strerr_dieexec(111, argv[0]) ;
+}
diff --git a/src/daemontools-extras/s6-tai64n.c b/src/daemontools-extras/s6-tai64n.c
new file mode 100644
index 0000000..085c053
--- /dev/null
+++ b/src/daemontools-extras/s6-tai64n.c
@@ -0,0 +1,35 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/skamisc.h>
+
+int main (void)
+{
+ char stamp[TIMESTAMP+1] ;
+ PROG = "s6-tai64n" ;
+ stamp[TIMESTAMP] = ' ' ;
+ for (;;)
+ {
+ register int r = skagetln(buffer_0f1, &satmp, '\n') ;
+ if (r < 0)
+ if (errno != EPIPE)
+ strerr_diefu1sys(111, "read from stdin") ;
+ else
+ {
+ r = 1 ;
+ if (!stralloc_catb(&satmp, "\n", 1))
+ strerr_diefu1sys(111, "add newline") ;
+ }
+ else if (!r) break ;
+ timestamp(stamp) ;
+ if ((buffer_put(buffer_1, stamp, TIMESTAMP+1) < 0)
+ || (buffer_put(buffer_1, satmp.s, satmp.len) < 0))
+ strerr_diefu1sys(111, "write to stdout") ;
+ satmp.len = 0 ;
+ }
+ return 0 ;
+}
diff --git a/src/daemontools-extras/s6-tai64nlocal.c b/src/daemontools-extras/s6-tai64nlocal.c
new file mode 100644
index 0000000..d7be880
--- /dev/null
+++ b/src/daemontools-extras/s6-tai64nlocal.c
@@ -0,0 +1,47 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbtime.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/skamisc.h>
+
+int main (void)
+{
+ PROG = "s6-tai64nlocal" ;
+ for (;;)
+ {
+ unsigned int p = 0 ;
+ int r = skagetln(buffer_0f1, &satmp, '\n') ;
+ if (r == -1)
+ if (errno != EPIPE)
+ strerr_diefu1sys(111, "read from stdin") ;
+ else r = 1 ;
+ else if (!r) break ;
+ if (satmp.len > TIMESTAMP)
+ {
+ tain_t a ;
+ p = timestamp_scan(satmp.s, &a) ;
+ if (p)
+ {
+ char fmt[LOCALTMN_FMT+1] ;
+ localtmn_t local ;
+ unsigned int len ;
+ localtmn_from_tain(&local, &a, 1) ;
+ len = localtmn_fmt(fmt, &local) ;
+ fmt[len++] = ' ' ;
+ if (buffer_put(buffer_1, fmt, len) < 0)
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+ }
+ if (buffer_put(buffer_1, satmp.s + p, satmp.len - p) < 0)
+ strerr_diefu1sys(111, "write to stdout") ;
+ satmp.len = 0 ;
+ }
+ return 0 ;
+}
diff --git a/src/daemontools-extras/ucspilogd.c b/src/daemontools-extras/ucspilogd.c
new file mode 100644
index 0000000..ddb6362
--- /dev/null
+++ b/src/daemontools-extras/ucspilogd.c
@@ -0,0 +1,117 @@
+/* ISC license. */
+
+#ifndef SYSLOG_NAMES
+#define SYSLOG_NAMES
+#endif
+
+#include <stdlib.h>
+#include <syslog.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+
+#define USAGE "ucspilogd [ -D default ] [ var... ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static inline void die (void)
+{
+ strerr_diefu1sys(111, "write to stdout") ;
+}
+
+static unsigned int syslog_names (char const *line)
+{
+ unsigned int fpr, i ;
+ int fp ;
+ CODE *p = facilitynames ;
+
+ if (line[0] != '<') return 0 ;
+ i = uint_scan(line+1, &fpr) ;
+ if (!i || (line[i+1] != '>')) return 0 ;
+ i += 2 ;
+
+ fp = LOG_FAC(fpr) << 3 ;
+ for (; p->c_name ; p++) if (p->c_val == fp) break ;
+ if (p->c_name)
+ {
+ if ((buffer_puts(buffer_1, p->c_name) < 0)
+ || (buffer_put(buffer_1, ".", 1) < 1)) die() ;
+ }
+ else
+ {
+ if (buffer_put(buffer_1, "unknown.", 8) < 8) die() ;
+ i = 0 ;
+ }
+
+ fp = LOG_PRI(fpr) ;
+ for (p = prioritynames ; p->c_name ; p++) if (p->c_val == fp) break ;
+ if (p->c_name)
+ {
+ if ((buffer_puts(buffer_1, p->c_name) < 0)
+ || (buffer_put(buffer_1, ": ", 2) < 2)) die() ;
+ }
+ else
+ {
+ if (buffer_put(buffer_1, "unknown: ", 9) < 9) die() ;
+ i = 0 ;
+ }
+ return i ;
+}
+
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ char const *d = "<undefined>" ;
+ PROG = "ucspilogd" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "D:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'D' : d = l.arg ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+
+ {
+ char const *envs[argc] ;
+ unsigned int i = 0 ;
+ for (; i < (unsigned int)argc ; i++)
+ {
+ envs[i] = env_get2(envp, argv[i]) ;
+ if (!envs[i]) envs[i] = d ;
+ }
+ for (;;)
+ {
+ unsigned int pos = 0 ;
+ satmp.len = 0 ;
+ {
+ register int r = skagetlnsep(buffer_0f1, &satmp, "\n", 2) ;
+ if (r < 0) strerr_diefu1sys(111, "read from stdin") ;
+ if (!r) break ;
+ }
+ if (!satmp.len) continue ;
+ satmp.s[satmp.len-1] = '\n' ;
+ if ((satmp.s[0] == '@') && (satmp.len > 26) && (byte_chr(satmp.s, 26, ' ') == 25))
+ {
+ if (buffer_put(buffer_1, satmp.s, 26) < 26) die() ;
+ pos += 26 ;
+ }
+ for (i = 0 ; i < (unsigned int)argc ; i++)
+ if ((buffer_puts(buffer_1, envs[i]) < 0)
+ || (buffer_put(buffer_1, ": ", 2) < 2)) die() ;
+ pos += syslog_names(satmp.s + pos) ;
+ if (buffer_put(buffer_1, satmp.s + pos, satmp.len - pos) < (int)(satmp.len - pos)) die() ;
+ }
+ }
+ return 0 ;
+}
diff --git a/src/include/s6/ftrigr.h b/src/include/s6/ftrigr.h
new file mode 100644
index 0000000..e7c8b0c
--- /dev/null
+++ b/src/include/s6/ftrigr.h
@@ -0,0 +1,94 @@
+/* ISC license. */
+
+#ifndef FTRIGR_H
+#define FTRIGR_H
+
+#include <skalibs/config.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6/config.h>
+
+
+ /* Constants */
+
+#define FTRIGR_IPCPATH SKALIBS_SPROOT "/service/ftrigrd/s"
+
+#define FTRIGRD_PROG S6_BINPREFIX "/s6-ftrigrd"
+#define FTRIGR_BANNER1 "ftrigr v1.0 (b)\n"
+#define FTRIGR_BANNER1_LEN (sizeof FTRIGR_BANNER1 - 1)
+#define FTRIGR_BANNER2 "ftrigr v1.0 (a)\n"
+#define FTRIGR_BANNER2_LEN (sizeof FTRIGR_BANNER2 - 1)
+
+
+ /* Internals of the ftrigr_t */
+
+typedef enum fr1state_e fr1state_t, *fr1state_t_ref ;
+enum fr1state_e
+{
+ FR1STATE_WAITACK,
+ FR1STATE_WAITACKDATA,
+ FR1STATE_LISTENING,
+ FR1STATE_ERROR
+} ;
+
+typedef struct ftrigr1_s ftrigr1_t, *ftrigr1_t_ref ;
+struct ftrigr1_s
+{
+ uint32 options ;
+ unsigned int count ;
+ fr1state_t state ;
+ char what ;
+} ;
+#define FTRIGR1_ZERO { 0, 0, FR1STATE_ERROR, 0 }
+extern ftrigr1_t const ftrigr1_zero ;
+
+
+ /* The ftrigr_t itself */
+
+typedef struct ftrigr_s ftrigr, ftrigr_t, *ftrigr_ref, *ftrigr_t_ref ;
+struct ftrigr_s
+{
+ skaclient_t connection ;
+ genalloc list ; /* array of uint16 */
+ gensetdyn data ; /* set of ftrigr1_t */
+ skaclient_buffer_t buffers ;
+} ;
+#define FTRIGR_ZERO { .connection = SKACLIENT_ZERO, .list = GENALLOC_ZERO, .data = GENSETDYN_INIT(ftrigr1_t, 2, 0, 1) }
+extern ftrigr_t const ftrigr_zero ;
+
+
+ /* Starting and ending a session */
+
+extern int ftrigr_start (ftrigr_t *, char const *, tain_t const *, tain_t *) ;
+#define ftrigr_start_g(a, path, deadline) ftrigr_start(a, path, (deadline), &STAMP)
+extern int ftrigr_startf (ftrigr_t *, tain_t const *, tain_t *) ;
+#define ftrigr_startf_g(a, deadline) ftrigr_startf(a, (deadline), &STAMP)
+extern void ftrigr_end (ftrigr_t *) ;
+
+
+ /* Instant primitives for async programming */
+
+#define ftrigr_fd(a) skaclient_fd(&(a)->connection)
+extern int ftrigr_update (ftrigr_t *) ;
+extern int ftrigr_check (ftrigr_t *, uint16, char *) ;
+
+
+ /* Synchronous functions with timeouts */
+
+#define FTRIGR_REPEAT 0x0001
+
+extern uint16 ftrigr_subscribe (ftrigr_t *, char const *, char const *, uint32, tain_t const *, tain_t *) ;
+#define ftrigr_subscribe_g(a, path, re, options, deadline) ftrigr_subscribe(a, path, re, options, (deadline), &STAMP)
+extern int ftrigr_unsubscribe (ftrigr_t *, uint16, tain_t const *, tain_t *) ;
+#define ftrigr_unsubscribe_g(a, id, deadline) ftrigr_unsubscribe(a, id, (deadline), &STAMP)
+
+extern int ftrigr_wait_and (ftrigr_t *, uint16 const *, unsigned int, tain_t const *, tain_t *) ;
+#define ftrigr_wait_and_g(a, list, len, deadline) ftrigr_wait_and(a, list, len, (deadline), &STAMP)
+extern int ftrigr_wait_or (ftrigr_t *, uint16 const *, unsigned int, tain_t const *, tain_t *, char *) ;
+#define ftrigr_wait_or_g(a, list, len, deadline, what) ftrigr_wait_or(a, list, len, deadline, &STAMP, what)
+
+#endif
diff --git a/src/include/s6/ftrigw.h b/src/include/s6/ftrigw.h
new file mode 100644
index 0000000..ccaf078
--- /dev/null
+++ b/src/include/s6/ftrigw.h
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#ifndef FTRIGW_H
+#define FTRIGW_H
+
+extern int ftrigw_fifodir_make (char const *, int, int) ;
+extern int ftrigw_notify (char const *, char) ;
+extern int ftrigw_notifyb (char const *, char const *, unsigned int) ;
+extern int ftrigw_clean (char const *) ;
+
+#endif
diff --git a/src/include/s6/s6-supervise.h b/src/include/s6/s6-supervise.h
new file mode 100644
index 0000000..2a39393
--- /dev/null
+++ b/src/include/s6/s6-supervise.h
@@ -0,0 +1,40 @@
+/* ISC license. */
+
+#ifndef S6_SUPERVISE_H
+#define S6_SUPERVISE_H
+
+#include <skalibs/tai.h>
+
+#define S6_SUPERVISE_CTLDIR "supervise"
+#define S6_SUPERVISE_EVENTDIR "event"
+#define S6_SVSCAN_CTLDIR ".s6-svscan"
+#define S6_SVSTATUS_FILENAME S6_SUPERVISE_CTLDIR "/status"
+#define S6_SVSTATUS_SIZE 18
+
+extern int s6_svc_write (char const *, char const *, unsigned int) ;
+extern int s6_svc_main (int, char const *const *, char const *, char const *, char const *) ;
+
+typedef struct s6_svstatus_s s6_svstatus_t, *s6_svstatus_t_ref ;
+struct s6_svstatus_s
+{
+ tain_t stamp ;
+ unsigned int pid ;
+ unsigned int flagwant : 1 ;
+ unsigned int flagwantup : 1 ;
+ unsigned int flagpaused : 1 ;
+ unsigned int flagfinishing : 1 ;
+} ;
+
+#define S6_SVSTATUS_ZERO { .stamp = TAIN_ZERO, .pid = 0, .flagwant = 0, .flagwantup = 0, .flagpaused = 0, .flagfinishing = 0 }
+
+
+extern void s6_svstatus_pack (char *, s6_svstatus_t const *) ;
+extern void s6_svstatus_unpack (char const *, s6_svstatus_t_ref) ;
+extern int s6_svstatus_read (char const *, s6_svstatus_t_ref) ;
+extern int s6_svstatus_write (char const *, s6_svstatus_t const *) ;
+
+/* These functions leak a fd, that's intended */
+extern int s6_supervise_lock (char const *) ;
+extern int s6_supervise_lock_mode (char const *, unsigned int, unsigned int) ;
+
+#endif
diff --git a/src/include/s6/s6.h b/src/include/s6/s6.h
new file mode 100644
index 0000000..84c552d
--- /dev/null
+++ b/src/include/s6/s6.h
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#ifndef S6_H
+#define S6_H
+
+#include <s6/s6-supervise.h>
+#include <s6/ftrigr.h>
+#include <s6/ftrigw.h>
+#include <s6/s6lock.h>
+
+#endif
diff --git a/src/include/s6/s6lock.h b/src/include/s6/s6lock.h
new file mode 100644
index 0000000..a44d80a
--- /dev/null
+++ b/src/include/s6/s6lock.h
@@ -0,0 +1,75 @@
+/* ISC license. */
+
+#ifndef S6LOCK_H
+#define S6LOCK_H
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6/config.h>
+
+
+ /* Constants */
+
+#define S6LOCKD_PROG S6_BINPREFIX "/s6lockd"
+#define S6LOCKD_HELPER_PROG S6_BINPREFIX "/s6lockd-helper"
+
+#define S6LOCK_BANNER1 "s6lock v1.0 (b)\n"
+#define S6LOCK_BANNER1_LEN (sizeof S6LOCK_BANNER1 - 1)
+#define S6LOCK_BANNER2 "s6lock v1.0 (a)\n"
+#define S6LOCK_BANNER2_LEN (sizeof S6LOCK_BANNER2 - 1)
+
+
+ /* The client handle */
+
+typedef struct s6lock_s s6lock_t, *s6lock_t_ref ;
+struct s6lock_s
+{
+ skaclient_t connection ;
+ genalloc list ; /* array of uint16 */
+ gensetdyn data ; /* set of char */
+ skaclient_buffer_t buffers ;
+} ;
+#define S6LOCK_ZERO { .connection = SKACLIENT_ZERO, .list = GENALLOC_ZERO, .data = GENSETDYN_INIT(int, 2, 0, 1) }
+extern s6lock_t const s6lock_zero ;
+
+
+ /* Starting and ending a session */
+
+extern int s6lock_start (s6lock_t *, char const *, tain_t const *, tain_t *) ;
+#define s6lock_start_g(a, ipcpath, deadline) s6lock_start(a, ipcpath, (deadline), &STAMP)
+extern int s6lock_startf (s6lock_t *, char const *, tain_t const *, tain_t *) ;
+#define s6lock_startf_g(a, lockdir, deadline) s6lock_startf(a, lockdir, (deadline), &STAMP)
+extern void s6lock_end (s6lock_t *) ;
+
+
+ /* Asynchronous primitives */
+
+#define s6lock_fd(a) skaclient_fd(&(a)->connection)
+extern int s6lock_update (s6lock_t *) ;
+extern int s6lock_check (s6lock_t *, uint16) ;
+
+
+ /* Synchronous functions */
+
+#define S6LOCK_OPTIONS_SH 0x0000U
+#define S6LOCK_OPTIONS_EX 0x0001U
+
+extern int s6lock_acquire (s6lock_t *, uint16 *, char const *, uint32, tain_t const *, tain_t const *, tain_t *) ;
+#define s6lock_acquire_g(a, id, path, options, limit, deadline) s6lock_acquire(a, id, path, options, limit, (deadline), &STAMP)
+#define s6lock_acquire_sh(a, id, path, limit, deadline, stamp) s6lock_aquire(a, id, path, S6LOCK_OPTIONS_SH, limit, deadline, stamp)
+#define s6lock_acquire_ex(a, id, path, limit, deadline, stamp) s6lock_aquire(a, id, path, S6LOCK_OPTIONS_EX, limit, deadline, stamp)
+#define s6lock_acquire_sh_g(a, id, path, limit, deadline) s6lock_acquire_sh(a, id, path, limit, (deadline), &STAMP)
+#define s6lock_acquire_ex_g(a, id, path, limit, deadline) s6lock_acquire_ex(a, id, path, limit, (deadline), &STAMP)
+extern int s6lock_release (s6lock_t *, uint16, tain_t const *, tain_t *) ;
+#define s6lock_release_g(a, id, deadline) s6lock_release(a, id, (deadline), &STAMP)
+
+extern int s6lock_wait_and (s6lock_t *, uint16 const *, unsigned int, tain_t const *, tain_t *) ;
+#define s6lock_wait_and_g(a, list, len, deadline) s6lock_wait_and(a, list, len, (deadline), &STAMP)
+extern int s6lock_wait_or (s6lock_t *, uint16 const *, unsigned int, tain_t const *, tain_t *) ;
+#define s6lock_wait_or_g(a, list, len, deadline) s6lock_wait_or(a, list, len, (deadline), &STAMP)
+
+#endif
diff --git a/src/libs6/deps-exe/s6-ftrigrd b/src/libs6/deps-exe/s6-ftrigrd
new file mode 100755
index 0000000..4b86d93
--- /dev/null
+++ b/src/libs6/deps-exe/s6-ftrigrd
@@ -0,0 +1,5 @@
+ftrig1_free.o
+ftrig1_make.o
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/libs6/deps-exe/s6lockd b/src/libs6/deps-exe/s6lockd
new file mode 100755
index 0000000..e027835
--- /dev/null
+++ b/src/libs6/deps-exe/s6lockd
@@ -0,0 +1,3 @@
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/libs6/deps-exe/s6lockd-helper b/src/libs6/deps-exe/s6lockd-helper
new file mode 100755
index 0000000..e7187fe
--- /dev/null
+++ b/src/libs6/deps-exe/s6lockd-helper
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/libs6/deps-lib/s6 b/src/libs6/deps-lib/s6
new file mode 100755
index 0000000..502694e
--- /dev/null
+++ b/src/libs6/deps-lib/s6
@@ -0,0 +1,33 @@
+ftrigr1_zero.o
+ftrigr_check.o
+ftrigr_end.o
+ftrigr_start.o
+ftrigr_startf.o
+ftrigr_subscribe.o
+ftrigr_unsubscribe.o
+ftrigr_update.o
+ftrigr_wait_and.o
+ftrigr_wait_or.o
+ftrigr_zero.o
+ftrigw_clean.o
+ftrigw_fifodir_make.o
+ftrigw_notify.o
+ftrigw_notifyb.o
+s6_supervise_lock.o
+s6_supervise_lock_mode.o
+s6_svc_main.o
+s6_svc_write.o
+s6_svstatus_pack.o
+s6_svstatus_read.o
+s6_svstatus_unpack.o
+s6_svstatus_write.o
+s6lock_acquire.o
+s6lock_check.o
+s6lock_end.o
+s6lock_release.o
+s6lock_start.o
+s6lock_startf.o
+s6lock_update.o
+s6lock_wait_and.o
+s6lock_wait_or.o
+s6lock_zero.o
diff --git a/src/libs6/ftrig1.h b/src/libs6/ftrig1.h
new file mode 100644
index 0000000..229de66
--- /dev/null
+++ b/src/libs6/ftrig1.h
@@ -0,0 +1,23 @@
+/* ISC license. */
+
+#ifndef FTRIG1_H
+#define FTRIG1_H
+
+#include <skalibs/stralloc.h>
+
+#define FTRIG1_PREFIX "ftrig1"
+#define FTRIG1_PREFIXLEN (sizeof FTRIG1_PREFIX - 1)
+
+typedef struct ftrig1_s ftrig1_t, *ftrig1_t_ref ;
+struct ftrig1_s
+{
+ int fd ;
+ int fdw ;
+ stralloc name ;
+} ;
+#define FTRIG1_ZERO { .fd = -1, .fdw = -1, .name = STRALLOC_ZERO }
+
+extern int ftrig1_make (ftrig1_t *, char const *) ;
+extern void ftrig1_free (ftrig1_t *) ;
+
+#endif
diff --git a/src/libs6/ftrig1_free.c b/src/libs6/ftrig1_free.c
new file mode 100644
index 0000000..091dc87
--- /dev/null
+++ b/src/libs6/ftrig1_free.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include "ftrig1.h"
+
+void ftrig1_free (ftrig1_t *p)
+{
+ if (p->name.s)
+ {
+ unlink(p->name.s) ;
+ stralloc_free(&p->name) ;
+ }
+ if (p->fd >= 0)
+ {
+ fd_close(p->fd) ;
+ p->fd = -1 ;
+ }
+ if (p->fdw >= 0)
+ {
+ fd_close(p->fdw) ;
+ p->fdw = -1 ;
+ }
+}
diff --git a/src/libs6/ftrig1_make.c b/src/libs6/ftrig1_make.c
new file mode 100644
index 0000000..7aedd08
--- /dev/null
+++ b/src/libs6/ftrig1_make.c
@@ -0,0 +1,65 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h> /* for rename() */
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/random.h>
+#include "ftrig1.h"
+
+int ftrig1_make (ftrig1_t *f, char const *path)
+{
+ ftrig1_t ff = FTRIG1_ZERO ;
+ unsigned int pathlen = str_len(path) ;
+ int e = 0 ;
+ char tmp[pathlen + 46 + FTRIG1_PREFIXLEN] ;
+
+ byte_copy(tmp, pathlen, path) ;
+ tmp[pathlen] = '/' ; tmp[pathlen+1] = '.' ;
+ byte_copy(tmp + pathlen + 2, FTRIG1_PREFIXLEN, FTRIG1_PREFIX) ;
+ tmp[pathlen + 2 + FTRIG1_PREFIXLEN] = ':' ;
+ if (!timestamp(tmp + pathlen + 3 + FTRIG1_PREFIXLEN)) return 0 ;
+ tmp[pathlen + 28 + FTRIG1_PREFIXLEN] = ':' ;
+ if (random_name(tmp + pathlen + 29 + FTRIG1_PREFIXLEN, 16) < 16) return 0 ;
+ tmp[pathlen + 45 + FTRIG1_PREFIXLEN] = 0 ;
+
+ {
+ mode_t m = umask(0) ;
+ if (mkfifo(tmp, S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH) == -1)
+ {
+ umask(m) ;
+ return 0 ;
+ }
+ umask(m) ;
+ }
+
+ if (!stralloc_catb(&ff.name, tmp, pathlen+1)) { e = errno ; goto err0 ; }
+ if (!stralloc_catb(&ff.name, tmp + pathlen + 2, FTRIG1_PREFIXLEN + 44))
+ {
+ e = errno ; goto err1 ;
+ }
+ ff.fd = open_read(tmp) ;
+ if (ff.fd == -1) { e = errno ; goto err1 ; }
+ ff.fdw = open_write(tmp) ;
+ if (ff.fdw == -1) { e = errno ; goto err2 ; }
+ if (rename(tmp, ff.name.s) == -1) goto err3 ;
+ *f = ff ;
+ return 1 ;
+
+ err3:
+ e = errno ;
+ fd_close(ff.fdw) ;
+ err2:
+ fd_close(ff.fd) ;
+ err1:
+ stralloc_free(&ff.name) ;
+ err0:
+ unlink(tmp) ;
+ errno = e ;
+ return 0 ;
+}
diff --git a/src/libs6/ftrigr1_zero.c b/src/libs6/ftrigr1_zero.c
new file mode 100644
index 0000000..967b4e0
--- /dev/null
+++ b/src/libs6/ftrigr1_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <s6/ftrigr.h>
+
+ftrigr1_t const ftrigr1_zero = FTRIGR1_ZERO ;
diff --git a/src/libs6/ftrigr_check.c b/src/libs6/ftrigr_check.c
new file mode 100644
index 0000000..147deca
--- /dev/null
+++ b/src/libs6/ftrigr_check.c
@@ -0,0 +1,40 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/gensetdyn.h>
+#include <s6/ftrigr.h>
+
+int ftrigr_check (ftrigr_t *a, uint16 id, char *c)
+{
+ ftrigr1_t *p ;
+ if (!id--) return (errno = EINVAL, -1) ;
+ p = GENSETDYN_P(ftrigr1_t, &a->data, id) ;
+ if (!p) return (errno = EINVAL, -1) ;
+ switch (p->state)
+ {
+ case FR1STATE_WAITACKDATA :
+ {
+ *c = p->what ;
+ *p = ftrigr1_zero ;
+ gensetdyn_delete(&a->data, id) ;
+ return 1 ;
+ }
+ case FR1STATE_LISTENING :
+ {
+ register unsigned int r = p->count ;
+ if (r) *c = p->what ;
+ p->count = 0 ;
+ return (int)r ;
+ }
+ case FR1STATE_WAITACK :
+ {
+ errno = p->what ;
+ *p = ftrigr1_zero ;
+ gensetdyn_delete(&a->data, id) ;
+ return -1 ;
+ }
+ default: return (errno = EINVAL, -1) ;
+ }
+ return 0 ;
+}
diff --git a/src/libs6/ftrigr_end.c b/src/libs6/ftrigr_end.c
new file mode 100644
index 0000000..f35f06c
--- /dev/null
+++ b/src/libs6/ftrigr_end.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6/ftrigr.h>
+
+void ftrigr_end (ftrigr_ref a)
+{
+ gensetdyn_free(&a->data) ;
+ genalloc_free(uint16, &a->list) ;
+ skaclient_end(&a->connection) ;
+ *a = ftrigr_zero ;
+}
diff --git a/src/libs6/ftrigr_start.c b/src/libs6/ftrigr_start.c
new file mode 100644
index 0000000..baf9ce5
--- /dev/null
+++ b/src/libs6/ftrigr_start.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <s6/ftrigr.h>
+
+int ftrigr_start (ftrigr_t *a, char const *path, tain_t const *deadline, tain_t *stamp)
+{
+ return skaclient_start_b(&a->connection, &a->buffers, path, FTRIGR_BANNER1, FTRIGR_BANNER1_LEN, FTRIGR_BANNER2, FTRIGR_BANNER2_LEN, deadline, stamp) ;
+}
diff --git a/src/libs6/ftrigr_startf.c b/src/libs6/ftrigr_startf.c
new file mode 100644
index 0000000..28c81aa
--- /dev/null
+++ b/src/libs6/ftrigr_startf.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <s6/ftrigr.h>
+
+int ftrigr_startf (ftrigr_ref a, tain_t const *deadline, tain_t *stamp)
+{
+ char const *cargv[2] = { FTRIGRD_PROG, 0 } ;
+ char const *cenvp[1] = { 0 } ;
+ return skaclient_startf_b(&a->connection, &a->buffers, cargv[0], cargv, cenvp, SKACLIENT_OPTION_WAITPID, FTRIGR_BANNER1, FTRIGR_BANNER1_LEN, FTRIGR_BANNER2, FTRIGR_BANNER2_LEN, deadline, stamp) ;
+}
diff --git a/src/libs6/ftrigr_subscribe.c b/src/libs6/ftrigr_subscribe.c
new file mode 100644
index 0000000..d645931
--- /dev/null
+++ b/src/libs6/ftrigr_subscribe.c
@@ -0,0 +1,43 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/siovec.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6/ftrigr.h>
+
+uint16 ftrigr_subscribe (ftrigr_t *a, char const *path, char const *re, uint32 options, tain_t const *deadline, tain_t *stamp)
+{
+ unsigned int pathlen = str_len(path) ;
+ unsigned int relen = str_len(re) ;
+ unsigned int i ;
+ char err ;
+ char tmp[15] = "--L" ;
+ siovec_t v[3] = { { .s = tmp, .len = 15 }, { .s = (char *)path, .len = pathlen + 1 }, { .s = (char *)re, .len = relen + 1 } } ;
+ if (!gensetdyn_new(&a->data, &i)) return 0 ;
+ uint16_pack_big(tmp, (uint16)i) ;
+ uint32_pack_big(tmp+3, options) ;
+ uint32_pack_big(tmp+7, (uint32)pathlen) ;
+ uint32_pack_big(tmp+11, (uint32)relen) ;
+ if (!skaclient_sendv(&a->connection, v, 3, &skaclient_default_cb, &err, deadline, stamp))
+ {
+ gensetdyn_delete(&a->data, i) ;
+ return 0 ;
+ }
+ if (err)
+ {
+ gensetdyn_delete(&a->data, i) ;
+ return (errno = err, 0) ;
+ }
+ {
+ register ftrigr1_t *p = GENSETDYN_P(ftrigr1_t, &a->data, i) ;
+ p->options = options ;
+ p->state = FR1STATE_LISTENING ;
+ p->count = 0 ;
+ p->what = 0 ;
+ }
+ return (uint16)(i+1) ;
+}
diff --git a/src/libs6/ftrigr_unsubscribe.c b/src/libs6/ftrigr_unsubscribe.c
new file mode 100644
index 0000000..4833571
--- /dev/null
+++ b/src/libs6/ftrigr_unsubscribe.c
@@ -0,0 +1,36 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6/ftrigr.h>
+
+int ftrigr_unsubscribe (ftrigr_ref a, uint16 i, tain_t const *deadline, tain_t *stamp)
+{
+ ftrigr1_t *p ;
+ if (!i--) return (errno = EINVAL, 0) ;
+ p = GENSETDYN_P(ftrigr1_t, &a->data, i) ;
+ if (!p) return (errno = EINVAL, 0) ;
+ switch (p->state)
+ {
+ case FR1STATE_WAITACK :
+ case FR1STATE_WAITACKDATA :
+ {
+ char dummy ;
+ ftrigr_check(a, i+1, &dummy) ;
+ return 1 ;
+ }
+ default : break ;
+ }
+ {
+ char err ;
+ char pack[3] = "--U" ;
+ uint16_pack_big(pack, i) ;
+ if (!skaclient_send(&a->connection, pack, 3, &skaclient_default_cb, &err, deadline, stamp)) return 0 ;
+ if (err) return (errno = err, 0) ;
+ }
+ *p = ftrigr1_zero ;
+ return gensetdyn_delete(&a->data, i) ;
+}
diff --git a/src/libs6/ftrigr_update.c b/src/libs6/ftrigr_update.c
new file mode 100644
index 0000000..ad69714
--- /dev/null
+++ b/src/libs6/ftrigr_update.c
@@ -0,0 +1,43 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <s6/ftrigr.h>
+
+static int msghandler (unixmessage_t const *m, void *context)
+{
+ ftrigr_t *a = (ftrigr_t *)context ;
+ ftrigr1_t *p ;
+ uint16 id ;
+ if (m->len != 4 || m->nfds) return (errno = EPROTO, 0) ;
+ uint16_unpack_big(m->s, &id) ;
+ p = GENSETDYN_P(ftrigr1_t, &a->data, id) ;
+ if (!p) return 1 ;
+ if (p->state != FR1STATE_LISTENING) return (errno = EINVAL, 0) ;
+ if (!genalloc_readyplus(uint16, &a->list, 1)) return 0 ;
+ switch (m->s[2])
+ {
+ case 'd' :
+ p->state = FR1STATE_WAITACK ;
+ break ;
+ case '!' :
+ if (p->options & FTRIGR_REPEAT) p->count++ ;
+ else p->state = FR1STATE_WAITACKDATA ;
+ break ;
+ default : return (errno = EPROTO, 0) ;
+ }
+ p->what = m->s[3] ;
+ id++ ; genalloc_append(uint16, &a->list, &id) ;
+ return 1 ;
+}
+
+int ftrigr_update (ftrigr_t *a)
+{
+ genalloc_setlen(uint16, &a->list, 0) ;
+ return skaclient_update(&a->connection, &msghandler, a) ;
+}
diff --git a/src/libs6/ftrigr_wait_and.c b/src/libs6/ftrigr_wait_and.c
new file mode 100644
index 0000000..f854a8d
--- /dev/null
+++ b/src/libs6/ftrigr_wait_and.c
@@ -0,0 +1,28 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <s6/ftrigr.h>
+
+int ftrigr_wait_and (ftrigr_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline, tain_t *stamp)
+{
+ iopause_fd x = { -1, IOPAUSE_READ, 0 } ;
+ x.fd = ftrigr_fd(a) ;
+ for (; n ; n--, idlist++)
+ {
+ for (;;)
+ {
+ char dummy ;
+ register int r = ftrigr_check(a, *idlist, &dummy) ;
+ if (r < 0) return r ;
+ else if (r) break ;
+ r = iopause_stamp(&x, 1, deadline, stamp) ;
+ if (r < 0) return r ;
+ else if (!r) return (errno = ETIMEDOUT, -1) ;
+ else if (ftrigr_update(a) < 0) return -1 ;
+ }
+ }
+ return 1 ;
+}
diff --git a/src/libs6/ftrigr_wait_or.c b/src/libs6/ftrigr_wait_or.c
new file mode 100644
index 0000000..8a01d85
--- /dev/null
+++ b/src/libs6/ftrigr_wait_or.c
@@ -0,0 +1,31 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <s6/ftrigr.h>
+
+int ftrigr_wait_or (ftrigr_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline, tain_t *stamp, char *c)
+{
+ iopause_fd x = { -1, IOPAUSE_READ | IOPAUSE_EXCEPT, 0 } ;
+ x.fd = ftrigr_fd(a) ;
+ if (x.fd < 0) return -1 ;
+ for (;;)
+ {
+ register unsigned int i = 0 ;
+ register int r ;
+ for (; i < n ; i++)
+ {
+ r = ftrigr_check(a, idlist[i], c) ;
+ if (r < 0) return r ;
+ else if (r) return i ;
+ }
+ r = iopause_stamp(&x, 1, deadline, stamp) ;
+ if (r < 0) return 0 ;
+ else if (!r) return (errno = ETIMEDOUT, -1) ;
+ else if (ftrigr_update(a) < 0) return -1 ;
+ }
+ return (errno = EPROTO, -1) ; /* can't happen */
+}
diff --git a/src/libs6/ftrigr_zero.c b/src/libs6/ftrigr_zero.c
new file mode 100644
index 0000000..b09ddb6
--- /dev/null
+++ b/src/libs6/ftrigr_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <s6/ftrigr.h>
+
+ftrigr_t const ftrigr_zero = FTRIGR_ZERO ;
diff --git a/src/libs6/ftrigw_clean.c b/src/libs6/ftrigw_clean.c
new file mode 100644
index 0000000..1198828
--- /dev/null
+++ b/src/libs6/ftrigw_clean.c
@@ -0,0 +1,39 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <skalibs/direntry.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/djbunix.h>
+#include "ftrig1.h"
+#include <s6/ftrigw.h>
+
+int ftrigw_clean (char const *path)
+{
+ unsigned int pathlen = str_len(path) ;
+ int e = 0 ;
+ DIR *dir = opendir(path) ;
+ if (!dir) return 0 ;
+ {
+ char tmp[pathlen + FTRIG1_PREFIXLEN + 45] ;
+ byte_copy(tmp, pathlen, path) ;
+ tmp[pathlen] = '/' ; tmp[pathlen + FTRIG1_PREFIXLEN + 44] = 0 ;
+ for (;;)
+ {
+ direntry *d ;
+ int fd ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (str_diffn(d->d_name, FTRIG1_PREFIX, FTRIG1_PREFIXLEN)) continue ;
+ if (str_len(d->d_name) != FTRIG1_PREFIXLEN + 43) continue ;
+ byte_copy(tmp + pathlen + 1, FTRIG1_PREFIXLEN + 43, d->d_name) ;
+ fd = open_write(tmp) ;
+ if (fd >= 0) fd_close(fd) ;
+ else if ((errno == ENXIO) && (unlink(tmp) < 0)) e = errno ;
+ }
+ }
+ if (errno) e = errno ;
+ dir_close(dir) ;
+ return e ? (errno = e, 0) : 1 ;
+}
diff --git a/src/libs6/ftrigw_fifodir_make.c b/src/libs6/ftrigw_fifodir_make.c
new file mode 100644
index 0000000..1a69a8e
--- /dev/null
+++ b/src/libs6/ftrigw_fifodir_make.c
@@ -0,0 +1,26 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <s6/ftrigw.h>
+
+int ftrigw_fifodir_make (char const *path, int gid, int force)
+{
+ mode_t m = umask(0) ;
+ if (mkdir(path, 0700) == -1)
+ {
+ struct stat st ;
+ umask(m) ;
+ if (errno != EEXIST) return 0 ;
+ if (stat(path, &st) == -1) return 0 ;
+ if (st.st_uid != getuid()) return (errno = EACCES, 0) ;
+ if (!S_ISDIR(st.st_mode)) return (errno = ENOTDIR, 0) ;
+ if (!force) return 1 ;
+ }
+ else umask(m) ;
+ if ((gid >= 0) && (chown(path, -1, gid) == -1)) return 0 ;
+ if (chmod(path, (gid >= 0) ? 03730 : 01733) == -1) return 0 ;
+ return 1 ;
+}
diff --git a/src/libs6/ftrigw_notify.c b/src/libs6/ftrigw_notify.c
new file mode 100644
index 0000000..230dad6
--- /dev/null
+++ b/src/libs6/ftrigw_notify.c
@@ -0,0 +1,8 @@
+/* ISC license. */
+
+#include <s6/ftrigw.h>
+
+int ftrigw_notify (char const *path, char c)
+{
+ return ftrigw_notifyb(path, &c, 1) ;
+}
diff --git a/src/libs6/ftrigw_notifyb.c b/src/libs6/ftrigw_notifyb.c
new file mode 100644
index 0000000..345a3cc
--- /dev/null
+++ b/src/libs6/ftrigw_notifyb.c
@@ -0,0 +1,67 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <skalibs/direntry.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sig.h>
+#include <skalibs/djbunix.h>
+#include "ftrig1.h"
+#include <s6/ftrigw.h>
+
+int ftrigw_notifyb (char const *path, char const *s, unsigned int len)
+{
+ unsigned int i = 0 ;
+ struct skasigaction old ;
+ DIR *dir = opendir(path) ;
+ if (!dir) return -1 ;
+ if (skasigaction(SIGPIPE, &SKASIG_IGN, &old) < 0) return -1 ;
+ {
+ unsigned int pathlen = str_len(path) ;
+ char tmp[pathlen + FTRIG1_PREFIXLEN + 45] ;
+ byte_copy(tmp, pathlen, path) ;
+ tmp[pathlen] = '/' ; tmp[pathlen + FTRIG1_PREFIXLEN + 44] = 0 ;
+ for (;;)
+ {
+ direntry *d ;
+ int fd ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (str_diffn(d->d_name, FTRIG1_PREFIX, FTRIG1_PREFIXLEN)) continue ;
+ if (str_len(d->d_name) != FTRIG1_PREFIXLEN + 43) continue ;
+ byte_copy(tmp + pathlen + 1, FTRIG1_PREFIXLEN + 43, d->d_name) ;
+ fd = open_write(tmp) ;
+ if (fd == -1)
+ {
+ if (errno == ENXIO) unlink(tmp) ;
+ }
+ else
+ {
+ register int r = fd_write(fd, s, len) ;
+ if ((r < 0) || (unsigned int)r < len)
+ {
+ if (errno == EPIPE) unlink(tmp) ;
+ /* what to do if EGAIN ? full fifo -> fix the reader !
+ There's a race condition in extreme cases though ;
+ but it's still better to be nonblocking - the writer
+ shouldn't get in trouble because of a bad reader. */
+ fd_close(fd) ;
+ }
+ else
+ {
+ fd_close(fd) ;
+ i++ ;
+ }
+ }
+ }
+ }
+ {
+ int e = errno ;
+ skasigaction(SIGPIPE, &old, 0) ;
+ dir_close(dir) ;
+ return e ? (errno = e, -1) : (int)i ;
+ }
+}
diff --git a/src/libs6/s6-ftrigrd.c b/src/libs6/s6-ftrigrd.c
new file mode 100644
index 0000000..b766f36
--- /dev/null
+++ b/src/libs6/s6-ftrigrd.c
@@ -0,0 +1,266 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <signal.h>
+#include <regex.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/error.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/buffer.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/bufalloc.h>
+#include <skalibs/sig.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/iopause.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include "ftrig1.h"
+#include <s6/ftrigr.h>
+
+#define FTRIGRD_MAXREADS 32
+#define FTRIGRD_BUFSIZE 16
+
+#define dienomem() strerr_diefu1sys(111, "stralloc_catb")
+
+typedef struct ftrigio_s ftrigio_t, *ftrigio_t_ref ;
+struct ftrigio_s
+{
+ unsigned int xindex ;
+ ftrig1_t trig ;
+ buffer b ;
+ char buf[FTRIGRD_BUFSIZE] ;
+ regex_t re ;
+ stralloc sa ;
+ uint32 options ;
+ uint16 id ; /* given by client */
+} ;
+#define FTRIGIO_ZERO { .xindex = 0, .trig = FTRIG1_ZERO, .b = BUFFER_INIT(0, -1, 0, 0), .buf = "", .sa = STRALLOC_ZERO, .options = 0, .id = 0 }
+static ftrigio_t const fzero = FTRIGIO_ZERO ;
+
+static genalloc a = GENALLOC_ZERO ; /* array of ftrigio_t */
+
+static void ftrigio_deepfree (ftrigio_t_ref p)
+{
+ ftrig1_free(&p->trig) ;
+ stralloc_free(&p->sa) ;
+ regfree(&p->re) ;
+ *p = fzero ;
+}
+
+static void cleanup (void)
+{
+ register unsigned int i = genalloc_len(ftrigio_t, &a) ;
+ for (; i ; i--) ftrigio_deepfree(genalloc_s(ftrigio_t, &a) + i - 1) ;
+ genalloc_setlen(ftrigio_t, &a, 0) ;
+}
+
+static void trig (uint16 id, char what, char info)
+{
+ char pack[4] ;
+ unixmessage_t m = { .s = pack, .len = 4, .fds = 0, .nfds = 0 } ;
+ uint16_pack_big(pack, id) ;
+ pack[2] = what ; pack[3] = info ;
+ if (!unixmessage_put(unixmessage_sender_x, &m))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "build answer") ;
+ }
+}
+
+static void answer (char c)
+{
+ unixmessage_t m = { &c, 1, 0, 0 } ;
+ if (!unixmessage_put(unixmessage_sender_1, &m))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "unixmessage_put") ;
+ }
+}
+
+static void remove (unsigned int i)
+{
+ register unsigned int n = genalloc_len(ftrigio_t, &a) - 1 ;
+ ftrigio_deepfree(genalloc_s(ftrigio_t, &a) + i) ;
+ genalloc_s(ftrigio_t, &a)[i] = genalloc_s(ftrigio_t, &a)[n] ;
+ genalloc_setlen(ftrigio_t, &a, n) ;
+}
+
+static inline int ftrigio_read (ftrigio_t *p)
+{
+ unsigned int n = FTRIGRD_MAXREADS ;
+ while (n--)
+ {
+ regmatch_t pmatch ;
+ unsigned int blen ;
+ register int r = sanitize_read(buffer_fill(&p->b)) ;
+ if (!r) break ;
+ if (r < 0) return (trig(p->id, 'd', errno), 0) ;
+ blen = buffer_len(&p->b) ;
+ if (!stralloc_readyplus(&p->sa, blen+1)) dienomem() ;
+ buffer_getnofill(&p->b, p->sa.s + p->sa.len, blen) ;
+ p->sa.len += blen ;
+ p->sa.s[p->sa.len] = 0 ;
+ while (!regexec(&p->re, p->sa.s, 1, &pmatch, REG_NOTBOL | REG_NOTEOL))
+ {
+ trig(p->id, '!', p->sa.s[pmatch.rm_eo - 1]) ;
+ if (!(p->options & FTRIGR_REPEAT)) return 0 ;
+ byte_copy(p->sa.s, p->sa.len + 1 - pmatch.rm_eo, p->sa.s + pmatch.rm_eo) ;
+ p->sa.len -= pmatch.rm_eo ;
+ }
+ }
+ return 1 ;
+}
+
+static int parse_protocol (unixmessage_t const *m, void *context)
+{
+ uint16 id ;
+ if (m->len < 3 || m->nfds)
+ {
+ cleanup() ;
+ strerr_dief1x(100, "invalid client request") ;
+ }
+ uint16_unpack_big(m->s, &id) ;
+ switch (m->s[2])
+ {
+ case 'U' : /* unsubscribe */
+ {
+ register unsigned int i = genalloc_len(ftrigio_t, &a) ;
+ for (; i ; i--) if (genalloc_s(ftrigio_t, &a)[i-1].id == id) break ;
+ if (i) remove(i-1) ;
+ answer(0) ;
+ break ;
+ }
+ case 'L' : /* subscribe to path and match re */
+ {
+ ftrigio_t f = FTRIGIO_ZERO ;
+ uint32 pathlen, relen ;
+ int r ;
+ if (m->len < 18)
+ {
+ answer(EPROTO) ;
+ break ;
+ }
+ uint32_unpack_big(m->s + 3, &f.options) ;
+ uint32_unpack_big(m->s + 7, &pathlen) ;
+ uint32_unpack_big(m->s + 11, &relen) ;
+ if (((pathlen + relen + 17) != m->len) || m->s[15 + pathlen] || m->s[m->len - 1])
+ {
+ answer(EPROTO) ;
+ break ;
+ }
+ f.id = id ;
+ r = regcomp(&f.re, m->s + 16 + pathlen, REG_EXTENDED) ;
+ if (r)
+ {
+ answer(r == REG_ESPACE ? ENOMEM : EINVAL) ;
+ break ;
+ }
+ if (!ftrig1_make(&f.trig, m->s + 15))
+ {
+ regfree(&f.re) ;
+ answer(errno) ;
+ break ;
+ }
+ if (!genalloc_append(ftrigio_t, &a, &f))
+ {
+ ftrigio_deepfree(&f) ;
+ answer(errno) ;
+ break ;
+ }
+ answer(0) ;
+ break ;
+ }
+ default :
+ {
+ cleanup() ;
+ strerr_dief1x(100, "invalid client request") ;
+ }
+ }
+ (void)context ;
+ return 1 ;
+}
+
+int main (void)
+{
+ PROG = "s6-ftrigrd" ;
+
+ if (ndelay_on(0) < 0) strerr_diefu2sys(111, "ndelay_on ", "0") ;
+ if (ndelay_on(1) < 0) strerr_diefu2sys(111, "ndelay_on ", "1") ;
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ;
+
+ {
+ tain_t deadline ;
+ tain_now_g() ;
+ tain_addsec_g(&deadline, 2) ;
+ if (!skaclient_server_01x_init_g(FTRIGR_BANNER1, FTRIGR_BANNER1_LEN, FTRIGR_BANNER2, FTRIGR_BANNER2_LEN, &deadline))
+ strerr_diefu1sys(111, "sync with client") ;
+ }
+
+ for (;;)
+ {
+ register unsigned int n = genalloc_len(ftrigio_t, &a) ;
+ iopause_fd x[3 + n] ;
+ unsigned int i = 0 ;
+
+ x[0].fd = 0 ; x[0].events = IOPAUSE_EXCEPT | IOPAUSE_READ ;
+ x[1].fd = 1 ; x[1].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_1) ? 0 : IOPAUSE_WRITE) ;
+ x[2].fd = unixmessage_sender_fd(unixmessage_sender_x) ;
+ x[2].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_x) ? 0 : IOPAUSE_WRITE) ;
+ for (; i < n ; i++)
+ {
+ register ftrigio_t_ref p = genalloc_s(ftrigio_t, &a) + i ;
+ p->xindex = 3 + i ;
+ x[3+i].fd = p->trig.fd ;
+ x[3+i].events = IOPAUSE_READ ;
+ }
+
+ if (iopause(x, 3 + n, 0, 0) < 0)
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "iopause") ;
+ }
+
+ /* client closed */
+ if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) break ;
+
+ /* client is reading */
+ if (x[1].revents & IOPAUSE_WRITE)
+ if ((unixmessage_sender_flush(unixmessage_sender_1) < 0) && !error_isagain(errno))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "flush stdout") ;
+ }
+ if (x[2].revents & IOPAUSE_WRITE)
+ if ((!unixmessage_sender_flush(unixmessage_sender_x) < 0) && !error_isagain(errno))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "flush asyncout") ;
+ }
+
+ /* scan listening ftrigs */
+ for (i = 0 ; i < genalloc_len(ftrigio_t, &a) ; i++)
+ {
+ register ftrigio_t_ref p = genalloc_s(ftrigio_t, &a) + i ;
+ if (x[p->xindex].revents & IOPAUSE_READ)
+ if (!ftrigio_read(p)) remove(i--) ;
+ }
+
+ /* client is writing */
+ if (!unixmessage_receiver_isempty(unixmessage_receiver_0) || x[0].revents & IOPAUSE_READ)
+ {
+ if (unixmessage_handle(unixmessage_receiver_0, &parse_protocol, 0) < 0)
+ {
+ if (errno == EPIPE) break ; /* normal exit */
+ cleanup() ;
+ strerr_diefu1sys(111, "handle messages from client") ;
+ }
+ }
+ }
+ cleanup() ;
+ return 0 ;
+}
diff --git a/src/libs6/s6_supervise_lock.c b/src/libs6/s6_supervise_lock.c
new file mode 100644
index 0000000..5c9ca30
--- /dev/null
+++ b/src/libs6/s6_supervise_lock.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <s6/s6-supervise.h>
+
+int s6_supervise_lock (char const *subdir)
+{
+ return s6_supervise_lock_mode(subdir, S_IRWXU, S_IRUSR | S_IWUSR) ;
+}
diff --git a/src/libs6/s6_supervise_lock_mode.c b/src/libs6/s6_supervise_lock_mode.c
new file mode 100644
index 0000000..ff3e11e
--- /dev/null
+++ b/src/libs6/s6_supervise_lock_mode.c
@@ -0,0 +1,54 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <s6/s6-supervise.h>
+
+int s6_supervise_lock_mode (char const *subdir, unsigned int subdirmode, unsigned int controlmode)
+{
+ unsigned int subdirlen = str_len(subdir) ;
+ int fdctl, fdctlw, fdlock ;
+ char control[subdirlen + 9] ;
+ char lock[subdirlen + 6] ;
+ byte_copy(control, subdirlen, subdir) ;
+ byte_copy(control + subdirlen, 9, "/control") ;
+ byte_copy(lock, subdirlen, subdir) ;
+ byte_copy(lock + subdirlen, 6, "/lock") ;
+ if ((mkdir(subdir, (mode_t)subdirmode) == -1) && (errno != EEXIST))
+ strerr_diefu2sys(111, "mkdir ", subdir) ;
+ if (mkfifo(control, controlmode) < 0)
+ {
+ struct stat st ;
+ if (errno != EEXIST)
+ strerr_diefu2sys(111, "mkfifo ", control) ;
+ if (stat(control, &st) < 0)
+ strerr_diefu2sys(111, "stat ", control) ;
+ if (!S_ISFIFO(st.st_mode))
+ strerr_diefu2x(100, control, " is not a FIFO") ;
+ }
+ fdlock = open_create(lock) ;
+ if (fdlock < 0)
+ strerr_diefu2sys(111, "open_create ", lock) ;
+ if (lock_ex(fdlock) < 0)
+ strerr_diefu2sys(111, "lock ", lock) ;
+ fdctlw = open_write(control) ;
+ if (fdctlw >= 0) strerr_dief1x(100, "directory already locked") ;
+ if (errno != ENXIO)
+ strerr_diefu2sys(111, "open_write ", control) ;
+ fdctl = open_read(control) ;
+ if (fdctl < 0)
+ strerr_diefu2sys(111, "open_read ", control) ;
+ fdctlw = open_write(control) ;
+ if (fdctlw < 0)
+ strerr_diefu2sys(111, "open_write ", control) ;
+ fd_close(fdlock) ;
+ if ((coe(fdctlw) < 0) || (coe(fdctl) < 0))
+ strerr_diefu2sys(111, "coe ", control) ;
+
+ return fdctl ;
+ /* fdctlw is leaking. That's okay, it's coe. */
+}
diff --git a/src/libs6/s6_svc_main.c b/src/libs6/s6_svc_main.c
new file mode 100644
index 0000000..5d14904
--- /dev/null
+++ b/src/libs6/s6_svc_main.c
@@ -0,0 +1,40 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/skamisc.h>
+#include <s6/s6-supervise.h>
+
+#define DATASIZE 256
+
+int s6_svc_main (int argc, char const *const *argv, char const *optstring, char const *usage, char const *controldir)
+{
+ char data[DATASIZE] ;
+ unsigned int datalen = 0 ;
+ register int r ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, optstring) ;
+ if (opt == -1) break ;
+ if (opt == '?') strerr_dieusage(100, usage) ;
+ if (datalen >= DATASIZE) strerr_dief1x(100, "too many commands") ;
+ data[datalen++] = opt ;
+ }
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ if (!argc) strerr_dieusage(100, usage) ;
+
+ {
+ unsigned int arglen = str_len(*argv) ;
+ unsigned int cdirlen = str_len(controldir) ;
+ char tmp[arglen + cdirlen + 10] ;
+ byte_copy(tmp, arglen, *argv) ;
+ tmp[arglen] = '/' ;
+ byte_copy(tmp + arglen + 1, cdirlen, controldir) ;
+ byte_copy(tmp + arglen + 1 + cdirlen, 9, "/control") ;
+ r = s6_svc_write(tmp, data, datalen) ;
+ }
+ if (r < 0) strerr_diefu2sys(111, "control ", *argv) ;
+ else if (!r) strerr_diefu3x(100, "control ", *argv, ": supervisor not listening") ;
+ return 0 ;
+}
diff --git a/src/libs6/s6_svc_write.c b/src/libs6/s6_svc_write.c
new file mode 100644
index 0000000..ea9eee5
--- /dev/null
+++ b/src/libs6/s6_svc_write.c
@@ -0,0 +1,22 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/djbunix.h>
+#include <s6/s6-supervise.h>
+
+int s6_svc_write (char const *fifo, char const *data, unsigned int datalen)
+{
+ int fd = open_write(fifo) ;
+ if (fd < 0) return (errno == ENXIO) ? 0 : -1 ;
+ else if (ndelay_off(fd) == -1) return -1 ;
+ else if (fd_write(fd, data, datalen) == -1)
+ {
+ register int e = errno ;
+ fd_close(fd) ;
+ errno = e ;
+ return -1 ;
+ }
+ fd_close(fd) ;
+ return 1 ;
+}
diff --git a/src/libs6/s6_svstatus_pack.c b/src/libs6/s6_svstatus_pack.c
new file mode 100644
index 0000000..2d5baf6
--- /dev/null
+++ b/src/libs6/s6_svstatus_pack.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <skalibs/uint32.h>
+#include <skalibs/tai.h>
+#include <s6/s6-supervise.h>
+
+void s6_svstatus_pack (char *pack, s6_svstatus_t const *sv)
+{
+ tain_pack(pack, &sv->stamp) ;
+ uint32_pack(pack + 12, (uint32)sv->pid) ;
+ pack[16] = sv->flagpaused | (sv->flagfinishing << 1) ;
+ pack[17] = sv->flagwant ? sv->flagwantup ? 'u' : 'd' : 0 ;
+}
diff --git a/src/libs6/s6_svstatus_read.c b/src/libs6/s6_svstatus_read.c
new file mode 100644
index 0000000..32ec660
--- /dev/null
+++ b/src/libs6/s6_svstatus_read.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/djbunix.h>
+#include <s6/s6-supervise.h>
+
+int s6_svstatus_read (char const *dir, s6_svstatus_t_ref status)
+{
+ unsigned int n = str_len(dir) ;
+ char pack[S6_SVSTATUS_SIZE] ;
+ char tmp[n + 2 + sizeof(S6_SVSTATUS_FILENAME)] ;
+ byte_copy(tmp, n, dir) ;
+ byte_copy(tmp + n, 2 + sizeof(S6_SVSTATUS_FILENAME), "/" S6_SVSTATUS_FILENAME) ;
+ if (openreadnclose(tmp, pack, S6_SVSTATUS_SIZE) < S6_SVSTATUS_SIZE) return 0 ;
+ s6_svstatus_unpack(pack, status) ;
+ return 1 ;
+}
diff --git a/src/libs6/s6_svstatus_unpack.c b/src/libs6/s6_svstatus_unpack.c
new file mode 100644
index 0000000..cce6989
--- /dev/null
+++ b/src/libs6/s6_svstatus_unpack.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <skalibs/uint32.h>
+#include <skalibs/tai.h>
+#include <s6/s6-supervise.h>
+
+void s6_svstatus_unpack (char const *pack, s6_svstatus_t_ref sv)
+{
+ uint32 pid ;
+ tain_unpack(pack, &sv->stamp) ;
+ uint32_unpack(pack + 12, &pid) ;
+ sv->pid = (int)pid ;
+ sv->flagpaused = pack[16] & 1 ;
+ sv->flagfinishing = (pack[16] >> 1) & 1 ;
+ switch (pack[17])
+ {
+ case 'u' :
+ sv->flagwant = 1 ;
+ sv->flagwantup = 1 ;
+ break ;
+ case 'd' :
+ sv->flagwant = 1 ;
+ sv->flagwantup = 0 ;
+ break ;
+ default :
+ sv->flagwant = 0 ;
+ sv->flagwantup = 0 ;
+ }
+}
diff --git a/src/libs6/s6_svstatus_write.c b/src/libs6/s6_svstatus_write.c
new file mode 100644
index 0000000..2cc8a7b
--- /dev/null
+++ b/src/libs6/s6_svstatus_write.c
@@ -0,0 +1,16 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/djbunix.h>
+#include <s6/s6-supervise.h>
+
+int s6_svstatus_write (char const *dir, s6_svstatus_t const *status)
+{
+ unsigned int n = str_len(dir) ;
+ char pack[S6_SVSTATUS_SIZE] ;
+ char tmp[n + 2 + sizeof(S6_SVSTATUS_FILENAME)] ;
+ byte_copy(tmp, n, dir) ;
+ byte_copy(tmp + n, 2 + sizeof(S6_SVSTATUS_FILENAME), "/" S6_SVSTATUS_FILENAME) ;
+ s6_svstatus_pack(pack, status) ;
+ return openwritenclose_suffix(tmp, pack, S6_SVSTATUS_SIZE, ".new") ;
+}
diff --git a/src/libs6/s6lock_acquire.c b/src/libs6/s6lock_acquire.c
new file mode 100644
index 0000000..b0fef54
--- /dev/null
+++ b/src/libs6/s6lock_acquire.c
@@ -0,0 +1,38 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/siovec.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6/s6lock.h>
+
+int s6lock_acquire (s6lock_t *a, uint16 *u, char const *path, uint32 options, tain_t const *limit, tain_t const *deadline, tain_t *stamp)
+{
+ unsigned int pathlen = str_len(path) ;
+ char err ;
+ char tmp[23] = "--<" ;
+ siovec_t v[2] = { { .s = tmp, .len = 23 }, { .s = (char *)path, .len = pathlen + 1 } } ;
+ unsigned int i ;
+ if (!gensetdyn_new(&a->data, &i)) return 0 ;
+ uint16_pack_big(tmp, (uint16)i) ;
+ uint32_pack_big(tmp+3, options) ;
+ tain_pack(tmp+7, limit) ;
+ uint32_pack_big(tmp+19, (uint32)pathlen) ;
+ if (!skaclient_sendv(&a->connection, v, 2, &skaclient_default_cb, &err, deadline, stamp))
+ {
+ gensetdyn_delete(&a->data, i) ;
+ return 0 ;
+ }
+ if (err)
+ {
+ gensetdyn_delete(&a->data, i) ;
+ return (errno = err, 0) ;
+ }
+ *GENSETDYN_P(char, &a->data, i) = EAGAIN ;
+ *u = i ;
+ return 1 ;
+}
diff --git a/src/libs6/s6lock_check.c b/src/libs6/s6lock_check.c
new file mode 100644
index 0000000..e794128
--- /dev/null
+++ b/src/libs6/s6lock_check.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <skalibs/gensetdyn.h>
+#include <s6/s6lock.h>
+
+int s6lock_check (s6lock_t *a, uint16 id)
+{
+ char *p = GENSETDYN_P(char, &a->data, id) ;
+ switch (*p)
+ {
+ case EBUSY : return 1 ;
+ case EINVAL : return (errno = EINVAL, -1) ;
+ default :
+ {
+ if (error_isagain(*p)) return 0 ;
+ errno = *p ;
+ *p = EINVAL ;
+ gensetdyn_delete(&a->data, id) ;
+ return -1 ;
+ }
+ }
+}
diff --git a/src/libs6/s6lock_end.c b/src/libs6/s6lock_end.c
new file mode 100644
index 0000000..c460efd
--- /dev/null
+++ b/src/libs6/s6lock_end.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6/s6lock.h>
+
+void s6lock_end (s6lock_t *a)
+{
+ gensetdyn_free(&a->data) ;
+ genalloc_free(uint16, &a->list) ;
+ skaclient_end(&a->connection) ;
+ *a = s6lock_zero ;
+}
diff --git a/src/libs6/s6lock_release.c b/src/libs6/s6lock_release.c
new file mode 100644
index 0000000..95e863f
--- /dev/null
+++ b/src/libs6/s6lock_release.c
@@ -0,0 +1,28 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/skaclient.h>
+#include <s6/s6lock.h>
+
+int s6lock_release (s6lock_t *a, uint16 i, tain_t const *deadline, tain_t *stamp)
+{
+ char *p = GENSETDYN_P(char, &a->data, i) ;
+ if ((*p != EBUSY) && !error_isagain(*p))
+ {
+ s6lock_check(a, i) ;
+ return 1 ;
+ }
+ {
+ char err ;
+ char pack[3] = "-->" ;
+ uint16_pack_big(pack, i) ;
+ if (!skaclient_send(&a->connection, pack, 3, &skaclient_default_cb, &err, deadline, stamp)) return 0 ;
+ if (err) return (errno = err, 0) ;
+ }
+ *p = EINVAL ;
+ return gensetdyn_delete(&a->data, i) ;
+}
diff --git a/src/libs6/s6lock_start.c b/src/libs6/s6lock_start.c
new file mode 100644
index 0000000..e7993d9
--- /dev/null
+++ b/src/libs6/s6lock_start.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/environ.h>
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <s6/s6lock.h>
+
+int s6lock_start (s6lock_t *a, char const *path, tain_t const *deadline, tain_t *stamp)
+{
+ return skaclient_start_b(&a->connection, &a->buffers, path, S6LOCK_BANNER1, S6LOCK_BANNER1_LEN, S6LOCK_BANNER2, S6LOCK_BANNER2_LEN, deadline, stamp) ;
+}
diff --git a/src/libs6/s6lock_startf.c b/src/libs6/s6lock_startf.c
new file mode 100644
index 0000000..c34a595
--- /dev/null
+++ b/src/libs6/s6lock_startf.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/environ.h>
+#include <skalibs/tai.h>
+#include <skalibs/skaclient.h>
+#include <s6/s6lock.h>
+
+int s6lock_startf (s6lock_t *a, char const *lockdir, tain_t const *deadline, tain_t *stamp)
+{
+ char const *cargv[3] = { S6LOCKD_PROG, lockdir, 0 } ;
+ if (!lockdir) return (errno = EINVAL, 0) ;
+ return skaclient_startf_b(&a->connection, &a->buffers, cargv[0], cargv, (char const *const *)environ, SKACLIENT_OPTION_WAITPID, S6LOCK_BANNER1, S6LOCK_BANNER1_LEN, S6LOCK_BANNER2, S6LOCK_BANNER2_LEN, deadline, stamp) ;
+}
diff --git a/src/libs6/s6lock_update.c b/src/libs6/s6lock_update.c
new file mode 100644
index 0000000..6e6a2a0
--- /dev/null
+++ b/src/libs6/s6lock_update.c
@@ -0,0 +1,31 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/error.h>
+#include <skalibs/uint16.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/gensetdyn.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <s6/s6lock.h>
+
+static int msghandler (unixmessage_t const *m, void *context)
+{
+ s6lock_t *a = (s6lock_t *)context ;
+ char *p ;
+ uint16 id ;
+ if (m->len != 3 || m->nfds) return (errno = EPROTO, 0) ;
+ uint16_unpack_big(m->s, &id) ;
+ p = GENSETDYN_P(char, &a->data, id) ;
+ if (*p == EBUSY) *p = m->s[2] ;
+ else if (error_isagain(*p)) *p = m->s[2] ? m->s[2] : EBUSY ;
+ else return (errno = EPROTO, 0) ;
+ if (!genalloc_append(uint16, &a->list, &id)) return 0 ;
+ return 1 ;
+}
+
+int s6lock_update (s6lock_t *a)
+{
+ genalloc_setlen(uint16, &a->list, 0) ;
+ return skaclient_update(&a->connection, &msghandler, a) ;
+}
diff --git a/src/libs6/s6lock_wait_and.c b/src/libs6/s6lock_wait_and.c
new file mode 100644
index 0000000..460cc07
--- /dev/null
+++ b/src/libs6/s6lock_wait_and.c
@@ -0,0 +1,27 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <s6/s6lock.h>
+
+int s6lock_wait_and (s6lock_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline, tain_t *stamp)
+{
+ iopause_fd x = { -1, IOPAUSE_READ, 0 } ;
+ x.fd = s6lock_fd(a) ;
+ for (; n ; n--, idlist++)
+ {
+ for (;;)
+ {
+ register int r = s6lock_check(a, *idlist) ;
+ if (r < 0) return r ;
+ else if (r) break ;
+ r = iopause_stamp(&x, 1, deadline, stamp) ;
+ if (r < 0) return r ;
+ else if (!r) return (errno = ETIMEDOUT, -1) ;
+ else if (s6lock_update(a) < 0) return -1 ;
+ }
+ }
+ return 0 ;
+}
diff --git a/src/libs6/s6lock_wait_or.c b/src/libs6/s6lock_wait_or.c
new file mode 100644
index 0000000..0219574
--- /dev/null
+++ b/src/libs6/s6lock_wait_or.c
@@ -0,0 +1,30 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/uint16.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <s6/s6lock.h>
+
+int s6lock_wait_or (s6lock_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline, tain_t *stamp)
+{
+ iopause_fd x = { -1, IOPAUSE_READ | IOPAUSE_EXCEPT, 0 } ;
+ x.fd = s6lock_fd(a) ;
+ if (x.fd < 0) return -1 ;
+ for (;;)
+ {
+ register unsigned int i = 0 ;
+ register int r ;
+ for (; i < n ; i++)
+ {
+ r = s6lock_check(a, idlist[i]) ;
+ if (r < 0) return r ;
+ else if (r) return i ;
+ }
+ r = iopause_stamp(&x, 1, deadline, stamp) ;
+ if (r < 0) return 0 ;
+ else if (!r) return (errno = ETIMEDOUT, -1) ;
+ else if (s6lock_update(a) < 0) return -1 ;
+ }
+ return (errno = EPROTO, -1) ; /* can't happen */
+}
diff --git a/src/libs6/s6lock_zero.c b/src/libs6/s6lock_zero.c
new file mode 100644
index 0000000..a4e0138
--- /dev/null
+++ b/src/libs6/s6lock_zero.c
@@ -0,0 +1,5 @@
+/* ISC license. */
+
+#include <s6/s6lock.h>
+
+s6lock_t const s6lock_zero = S6LOCK_ZERO ;
diff --git a/src/libs6/s6lockd-helper.c b/src/libs6/s6lockd-helper.c
new file mode 100644
index 0000000..8979c67
--- /dev/null
+++ b/src/libs6/s6lockd-helper.c
@@ -0,0 +1,27 @@
+/* ISC license. */
+
+#include <skalibs/allreadwrite.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "s6lockd-helper lockfile"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ int fd ;
+ char const *x = env_get2(envp, "S6LOCK_EX") ;
+ char c ;
+ PROG = "s6lockd-helper" ;
+ if (argc < 2) dieusage() ;
+ fd = open_create(argv[1]) ;
+ if (fd < 0) strerr_diefu2sys(111, "open ", argv[1]) ;
+ if (((x && *x) ? lock_ex(fd) : lock_sh(fd)) < 0)
+ strerr_diefu2sys(111, "lock ", argv[1]) ;
+ if (fd_write(1, "!", 1) <= 0)
+ strerr_diefu1sys(111, "write to stdout") ;
+ if (fd_read(0, &c, 1) < 0)
+ strerr_diefu1sys(111, "read from stdin") ;
+ return 0 ;
+}
diff --git a/src/libs6/s6lockd.c b/src/libs6/s6lockd.c
new file mode 100644
index 0000000..ef088f9
--- /dev/null
+++ b/src/libs6/s6lockd.c
@@ -0,0 +1,316 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/error.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/sig.h>
+#include <skalibs/selfpipe.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/iopause.h>
+#include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
+#include <s6/s6lock.h>
+
+#define USAGE "s6lockd lockdir"
+#define X() strerr_dief1x(101, "internal inconsistency, please submit a bug-report.")
+
+typedef struct s6lockio_s s6lockio_t, *s6lockio_t_ref ;
+struct s6lockio_s
+{
+ unsigned int xindex ;
+ unsigned int pid ;
+ tain_t limit ;
+ int p[2] ;
+ uint16 id ; /* given by client */
+} ;
+#define S6LOCKIO_ZERO { 0, 0, TAIN_ZERO, { -1, -1 }, 0 }
+static s6lockio_t const szero = S6LOCKIO_ZERO ;
+
+static genalloc a = GENALLOC_ZERO ; /* array of s6lockio_t */
+
+static void s6lockio_free (s6lockio_t_ref p)
+{
+ register int e = errno ;
+ fd_close(p->p[1]) ;
+ fd_close(p->p[0]) ;
+ kill(p->pid, SIGTERM) ;
+ *p = szero ;
+ errno = e ;
+}
+
+static void cleanup (void)
+{
+ register unsigned int i = genalloc_len(s6lockio_t, &a) ;
+ for (; i ; i--) s6lockio_free(genalloc_s(s6lockio_t, &a) + i - 1) ;
+ genalloc_setlen(s6lockio_t, &a, 0) ;
+}
+
+static void trig (uint16 id, char e)
+{
+ char pack[3] ;
+ unixmessage_t m = { .s = pack, .len = 3, .fds = 0, .nfds = 0 } ;
+ uint16_pack_big(pack, id) ;
+ pack[2] = e ;
+ if (!unixmessage_put(unixmessage_sender_x, &m))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "build answer") ;
+ }
+}
+
+static void answer (char c)
+{
+ unixmessage_t m = { .s = &c, .len = 1, .fds = 0, .nfds = 0 } ;
+ if (!unixmessage_put(unixmessage_sender_1, &m))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "unixmessage_put") ;
+ }
+}
+
+static void remove (unsigned int i)
+{
+ register unsigned int n = genalloc_len(s6lockio_t, &a) - 1 ;
+ s6lockio_free(genalloc_s(s6lockio_t, &a) + i) ;
+ genalloc_s(s6lockio_t, &a)[i] = genalloc_s(s6lockio_t, &a)[n] ;
+ genalloc_setlen(s6lockio_t, &a, n) ;
+}
+
+static void handle_signals (void)
+{
+ for (;;)
+ {
+ switch (selfpipe_read())
+ {
+ case -1 : cleanup() ; strerr_diefu1sys(111, "selfpipe_read") ;
+ case 0 : return ;
+ case SIGTERM :
+ case SIGQUIT :
+ case SIGHUP :
+ case SIGABRT :
+ case SIGINT : cleanup() ; _exit(0) ;
+ case SIGCHLD : wait_reap() ; break ;
+ default : cleanup() ; X() ;
+ }
+ }
+}
+
+static int parse_protocol (unixmessage_t const *m, void *context)
+{
+ uint16 id ;
+ if (m->len < 3 || m->nfds)
+ {
+ cleanup() ;
+ strerr_dief1x(100, "invalid client request") ;
+ }
+ uint16_unpack_big(m->s, &id) ;
+ switch (m->s[2])
+ {
+ case '>' : /* release */
+ {
+ register unsigned int i = genalloc_len(s6lockio_t, &a) ;
+ for (; i ; i--) if (genalloc_s(s6lockio_t, &a)[i-1].id == id) break ;
+ if (i)
+ {
+ remove(i-1) ;
+ answer(0) ;
+ }
+ else answer(ENOENT) ;
+ break ;
+ }
+ case '<' : /* lock path */
+ {
+ s6lockio_t f = S6LOCKIO_ZERO ;
+ char const *cargv[3] = { S6LOCKD_HELPER_PROG, 0, 0 } ;
+ char const *cenvp[2] = { 0, 0 } ;
+ uint32 options, pathlen ;
+ if (m->len < 23)
+ {
+ answer(EPROTO) ;
+ break ;
+ }
+ uint32_unpack_big(m->s + 3, &options) ;
+ tain_unpack(m->s + 7, &f.limit) ;
+ uint32_unpack_big(m->s + 19, &pathlen) ;
+ if (pathlen + 23 != m->len || m->s[m->len - 1])
+ {
+ answer(EPROTO) ;
+ break ;
+ }
+ f.id = id ;
+ m->s[21] = '.' ;
+ m->s[22] = '/' ;
+ cargv[1] = (char const *)m->s + 21 ;
+ if (options & S6LOCK_OPTIONS_EX) cenvp[0] = "S6LOCK_EX=1" ;
+ f.pid = child_spawn(cargv[0], cargv, cenvp, f.p, 2) ;
+ if (!f.pid)
+ {
+ answer(errno) ;
+ break ;
+ }
+ if (!genalloc_append(s6lockio_t, &a, &f))
+ {
+ s6lockio_free(&f) ;
+ answer(errno) ;
+ break ;
+ }
+ answer(0) ;
+ break ;
+ }
+ default :
+ {
+ cleanup() ;
+ strerr_dief1x(100, "invalid client request") ;
+ }
+ }
+ (void)context ;
+ return 1 ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ tain_t deadline ;
+ int sfd ;
+ PROG = "s6lockd" ;
+
+ if (argc < 2) strerr_dieusage(100, USAGE) ;
+ if (chdir(argv[1]) < 0) strerr_diefu2sys(111, "chdir to ", argv[1]) ;
+ if (ndelay_on(0) < 0) strerr_diefu2sys(111, "ndelay_on ", "0") ;
+ if (ndelay_on(1) < 0) strerr_diefu2sys(111, "ndelay_on ", "1") ;
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ;
+
+ sfd = selfpipe_init() ;
+ if (sfd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
+ {
+ sigset_t set ;
+ sigemptyset(&set) ;
+ sigaddset(&set, SIGCHLD) ;
+ sigaddset(&set, SIGTERM) ;
+ sigaddset(&set, SIGQUIT) ;
+ sigaddset(&set, SIGHUP) ;
+ sigaddset(&set, SIGABRT) ;
+ sigaddset(&set, SIGINT) ;
+ if (selfpipe_trapset(&set) < 0)
+ strerr_diefu1sys(111, "trap signals") ;
+ }
+
+ tain_now_g() ;
+ tain_addsec_g(&deadline, 2) ;
+
+ if (!skaclient_server_01x_init_g(S6LOCK_BANNER1, S6LOCK_BANNER1_LEN, S6LOCK_BANNER2, S6LOCK_BANNER2_LEN, &deadline))
+ strerr_diefu1sys(111, "sync with client") ;
+
+ for (;;)
+ {
+ register unsigned int n = genalloc_len(s6lockio_t, &a) ;
+ iopause_fd x[4 + n] ;
+ unsigned int i = 0 ;
+ int r ;
+
+ tain_add_g(&deadline, &tain_infinite_relative) ;
+ x[0].fd = 0 ; x[0].events = IOPAUSE_EXCEPT | IOPAUSE_READ ;
+ x[1].fd = 1 ; x[1].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_1) ? 0 : IOPAUSE_WRITE ) ;
+ x[2].fd = unixmessage_sender_fd(unixmessage_sender_x) ;
+ x[2].events = IOPAUSE_EXCEPT | (unixmessage_sender_isempty(unixmessage_sender_x) ? 0 : IOPAUSE_WRITE) ;
+ x[3].fd = sfd ; x[3].events = IOPAUSE_READ ;
+ for (; i < n ; i++)
+ {
+ register s6lockio_t_ref p = genalloc_s(s6lockio_t, &a) + i ;
+ x[4+i].fd = p->p[0] ;
+ x[4+i].events = IOPAUSE_READ ;
+ if (p->limit.sec.x && tain_less(&p->limit, &deadline)) deadline = p->limit ;
+ p->xindex = 4+i ;
+ }
+
+ r = iopause_g(x, 4 + n, &deadline) ;
+ if (r < 0)
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "iopause") ;
+ }
+
+ /* timeout => seek and destroy */
+ if (!r)
+ {
+ for (i = 0 ; i < n ; i++)
+ {
+ register s6lockio_t_ref p = genalloc_s(s6lockio_t, &a) + i ;
+ if (p->limit.sec.x && !tain_future(&p->limit)) break ;
+ }
+ if (i < n)
+ {
+ trig(genalloc_s(s6lockio_t, &a)[i].id, ETIMEDOUT) ;
+ remove(i) ;
+ }
+ continue ;
+ }
+
+ /* client closed */
+ if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT) break ;
+
+ /* client is reading */
+ if (x[1].revents & IOPAUSE_WRITE)
+ if ((unixmessage_sender_flush(unixmessage_sender_1) < 0) && !error_isagain(errno))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "flush stdout") ;
+ }
+ if (x[2].revents & IOPAUSE_WRITE)
+ if ((unixmessage_sender_flush(unixmessage_sender_x) < 0) && !error_isagain(errno))
+ {
+ cleanup() ;
+ strerr_diefu1sys(111, "flush asyncout") ;
+ }
+
+ /* scan children for successes */
+ for (i = 0 ; i < genalloc_len(s6lockio_t, &a) ; i++)
+ {
+ register s6lockio_t_ref p = genalloc_s(s6lockio_t, &a) + i ;
+ if (p->p[0] < 0) continue ;
+ if (x[p->xindex].revents & IOPAUSE_READ)
+ {
+ char c ;
+ register int r = sanitize_read(fd_read(p->p[0], &c, 1)) ;
+ if (!r) continue ;
+ if (r < 0)
+ {
+ trig(p->id, errno) ;
+ remove(i--) ;
+ }
+ else if (c != '!')
+ {
+ trig(p->id, EPROTO) ;
+ remove(i--) ;
+ }
+ else
+ {
+ trig(p->id, 0) ;
+ p->limit = tain_zero ;
+ }
+ }
+ }
+
+ /* signals arrived */
+ if (x[3].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT)) handle_signals() ;
+
+ /* client is writing */
+ if (!unixmessage_receiver_isempty(unixmessage_receiver_0) || x[0].revents & IOPAUSE_READ)
+ {
+ if (unixmessage_handle(unixmessage_receiver_0, &parse_protocol, 0) < 0)
+ {
+ if (errno == EPIPE) break ; /* normal exit */
+ cleanup() ;
+ strerr_diefu1sys(111, "handle messages from client") ;
+ }
+ }
+ }
+ cleanup() ;
+ return 0 ;
+}
diff --git a/src/pipe-tools/deps-exe/s6-cleanfifodir b/src/pipe-tools/deps-exe/s6-cleanfifodir
new file mode 100644
index 0000000..83cec1e
--- /dev/null
+++ b/src/pipe-tools/deps-exe/s6-cleanfifodir
@@ -0,0 +1,2 @@
+-ls6
+-lskarnet
diff --git a/src/pipe-tools/deps-exe/s6-ftrig-listen b/src/pipe-tools/deps-exe/s6-ftrig-listen
new file mode 100644
index 0000000..38a1f7a
--- /dev/null
+++ b/src/pipe-tools/deps-exe/s6-ftrig-listen
@@ -0,0 +1,4 @@
+-ls6
+-lexecline
+-lskarnet
+${TAINNOW_LIB}
diff --git a/src/pipe-tools/deps-exe/s6-ftrig-listen1 b/src/pipe-tools/deps-exe/s6-ftrig-listen1
new file mode 100644
index 0000000..58a34e0
--- /dev/null
+++ b/src/pipe-tools/deps-exe/s6-ftrig-listen1
@@ -0,0 +1,3 @@
+-ls6
+-lskarnet
+${TAINNOW_LIB}
diff --git a/src/pipe-tools/deps-exe/s6-ftrig-notify b/src/pipe-tools/deps-exe/s6-ftrig-notify
new file mode 100644
index 0000000..83cec1e
--- /dev/null
+++ b/src/pipe-tools/deps-exe/s6-ftrig-notify
@@ -0,0 +1,2 @@
+-ls6
+-lskarnet
diff --git a/src/pipe-tools/deps-exe/s6-ftrig-wait b/src/pipe-tools/deps-exe/s6-ftrig-wait
new file mode 100644
index 0000000..58a34e0
--- /dev/null
+++ b/src/pipe-tools/deps-exe/s6-ftrig-wait
@@ -0,0 +1,3 @@
+-ls6
+-lskarnet
+${TAINNOW_LIB}
diff --git a/src/pipe-tools/deps-exe/s6-mkfifodir b/src/pipe-tools/deps-exe/s6-mkfifodir
new file mode 100644
index 0000000..83cec1e
--- /dev/null
+++ b/src/pipe-tools/deps-exe/s6-mkfifodir
@@ -0,0 +1,2 @@
+-ls6
+-lskarnet
diff --git a/src/pipe-tools/s6-cleanfifodir.c b/src/pipe-tools/s6-cleanfifodir.c
new file mode 100644
index 0000000..4af38e1
--- /dev/null
+++ b/src/pipe-tools/s6-cleanfifodir.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <s6/ftrigw.h>
+
+#define USAGE "s6-cleanfifodir fifodir"
+
+int main (int argc, char const *const *argv)
+{
+ PROG = "s6-cleanfifodir" ;
+ if (argc < 2) strerr_dieusage(100, USAGE) ;
+ if (!ftrigw_clean(argv[1]))
+ strerr_diefu2sys(111, "clean up fifodir at ", argv[1]) ;
+ return 0 ;
+}
diff --git a/src/pipe-tools/s6-ftrig-listen.c b/src/pipe-tools/s6-ftrig-listen.c
new file mode 100644
index 0000000..2f6e82b
--- /dev/null
+++ b/src/pipe-tools/s6-ftrig-listen.c
@@ -0,0 +1,122 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/sig.h>
+#include <skalibs/selfpipe.h>
+#include <execline/execline.h>
+#include <s6/ftrigr.h>
+
+#define USAGE "s6-ftrig-listen [ -a | -o ] [ -t timeout ] ~fifodir1 ~regexp1 ... ; prog..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static void handle_signals (void)
+{
+ for (;;) switch (selfpipe_read())
+ {
+ case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
+ case 0 : return ;
+ case SIGCHLD : wait_reap() ; break ;
+ default : strerr_dief1x(101, "unexpected data in selfpipe") ;
+ }
+}
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ;
+ tain_t deadline, tto ;
+ ftrigr_t a = FTRIGR_ZERO ;
+ int argc1 ;
+ unsigned int i = 0 ;
+ char or = 0 ;
+ PROG = "s6-ftrig-listen" ;
+ {
+ unsigned int t = 0 ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "aot:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'a' : or = 0 ; break ;
+ case 'o' : or = 1 ; break ;
+ case 't' : if (uint0_scan(subgetopt_here.arg, &t)) break ;
+ default : dieusage() ;
+ }
+ }
+ if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ;
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ }
+ if (argc < 2) dieusage() ;
+ argc1 = el_semicolon(argv) ;
+ if (!argc1 || (argc1 & 1) || (argc == argc1 + 1)) dieusage() ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated fifodir+regex block") ;
+ tain_now_g() ;
+ tain_add_g(&deadline, &tto) ;
+ x[0].fd = selfpipe_init() ;
+ if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
+ if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "selfpipe_trap") ;
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "sig_ignore") ;
+
+ if (!ftrigr_startf_g(&a, &deadline)) strerr_diefu1sys(111, "ftrigr_startf") ;
+ x[1].fd = ftrigr_fd(&a) ;
+
+ {
+ int pid = 0 ;
+ unsigned int idlen = argc1 >> 1 ;
+ uint16 ids[idlen] ;
+ for (; i < idlen ; i++)
+ {
+ ids[i] = ftrigr_subscribe_g(&a, argv[i<<1], argv[(i<<1)+1], 0, &deadline) ;
+ if (!ids[i]) strerr_diefu4sys(111, "subscribe to ", argv[i<<1], " with regexp ", argv[(i<<1)+1]) ;
+ }
+
+ pid = fork() ;
+ switch (pid)
+ {
+ case -1 : strerr_diefu1sys(111, "fork") ;
+ case 0 :
+ {
+ PROG = "s6-ftrig-listen (child)" ;
+ pathexec_run(argv[argc1 + 1], argv + argc1 + 1, envp) ;
+ strerr_dieexec(111, argv[argc1 + 1]) ;
+ }
+ }
+
+ for (;;)
+ {
+ register int r ;
+ i = 0 ;
+ while (i < idlen)
+ {
+ char dummy ;
+ r = ftrigr_check(&a, ids[i], &dummy) ;
+ if (r < 0) strerr_diefu1sys(111, "ftrigr_check") ;
+ else if (!r) i++ ;
+ else if (or) idlen = 0 ;
+ else ids[i] = ids[--idlen] ;
+ }
+ if (!idlen) break ;
+ r = iopause_g(x, 2, &deadline) ;
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+ else if (!r)
+ {
+ errno = ETIMEDOUT ;
+ strerr_diefu1sys(1, "get expected event") ;
+ }
+ if (x[0].revents & IOPAUSE_READ) handle_signals() ;
+ if (x[1].revents & IOPAUSE_READ)
+ {
+ if (ftrigr_update(&a) < 0) strerr_diefu1sys(111, "ftrigr_update") ;
+ }
+ }
+ }
+ return 0 ;
+}
diff --git a/src/pipe-tools/s6-ftrig-listen1.c b/src/pipe-tools/s6-ftrig-listen1.c
new file mode 100644
index 0000000..1354a64
--- /dev/null
+++ b/src/pipe-tools/s6-ftrig-listen1.c
@@ -0,0 +1,101 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/sig.h>
+#include <skalibs/selfpipe.h>
+#include <s6/ftrigr.h>
+
+#define USAGE "s6-ftrig-listen1 [ -t timeout ] fifodir regexp prog..."
+
+static void handle_signals (void)
+{
+ for (;;) switch (selfpipe_read())
+ {
+ case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
+ case 0 : return ;
+ case SIGCHLD : wait_reap() ; break ;
+ default : strerr_dief1x(101, "unexpected data in selfpipe") ;
+ }
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ;
+ tain_t deadline, tto ;
+ ftrigr_t a = FTRIGR_ZERO ;
+ int pid ;
+ uint16 id ;
+ PROG = "s6-ftrig-listen1" ;
+ {
+ unsigned int t = 0 ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "t:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 't' : if (uint0_scan(subgetopt_here.arg, &t)) break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ if (t) tain_from_millisecs(&tto, t) ;
+ else tto = tain_infinite_relative ;
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ }
+ if (argc < 3) strerr_dieusage(100, USAGE) ;
+
+ tain_now_g() ;
+ tain_add_g(&deadline, &tto) ;
+
+ if (!ftrigr_startf_g(&a, &deadline)) strerr_diefu1sys(111, "ftrigr_startf") ;
+ id = ftrigr_subscribe_g(&a, argv[0], argv[1], 0, &deadline) ;
+ if (!id) strerr_diefu4sys(111, "subscribe to ", argv[0], " with regexp ", argv[1]) ;
+
+ x[0].fd = selfpipe_init() ;
+ if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
+ if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "selfpipe_trap") ;
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "sig_ignore") ;
+ x[1].fd = ftrigr_fd(&a) ;
+
+ pid = fork() ;
+ switch (pid)
+ {
+ case -1 : strerr_diefu1sys(111, "fork") ;
+ case 0 :
+ {
+ PROG = "s6-ftrig-listen1 (child)" ;
+ pathexec_run(argv[2], argv+2, envp) ;
+ strerr_dieexec(111, argv[2]) ;
+ }
+ }
+
+ for (;;)
+ {
+ char dummy ;
+ register int r = ftrigr_check(&a, id, &dummy) ;
+ if (r < 0) strerr_diefu1sys(111, "ftrigr_check") ;
+ if (r) break ;
+ r = iopause_g(x, 2, &deadline) ;
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+ else if (!r)
+ {
+ errno = ETIMEDOUT ;
+ strerr_diefu1sys(1, "get expected event") ;
+ }
+ if (x[0].revents & IOPAUSE_READ) handle_signals() ;
+ if (x[1].revents & IOPAUSE_READ)
+ {
+ if (ftrigr_update(&a) < 0) strerr_diefu1sys(111, "ftrigr_update") ;
+ }
+ }
+
+ return 0 ;
+}
diff --git a/src/pipe-tools/s6-ftrig-notify.c b/src/pipe-tools/s6-ftrig-notify.c
new file mode 100644
index 0000000..1216a6a
--- /dev/null
+++ b/src/pipe-tools/s6-ftrig-notify.c
@@ -0,0 +1,20 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <s6/ftrigw.h>
+
+#define USAGE "s6-ftrig-notify fifodir message"
+
+int main (int argc, char const *const *argv)
+{
+ char const *p ;
+ PROG = "s6-ftrig-notify" ;
+ if (argc < 3) strerr_dieusage(100, USAGE) ;
+ p = argv[2] ;
+ for (; *p ; p++)
+ {
+ if (ftrigw_notify(argv[1], *p) == -1)
+ strerr_diefu2sys(111, "notify ", argv[1]) ;
+ }
+ return 0 ;
+}
diff --git a/src/pipe-tools/s6-ftrig-wait.c b/src/pipe-tools/s6-ftrig-wait.c
new file mode 100644
index 0000000..772ce86
--- /dev/null
+++ b/src/pipe-tools/s6-ftrig-wait.c
@@ -0,0 +1,48 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <s6/ftrigr.h>
+
+#define USAGE "s6-ftrig-wait [ -t timeout ] fifodir regexp"
+
+int main (int argc, char const *const *argv)
+{
+ tain_t deadline, tto ;
+ ftrigr_t a = FTRIGR_ZERO ;
+ uint16 id ;
+ char pack[2] = " \n" ;
+ PROG = "s6-ftrig-wait" ;
+ {
+ unsigned int t = 0 ;
+ for (;;)
+ {
+ register int opt = subgetopt(argc, argv, "t:") ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 't' : if (uint0_scan(subgetopt_here.arg, &t)) break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ if (t) tain_from_millisecs(&tto, t) ;
+ else tto = tain_infinite_relative ;
+ argc -= subgetopt_here.ind ; argv += subgetopt_here.ind ;
+ }
+ if (argc < 2) strerr_dieusage(100, USAGE) ;
+
+ tain_now_g() ;
+ tain_add_g(&deadline, &tto) ;
+
+ if (!ftrigr_startf_g(&a, &deadline)) strerr_diefu1sys(111, "ftrigr_startf") ;
+ id = ftrigr_subscribe_g(&a, argv[0], argv[1], 0, &deadline) ;
+ if (!id) strerr_diefu4sys(111, "subscribe to ", argv[0], " with regexp ", argv[1]) ;
+ if (ftrigr_wait_or_g(&a, &id, 1, &deadline, &pack[0]) == -1)
+ strerr_diefu2sys((errno == ETIMEDOUT) ? 1 : 111, "match regexp on ", argv[1]) ;
+ if (allwrite(1, pack, 2) < 2) strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/pipe-tools/s6-mkfifodir.c b/src/pipe-tools/s6-mkfifodir.c
new file mode 100644
index 0000000..4f5151a
--- /dev/null
+++ b/src/pipe-tools/s6-mkfifodir.c
@@ -0,0 +1,39 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <s6/ftrigw.h>
+
+#define USAGE "s6-mkfifodir [ -f ] [ -g gid ] fifodir"
+
+int main (int argc, char const *const *argv)
+{
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ int gid = -1 ;
+ int force = 0 ;
+ PROG = "s6-mkfifodir" ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "fg:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'f' : force = 1 ; break ;
+ case 'g' :
+ {
+ unsigned int g ;
+ if (!uint0_scan(l.arg, &g)) strerr_dieusage(100, USAGE) ;
+ gid = (int)g ;
+ break ;
+ }
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (argc < 1) strerr_dieusage(100, USAGE) ;
+
+ if (!ftrigw_fifodir_make(*argv, gid, force))
+ strerr_diefu2sys(111, "create fifodir at ", *argv) ;
+ return 0 ;
+}
diff --git a/src/supervision/deps-exe/s6-supervise b/src/supervision/deps-exe/s6-supervise
new file mode 100644
index 0000000..58a34e0
--- /dev/null
+++ b/src/supervision/deps-exe/s6-supervise
@@ -0,0 +1,3 @@
+-ls6
+-lskarnet
+${TAINNOW_LIB}
diff --git a/src/supervision/deps-exe/s6-svc b/src/supervision/deps-exe/s6-svc
new file mode 100644
index 0000000..83cec1e
--- /dev/null
+++ b/src/supervision/deps-exe/s6-svc
@@ -0,0 +1,2 @@
+-ls6
+-lskarnet
diff --git a/src/supervision/deps-exe/s6-svok b/src/supervision/deps-exe/s6-svok
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/supervision/deps-exe/s6-svok
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/supervision/deps-exe/s6-svscan b/src/supervision/deps-exe/s6-svscan
new file mode 100644
index 0000000..58a34e0
--- /dev/null
+++ b/src/supervision/deps-exe/s6-svscan
@@ -0,0 +1,3 @@
+-ls6
+-lskarnet
+${TAINNOW_LIB}
diff --git a/src/supervision/deps-exe/s6-svscanctl b/src/supervision/deps-exe/s6-svscanctl
new file mode 100644
index 0000000..83cec1e
--- /dev/null
+++ b/src/supervision/deps-exe/s6-svscanctl
@@ -0,0 +1,2 @@
+-ls6
+-lskarnet
diff --git a/src/supervision/deps-exe/s6-svstat b/src/supervision/deps-exe/s6-svstat
new file mode 100644
index 0000000..7065b26
--- /dev/null
+++ b/src/supervision/deps-exe/s6-svstat
@@ -0,0 +1,3 @@
+-ls6
+-lskarnet
+${SYSCLOCK_LIB}
diff --git a/src/supervision/deps-exe/s6-svwait b/src/supervision/deps-exe/s6-svwait
new file mode 100644
index 0000000..58a34e0
--- /dev/null
+++ b/src/supervision/deps-exe/s6-svwait
@@ -0,0 +1,3 @@
+-ls6
+-lskarnet
+${TAINNOW_LIB}
diff --git a/src/supervision/s6-supervise.c b/src/supervision/s6-supervise.c
new file mode 100644
index 0000000..f9a9872
--- /dev/null
+++ b/src/supervision/s6-supervise.c
@@ -0,0 +1,508 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/sig.h>
+#include <skalibs/selfpipe.h>
+#include <skalibs/environ.h>
+#include <skalibs/skamisc.h>
+#include <s6/ftrigw.h>
+#include <s6/s6-supervise.h>
+
+#define USAGE "s6-supervise dir"
+
+typedef enum trans_e trans_t, *trans_t_ref ;
+enum trans_e
+{
+ V_TIMEOUT, V_CHLD, V_TERM, V_HUP, V_QUIT,
+ V_a, V_b, V_q, V_h, V_k, V_t, V_i, V_1, V_2, V_f, V_F, V_p, V_c,
+ V_o, V_d, V_u, V_x, V_O
+} ;
+
+typedef enum state_e state_t, *state_t_ref ;
+enum state_e
+{
+ DOWN,
+ UP,
+ FINISH,
+ LASTUP,
+ LASTFINISH
+} ;
+
+typedef void action_t (void) ;
+typedef action_t *action_t_ref ;
+
+static tain_t deadline ;
+static s6_svstatus_t status = { .stamp = TAIN_ZERO, .pid = 0, .flagwant = 1, .flagwantup = 1, .flagpaused = 0, .flagfinishing = 0 } ;
+static state_t state = DOWN ;
+static int flagsetsid = 1 ;
+static int cont = 1 ;
+
+static inline void settimeout (int secs)
+{
+ tain_addsec_g(&deadline, secs) ;
+}
+
+static inline void settimeout_infinite (void)
+{
+ tain_add_g(&deadline, &tain_infinite_relative) ;
+}
+
+static inline void announce (void)
+{
+ if (!s6_svstatus_write(".", &status))
+ strerr_warnwu1sys("write status file") ;
+}
+
+
+/* The action array. */
+
+static void nop (void)
+{
+}
+
+static void bail (void)
+{
+ cont = 0 ;
+}
+
+static void killa (void)
+{
+ kill(status.pid, SIGALRM) ;
+}
+
+static void killb (void)
+{
+ kill(status.pid, SIGABRT) ;
+}
+
+static void killh (void)
+{
+ kill(status.pid, SIGHUP) ;
+}
+
+static void killq (void)
+{
+ kill(status.pid, SIGQUIT) ;
+}
+
+static void killk (void)
+{
+ kill(status.pid, SIGKILL) ;
+}
+
+static void killt (void)
+{
+ kill(status.pid, SIGTERM) ;
+}
+
+static void killi (void)
+{
+ kill(status.pid, SIGINT) ;
+}
+
+static void kill1 (void)
+{
+ kill(status.pid, SIGUSR1) ;
+}
+
+static void kill2 (void)
+{
+ kill(status.pid, SIGUSR2) ;
+}
+
+static void killp (void)
+{
+ kill(status.pid, SIGSTOP) ;
+ status.flagpaused = 1 ;
+ announce() ;
+}
+
+static void killc (void)
+{
+ kill(status.pid, SIGCONT) ;
+ status.flagpaused = 0 ;
+ announce() ;
+}
+
+static void trystart (void)
+{
+ int p[2] ;
+ pid_t pid ;
+ if (pipecoe(p) < 0)
+ {
+ settimeout(60) ;
+ strerr_warnwu1sys("pipecoe (waiting 60 seconds)") ;
+ return ;
+ }
+ pid = fork() ;
+ if (pid < 0)
+ {
+ settimeout(60) ;
+ strerr_warnwu1sys("fork (waiting 60 seconds)") ;
+ fd_close(p[1]) ; fd_close(p[0]) ;
+ return ;
+ }
+ else if (!pid)
+ {
+ char const *cargv[2] = { "run", 0 } ;
+ PROG = "s6-supervise (child)" ;
+ selfpipe_finish() ;
+ fd_close(p[0]) ;
+ if (flagsetsid) setsid() ;
+ execve("./run", (char *const *)cargv, (char *const *)environ) ;
+ fd_write(p[1], "", 1) ;
+ strerr_dieexec(111, "run") ;
+ }
+ fd_close(p[1]) ;
+ {
+ char c ;
+ switch (fd_read(p[0], &c, 1))
+ {
+ case -1 :
+ fd_close(p[0]) ;
+ settimeout(60) ;
+ strerr_warnwu1sys("read pipe (waiting 60 seconds)") ;
+ kill(pid, SIGKILL) ;
+ return ;
+ case 1 :
+ {
+ fd_close(p[0]) ;
+ settimeout(10) ;
+ strerr_warnwu1x("spawn ./run - waiting 10 seconds") ;
+ return ;
+ }
+ }
+ }
+ fd_close(p[0]) ;
+ settimeout_infinite() ;
+ state = UP ;
+ status.pid = pid ;
+ tain_copynow(&status.stamp) ;
+ announce() ;
+ ftrigw_notify(S6_SUPERVISE_EVENTDIR, 'u') ;
+}
+
+static void downtimeout (void)
+{
+ if (status.flagwant && status.flagwantup) trystart() ;
+ else settimeout_infinite() ;
+}
+
+static void down_O (void)
+{
+ status.flagwant = 0 ;
+ announce() ;
+}
+
+static void down_o (void)
+{
+ down_O() ;
+ trystart() ;
+}
+
+static void down_u (void)
+{
+ status.flagwant = 1 ;
+ status.flagwantup = 1 ;
+ announce() ;
+ trystart() ;
+}
+
+static void down_d (void)
+{
+ status.flagwant = 1 ;
+ status.flagwantup = 0 ;
+ announce() ;
+}
+
+static void tryfinish (int wstat, int islast)
+{
+ register pid_t pid = fork() ;
+ if (pid < 0)
+ {
+ strerr_warnwu2sys("fork for ", "./finish") ;
+ if (islast) bail() ;
+ state = DOWN ;
+ status.pid = 0 ;
+ settimeout(1) ;
+ return ;
+ }
+ else if (!pid)
+ {
+ char fmt0[UINT_FMT] ;
+ char fmt1[UINT_FMT] ;
+ char *cargv[4] = { "finish", fmt0, fmt1, 0 } ;
+ selfpipe_finish() ;
+ fmt0[uint_fmt(fmt0, WIFSIGNALED(wstat) ? 255 : WEXITSTATUS(wstat))] = 0 ;
+ fmt1[uint_fmt(fmt1, WIFSIGNALED(wstat))] = 0 ;
+ if (flagsetsid) setsid() ;
+ execve("./finish", cargv, (char *const *)environ) ;
+ _exit(111) ;
+ }
+ status.pid = pid ;
+ status.flagfinishing = 1 ;
+ state = islast ? LASTFINISH : FINISH ;
+ settimeout(5) ;
+}
+
+static void uptimeout (void)
+{
+ settimeout_infinite() ;
+ strerr_warnw1x("can't happen: timeout while the service is up!") ;
+}
+
+static void up_z (void)
+{
+ int wstat = status.pid ;
+ status.pid = 0 ;
+ tain_copynow(&status.stamp) ;
+ announce() ;
+ ftrigw_notify(S6_SUPERVISE_EVENTDIR, 'd') ;
+ tryfinish(wstat, 0) ;
+}
+
+static void up_o (void)
+{
+ status.flagwant = 0 ;
+ announce() ;
+}
+
+static void up_d (void)
+{
+ status.flagwant = 1 ;
+ status.flagwantup = 0 ;
+ killt() ;
+ killc() ;
+}
+
+static void up_u (void)
+{
+ status.flagwant = 1 ;
+ status.flagwantup = 1 ;
+ announce() ;
+}
+
+static void closethem (void)
+{
+ fd_close(0) ;
+ fd_close(1) ;
+ open_read("/dev/null") ;
+ open_write("/dev/null") ;
+}
+
+static void up_x (void)
+{
+ state = LASTUP ;
+ closethem() ;
+}
+
+static void up_term (void)
+{
+ up_x() ;
+ up_d() ;
+}
+
+static void finishtimeout (void)
+{
+ strerr_warnw1x("finish script takes too long - killing it") ;
+ killc() ; killk() ;
+ settimeout(3) ;
+}
+
+static void finish_z (void)
+{
+ status.pid = 0 ;
+ status.flagfinishing = 0 ;
+ state = DOWN ;
+ announce() ;
+ settimeout(1) ;
+}
+
+static void finish_u (void)
+{
+ status.flagwant = 1 ;
+ status.flagwantup = 1 ;
+ announce() ;
+}
+
+static void finish_x (void)
+{
+ state = LASTFINISH ;
+ closethem() ;
+}
+
+static void lastup_z (void)
+{
+ int wstat = status.pid ;
+ status.pid = 0 ;
+ tain_copynow(&status.stamp) ;
+ announce() ;
+ ftrigw_notify(S6_SUPERVISE_EVENTDIR, 'd') ;
+ tryfinish(wstat, 1) ;
+}
+
+static action_t_ref const actions[5][23] =
+{
+ { &downtimeout, &nop, &bail, &bail, &bail,
+ &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop,
+ &down_o, &down_d, &down_u, &bail, &down_O },
+ { &uptimeout, &up_z, &up_term, &up_x, &up_term,
+ &killa, &killb, &killq, &killh, &killk, &killt, &killi, &kill1, &kill2, &nop, &nop, &killp, &killc,
+ &up_o, &up_d, &up_u, &up_x, &up_o },
+ { &finishtimeout, &finish_z, &finish_x, &finish_x, &finish_x,
+ &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop,
+ &up_o, &down_d, &finish_u, &finish_x, &up_o },
+ { &uptimeout, &lastup_z, &up_d, &nop, &up_d,
+ &killa, &killb, &killq, &killh, &killk, &killt, &killi, &kill1, &kill2, &nop, &nop, &killp, &killc,
+ &up_o, &up_d, &nop, &nop, &up_o },
+ { &finishtimeout, &bail, &nop, &nop, &nop,
+ &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop, &nop,
+ &nop, &nop, &nop, &nop, &nop }
+} ;
+
+
+/* The main loop.
+ It just loops around the iopause(), calling snippets of code in "actions" when needed. */
+
+
+static void handle_signals (void)
+{
+ for (;;)
+ {
+ char c = selfpipe_read() ;
+ switch (c)
+ {
+ case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
+ case 0 : return ;
+ case SIGCHLD :
+ if (!status.pid) wait_reap() ;
+ else
+ {
+ int wstat ;
+ int r = wait_pid_nohang(status.pid, &wstat) ;
+ if (r < 0)
+ if (errno != ECHILD) strerr_diefu1sys(111, "wait_pid_nohang") ;
+ else break ;
+ else if (!r) break ;
+ status.pid = wstat ;
+ (*actions[state][V_CHLD])() ;
+ }
+ break ;
+ case SIGTERM :
+ (*actions[state][V_TERM])() ;
+ break ;
+ case SIGHUP :
+ (*actions[state][V_HUP])() ;
+ break ;
+ case SIGQUIT :
+ (*actions[state][V_QUIT])() ;
+ break ;
+ default :
+ strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ;
+ }
+ }
+}
+
+static void handle_control (int fd)
+{
+ for (;;)
+ {
+ char c ;
+ register int r = sanitize_read(fd_read(fd, &c, 1)) ;
+ if (r < 0) strerr_diefu1sys(111, "read " S6_SUPERVISE_CTLDIR "/control") ;
+ else if (!r) break ;
+ else
+ {
+ register unsigned int pos = byte_chr("abqhkti12fFpcoduxO", 18, c) ;
+ if (pos < 18) (*actions[state][V_a + pos])() ;
+ }
+ }
+}
+
+int main (int argc, char const *const *argv)
+{
+ iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ;
+ PROG = "s6-supervise" ;
+ if (argc < 2) strerr_dieusage(100, USAGE) ;
+ if (chdir(argv[1]) < 0) strerr_diefu2sys(111, "chdir to ", argv[1]) ;
+ {
+ register unsigned int proglen = str_len(PROG) ;
+ register unsigned int namelen = str_len(argv[1]) ;
+ char progname[proglen + namelen + 2] ;
+ byte_copy(progname, proglen, PROG) ;
+ progname[proglen] = ' ' ;
+ byte_copy(progname + proglen + 1, namelen + 1, argv[1]) ;
+ PROG = progname ;
+ if (!fd_sanitize()) strerr_diefu1sys(111, "sanitize stdin and stdout") ;
+ x[1].fd = s6_supervise_lock(S6_SUPERVISE_CTLDIR) ;
+ if (!ftrigw_fifodir_make(S6_SUPERVISE_EVENTDIR, -1, 0))
+ strerr_diefu2sys(111, "mkfifodir ", S6_SUPERVISE_EVENTDIR) ;
+ x[0].fd = selfpipe_init() ;
+ if (x[0].fd == -1) strerr_diefu1sys(111, "init selfpipe") ;
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ {
+ sigset_t set ;
+ sigemptyset(&set) ;
+ sigaddset(&set, SIGTERM) ;
+ sigaddset(&set, SIGHUP) ;
+ sigaddset(&set, SIGQUIT) ;
+ sigaddset(&set, SIGCHLD) ;
+ if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ;
+ }
+
+ if (!ftrigw_clean(S6_SUPERVISE_EVENTDIR))
+ strerr_warnwu2sys("ftrigw_clean ", S6_SUPERVISE_EVENTDIR) ;
+
+ {
+ struct stat st ;
+ if (stat("down", &st) == -1)
+ {
+ if (errno != ENOENT)
+ strerr_diefu1sys(111, "stat down") ;
+ }
+ else status.flagwantup = 0 ;
+ if (stat("nosetsid", &st) == -1)
+ {
+ if (errno != ENOENT)
+ strerr_diefu1sys(111, "stat nosetsid") ;
+ }
+ else flagsetsid = 0 ;
+ }
+
+ tain_now_g() ;
+ settimeout(0) ;
+ tain_copynow(&status.stamp) ;
+ announce() ;
+ ftrigw_notify(S6_SUPERVISE_EVENTDIR, 's') ;
+
+ while (cont)
+ {
+ register int r = iopause_g(x, 2, &deadline) ;
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+ else if (!r) (*actions[state][V_TIMEOUT])() ;
+ else
+ {
+ if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT)
+ strerr_diefu1x(111, "iopause: trouble with pipes") ;
+ if (x[0].revents & IOPAUSE_READ) handle_signals() ;
+ else if (x[1].revents & IOPAUSE_READ) handle_control(x[1].fd) ;
+ }
+ }
+
+ ftrigw_notify(S6_SUPERVISE_EVENTDIR, 'x') ;
+ }
+ return 0 ;
+}
diff --git a/src/supervision/s6-svc.c b/src/supervision/s6-svc.c
new file mode 100644
index 0000000..699eefd
--- /dev/null
+++ b/src/supervision/s6-svc.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <s6/s6-supervise.h>
+
+#define USAGE "s6-svc [ -abqhkti12fFpcoduxO ] servicedir"
+
+int main (int argc, char const *const *argv)
+{
+ PROG = "s6-svc" ;
+ return s6_svc_main(argc, argv, "abqhkti12fFpcoduxO", USAGE, "supervise") ;
+}
diff --git a/src/supervision/s6-svok.c b/src/supervision/s6-svok.c
new file mode 100644
index 0000000..4a615e9
--- /dev/null
+++ b/src/supervision/s6-svok.c
@@ -0,0 +1,32 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <s6/s6-supervise.h>
+
+#define USAGE "s6-svok servicedir"
+
+int main (int argc, char const *const *argv)
+{
+ PROG = "s6-svok" ;
+ if (argc < 2) strerr_dieusage(100, USAGE) ;
+ argv++ ; argc-- ;
+ {
+ int fd ;
+ unsigned int dirlen = str_len(*argv) ;
+ char fn[dirlen + 9 + sizeof(S6_SUPERVISE_CTLDIR)] ;
+ byte_copy(fn, dirlen, *argv) ;
+ fn[dirlen] = '/' ;
+ byte_copy(fn + dirlen + 1, sizeof(S6_SUPERVISE_CTLDIR) - 1, S6_SUPERVISE_CTLDIR) ;
+ byte_copy(fn + dirlen + sizeof(S6_SUPERVISE_CTLDIR), 9, "/control") ;
+ fd = open_write(fn) ;
+ if (fd < 0)
+ {
+ if ((errno == ENXIO) || (errno == ENOENT)) return 1 ;
+ else strerr_diefu2sys(111, "open_write ", fn) ;
+ }
+ }
+ return 0 ;
+}
diff --git a/src/supervision/s6-svscan.c b/src/supervision/s6-svscan.c
new file mode 100644
index 0000000..8b0f82e
--- /dev/null
+++ b/src/supervision/s6-svscan.c
@@ -0,0 +1,498 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/direntry.h>
+#include <skalibs/sig.h>
+#include <skalibs/selfpipe.h>
+#include <skalibs/environ.h>
+#include <s6/config.h>
+#include <s6/s6-supervise.h>
+
+#define USAGE "s6-svscan [ -c maxservices ] [ -t timeout ] [ dir ]"
+
+#define FINISH_PROG S6_SVSCAN_CTLDIR "/finish"
+#define CRASH_PROG S6_SVSCAN_CTLDIR "/crash"
+
+#define DIR_RETRY_TIMEOUT 3
+#define CHECK_RETRY_TIMEOUT 4
+
+struct svinfo
+{
+ dev_t dev ;
+ ino_t ino ;
+ tain_t restartafter[2] ;
+ int pid[2] ;
+ int p[2] ;
+ unsigned int flagactive : 1 ;
+ unsigned int flaglog : 1 ;
+} ;
+#define SVINFO_ZERO { -1, -1, { TAIN_ZERO, TAIN_ZERO }, { 0, 0 }, { -1, -1 }, 0, 0, 0 } ;
+
+static struct svinfo *services ;
+static unsigned int max = 500 ;
+static unsigned int n = 0 ;
+static tain_t deadline, defaulttimeout ;
+static char const *finish_arg = "reboot" ;
+static int wantreap = 1 ;
+static int wantscan = 1 ;
+static unsigned int wantkill = 0 ;
+static int cont = 1 ;
+
+static void panicnosp (char const *) gccattr_noreturn ;
+static void panicnosp (char const *errmsg)
+{
+ char const *eargv[2] = { CRASH_PROG, 0 } ;
+ strerr_warnwu1sys(errmsg) ;
+ strerr_warnw2x("executing into ", eargv[0]) ;
+ execve(eargv[0], (char *const *)eargv, (char *const *)environ) ;
+ /* and if that execve fails, screw it and just die */
+ strerr_dieexec(111, eargv[0]) ;
+}
+
+static void panic (char const *) gccattr_noreturn ;
+static void panic (char const *errmsg)
+{
+ int e = errno ;
+ selfpipe_finish() ;
+ errno = e ;
+ panicnosp(errmsg) ;
+}
+
+static void killthem (void)
+{
+ register unsigned int i = 0 ;
+ if (!wantkill) return ;
+ for (; i < n ; i++)
+ {
+ if (!(wantkill & 1) && services[i].flagactive) continue ;
+ if (services[i].pid[0])
+ kill(services[i].pid[0], (wantkill & 2) ? SIGTERM : SIGHUP) ;
+ if (services[i].flaglog && services[i].pid[1])
+ kill(services[i].pid[1], (wantkill & 4) ? SIGTERM : SIGHUP) ;
+ }
+ wantkill = 0 ;
+}
+
+static void term (void)
+{
+ cont = 0 ;
+ wantkill = 3 ;
+}
+
+static void hup (void)
+{
+ cont = 0 ;
+ wantkill = 1 ;
+}
+
+static void quit (void)
+{
+ cont = 0 ;
+ wantkill = 7 ;
+}
+
+static void intr (void)
+{
+ finish_arg = "reboot" ;
+ term() ;
+}
+
+static void handle_signals (void)
+{
+ for (;;)
+ {
+ switch (selfpipe_read())
+ {
+ case -1 : panic("selfpipe_read") ;
+ case 0 : return ;
+ case SIGCHLD : wantreap = 1 ; break ;
+ case SIGALRM : wantscan = 1 ; break ;
+ case SIGTERM : term() ; break ;
+ case SIGHUP : hup() ; break ;
+ case SIGQUIT : quit() ; break ;
+ case SIGABRT : cont = 0 ; break ;
+ case SIGINT : intr() ; break ;
+ }
+ }
+}
+
+static void handle_control (int fd)
+{
+ for (;;)
+ {
+ char c ;
+ int r = sanitize_read(fd_read(fd, &c, 1)) ;
+ if (r == -1) panic("read control pipe") ;
+ else if (!r) break ;
+ else switch (c)
+ {
+ case 'p' : finish_arg = "poweroff" ; break ;
+ case 'h' : hup() ; return ;
+ case 'r' : finish_arg = "reboot" ; break ;
+ case 'a' : wantscan = 1 ; break ;
+ case 't' : term() ; return ;
+ case 's' : finish_arg = "halt" ; break ;
+ case 'z' : wantreap = 1 ; break ;
+ case 'b' : cont = 0 ; return ;
+ case 'n' : wantkill = 2 ; break ;
+ case 'N' : wantkill = 6 ; break ;
+ case '6' :
+ case 'i' : intr() ; return ;
+ case 'q' : quit() ; return ;
+ case '0' : finish_arg = "halt" ; term() ; return ;
+ case '7' : finish_arg = "poweroff" ; term() ; return ;
+ case '8' : finish_arg = "other" ; term() ; return ;
+ default :
+ {
+ char s[2] = { c, 0 } ;
+ strerr_warnw2x("received unknown control command: ", s) ;
+ }
+ }
+ }
+}
+
+
+/* First essential function: the reaper.
+ s6-svscan must wait() for all children,
+ including ones it doesn't know it has.
+ Dead active services are flagged to be restarted in 1 second. */
+
+static void reap (void)
+{
+ tain_t nextscan ;
+ if (!wantreap) return ;
+ wantreap = 0 ;
+ tain_addsec_g(&nextscan, 1) ;
+ for (;;)
+ {
+ int wstat ;
+ int r = wait_nohang(&wstat) ;
+ if (r < 0)
+ if (errno != ECHILD) panic("wait_nohang") ;
+ else break ;
+ else if (!r) break ;
+ else
+ {
+ register unsigned int i = 0 ;
+ for (; i < n ; i++)
+ {
+ if (services[i].pid[0] == r)
+ {
+ services[i].pid[0] = 0 ;
+ services[i].restartafter[0] = nextscan ;
+ break ;
+ }
+ else if (services[i].pid[1] == r)
+ {
+ services[i].pid[1] = 0 ;
+ services[i].restartafter[1] = nextscan ;
+ break ;
+ }
+ }
+ if (i == n) continue ;
+ if (services[i].flagactive)
+ {
+ if (tain_less(&nextscan, &deadline)) deadline = nextscan ;
+ }
+ else
+ {
+ if (services[i].flaglog)
+ {
+ /*
+ BLACK MAGIC:
+ - we need to close the pipe early:
+ * as soon as the writer exits so the logger can exit on EOF
+ * or as soon as the logger exits so the writer can crash on EPIPE
+ - but if the same service gets reactivated before the second
+ supervise process exits, ouch: we've lost the pipe
+ - so we can't reuse the same service even if it gets reactivated
+ - so we're marking a dying service with a closed pipe
+ - if the scanner sees a service with p[0] = -1 it won't flag
+ it as active (and won't restart the dead supervise)
+ - but if the service gets reactivated we want it to restart
+ as soon as the 2nd supervise process dies
+ - so the scanner marks such a process with p[0] = -2
+ - and the reaper triggers a scan when it finds a -2.
+ */
+ if (services[i].p[0] >= 0)
+ {
+ fd_close(services[i].p[1]) ; services[i].p[1] = -1 ;
+ fd_close(services[i].p[0]) ; services[i].p[0] = -1 ;
+ }
+ else if (services[i].p[0] == -2) wantscan = 1 ;
+ }
+ if (!services[i].pid[0] && !services[i].pid[1])
+ services[i] = services[--n] ;
+ }
+ }
+ }
+}
+
+
+/* Second essential function: the scanner.
+ It monitors the service directories and spawns a supervisor
+ if needed. */
+
+static void trystart (unsigned int i, char const *name, int islog)
+{
+ int pid = fork() ;
+ switch (pid)
+ {
+ case -1 :
+ tain_addsec_g(&services[i].restartafter[islog], CHECK_RETRY_TIMEOUT) ;
+ strerr_warnwu2sys("fork for ", name) ;
+ return ;
+ case 0 :
+ {
+ char const *cargv[3] = { "s6-supervise", name, 0 } ;
+ PROG = "s6-svscan (child)" ;
+ selfpipe_finish() ;
+ if (services[i].flaglog)
+ if (fd_move(!islog, services[i].p[!islog]) == -1)
+ strerr_diefu2sys(111, "set fds for ", name) ;
+ pathexec_run(S6_BINPREFIX "s6-supervise", cargv, (char const **)environ) ;
+ strerr_dieexec(111, S6_BINPREFIX "s6-supervise") ;
+ }
+ }
+ services[i].pid[islog] = pid ;
+}
+
+static void retrydirlater (void)
+{
+ tain_t a ;
+ tain_addsec_g(&a, DIR_RETRY_TIMEOUT) ;
+ if (tain_less(&a, &deadline)) deadline = a ;
+}
+
+static void check (char const *name)
+{
+ struct stat st ;
+ unsigned int namelen ;
+ unsigned int i = 0 ;
+ if (name[0] == '.') return ;
+ if (stat(name, &st) == -1)
+ {
+ strerr_warnwu2sys("stat ", name) ;
+ retrydirlater() ;
+ return ;
+ }
+ if (!S_ISDIR(st.st_mode)) return ;
+ namelen = str_len(name) ;
+ for (; i < n ; i++) if ((services[i].ino == st.st_ino) && (services[i].dev == st.st_dev)) break ;
+ if (i < n)
+ {
+ if (services[i].flaglog && (services[i].p[0] < 0))
+ {
+ /* See BLACK MAGIC above. */
+ services[i].p[0] = -2 ;
+ return ;
+ }
+ }
+ else
+ {
+ if (n >= max)
+ {
+ strerr_warnwu3x("start supervisor for ", name, ": too many services") ;
+ return ;
+ }
+ else
+ {
+ struct stat su ;
+ char tmp[namelen + 5] ;
+ byte_copy(tmp, namelen, name) ;
+ byte_copy(tmp + namelen, 5, "/log") ;
+ if (stat(tmp, &su) < 0)
+ if (errno == ENOENT) services[i].flaglog = 0 ;
+ else
+ {
+ strerr_warnwu2sys("stat ", tmp) ;
+ retrydirlater() ;
+ return ;
+ }
+ else if (!S_ISDIR(su.st_mode))
+ services[i].flaglog = 0 ;
+ else
+ {
+ if (pipecoe(services[i].p) < 0)
+ {
+ strerr_warnwu1sys("pipecoe") ;
+ retrydirlater() ;
+ return ;
+ }
+ services[i].flaglog = 1 ;
+ }
+ services[i].ino = st.st_ino ;
+ services[i].dev = st.st_dev ;
+ tain_copynow(&services[i].restartafter[0]) ;
+ tain_copynow(&services[i].restartafter[1]) ;
+ services[i].pid[0] = 0 ;
+ services[i].pid[1] = 0 ;
+ n++ ;
+ }
+ }
+
+ services[i].flagactive = 1 ;
+
+ if (services[i].flaglog && !services[i].pid[1])
+ {
+ if (!tain_future(&services[i].restartafter[1]))
+ {
+ char tmp[namelen + 5] ;
+ byte_copy(tmp, namelen, name) ;
+ byte_copy(tmp + namelen, 5, "/log") ;
+ trystart(i, tmp, 1) ;
+ }
+ else if (tain_less(&services[i].restartafter[1], &deadline))
+ deadline = services[i].restartafter[1] ;
+ }
+
+ if (!services[i].pid[0])
+ {
+ if (!tain_future(&services[i].restartafter[0]))
+ trystart(i, name, 0) ;
+ else if (tain_less(&services[i].restartafter[0], &deadline))
+ deadline = services[i].restartafter[0] ;
+ }
+}
+
+static void scan (void)
+{
+ DIR *dir ;
+ if (!wantscan) return ;
+ wantscan = 0 ;
+ dir = opendir(".") ;
+ if (!dir)
+ {
+ strerr_warnwu1sys("opendir .") ;
+ retrydirlater() ;
+ return ;
+ }
+ {
+ register unsigned int i = 0 ;
+ for (; i < n ; i++) services[i].flagactive = 0 ;
+ }
+ for (;;)
+ {
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ check(d->d_name) ;
+ }
+ if (errno)
+ {
+ strerr_warnwu1sys("readdir .") ;
+ retrydirlater() ;
+ }
+ dir_close(dir) ;
+}
+
+
+int main (int argc, char const *const *argv)
+{
+ iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ;
+ PROG = "s6-svscan" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ unsigned int t = 5000 ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "t:c:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 't' : if (uint0_scan(l.arg, &t)) break ;
+ case 'c' : if (uint0_scan(l.arg, &max)) break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (t) tain_from_millisecs(&defaulttimeout, t) ;
+ else defaulttimeout = tain_infinite_relative ;
+ if (max < 2) max = 2 ;
+ }
+
+ /* Init phase.
+ If something fails here, we can die, because it means that
+ something is seriously wrong with the system, and we can't
+ run correctly anyway.
+ */
+
+ if (argc && (chdir(argv[0]) < 0)) strerr_diefu1sys(111, "chdir") ;
+ x[1].fd = s6_supervise_lock(S6_SVSCAN_CTLDIR) ;
+ x[0].fd = selfpipe_init() ;
+ if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
+
+ if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ {
+ sigset_t set ;
+ sigemptyset(&set) ;
+ sigaddset(&set, SIGCHLD) ;
+ sigaddset(&set, SIGALRM) ;
+ sigaddset(&set, SIGTERM) ;
+ sigaddset(&set, SIGHUP) ;
+ sigaddset(&set, SIGQUIT) ;
+ sigaddset(&set, SIGABRT) ;
+ sigaddset(&set, SIGINT) ;
+ if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ;
+ }
+
+
+ {
+ struct svinfo blob[max] ; /* careful with that stack, Eugene */
+ services = blob ;
+ tain_now_g() ;
+
+
+ /* Loop phase.
+ From now on, we must not die.
+ Temporize on recoverable errors, and panic on serious ones. */
+
+ while (cont)
+ {
+ int r ;
+ tain_add_g(&deadline, &defaulttimeout) ;
+ reap() ;
+ scan() ;
+ killthem() ;
+ r = iopause_g(x, 2, &deadline) ;
+ if (r < 0) panic("iopause") ;
+ else if (!r) wantscan = 1 ;
+ else
+ {
+ if ((x[0].revents | x[1].revents) & IOPAUSE_EXCEPT)
+ {
+ errno = EIO ;
+ panic("check internal pipes") ;
+ }
+ if (x[0].revents & IOPAUSE_READ) handle_signals() ;
+ if (x[1].revents & IOPAUSE_READ) handle_control(x[1].fd) ;
+ }
+ }
+
+
+ /* Finish phase. */
+
+ selfpipe_finish() ;
+ killthem() ;
+ reap() ;
+ }
+ {
+ char const *eargv[3] = { FINISH_PROG, finish_arg, 0 } ;
+ execve(eargv[0], (char **)eargv, (char *const *)environ) ;
+ }
+ panicnosp("exec finish script " FINISH_PROG) ;
+}
diff --git a/src/supervision/s6-svscanctl.c b/src/supervision/s6-svscanctl.c
new file mode 100644
index 0000000..48e6420
--- /dev/null
+++ b/src/supervision/s6-svscanctl.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <s6/s6-supervise.h>
+
+#define USAGE "s6-svscanctl [ -phratszbnNiq0678 ] svscandir"
+
+int main (int argc, char const *const *argv)
+{
+ PROG = "s6-svscanctl" ;
+ return s6_svc_main(argc, argv, "phratszbnNiq0678", USAGE, ".s6-svscan") ;
+}
diff --git a/src/supervision/s6-svstat.c b/src/supervision/s6-svstat.c
new file mode 100644
index 0000000..c986b8d
--- /dev/null
+++ b/src/supervision/s6-svstat.c
@@ -0,0 +1,70 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <skalibs/uint64.h>
+#include <skalibs/uint.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/djbunix.h>
+#include <s6/s6-supervise.h>
+
+#define USAGE "s6-svstat servicedir"
+
+int main (int argc, char const *const *argv)
+{
+ s6_svstatus_t status ;
+ char fmt[UINT_FMT] ;
+ int isup, normallyup ;
+ PROG = "s6-svstat" ;
+ if (argc < 2) strerr_dieusage(100, USAGE) ;
+ argv++ ; argc-- ;
+ if (!s6_svstatus_read(*argv, &status))
+ strerr_diefu2sys(111, "read status for ", *argv) ;
+
+ {
+ struct stat st ;
+ unsigned int dirlen = str_len(*argv) ;
+ char fn[dirlen + 6] ;
+ byte_copy(fn, dirlen, *argv) ;
+ byte_copy(fn + dirlen, 6, "/down") ;
+ if (stat(fn, &st) == -1)
+ if (errno != ENOENT) strerr_diefu2sys(111, "stat ", fn) ;
+ else normallyup = 1 ;
+ else normallyup = 0 ;
+ }
+
+ tain_now_g() ;
+ if (tain_future(&status.stamp)) tain_copynow(&status.stamp) ;
+ tain_sub(&status.stamp, &STAMP, &status.stamp) ;
+
+ isup = status.pid && !status.flagfinishing ;
+ if (isup)
+ {
+ buffer_putnoflush(buffer_1small,"up (pid ", 8) ;
+ buffer_putnoflush(buffer_1small, fmt, uint_fmt(fmt, status.pid)) ;
+ buffer_putnoflush(buffer_1small, ") ", 2) ;
+ }
+ else buffer_putnoflush(buffer_1small, "down ", 5) ;
+
+ buffer_putnoflush(buffer_1small, fmt, uint64_fmt(fmt, status.stamp.sec.x)) ;
+ buffer_putnoflush(buffer_1small," seconds", 8) ;
+
+ if (isup && !normallyup)
+ buffer_putnoflush(buffer_1small, ", normally down", 15) ;
+ if (!isup && normallyup)
+ buffer_putnoflush(buffer_1small, ", normally up", 13) ;
+ if (isup && status.flagpaused)
+ buffer_putnoflush(buffer_1small, ", paused", 8) ;
+ if (!isup && (status.flagwant == 'u'))
+ buffer_putnoflush(buffer_1small, ", want up", 10) ;
+ if (isup && (status.flagwant == 'd'))
+ buffer_putnoflush(buffer_1small, ", want down", 12) ;
+
+ if (buffer_putflush(buffer_1small, "\n", 1) < 0)
+ strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/supervision/s6-svwait.c b/src/supervision/s6-svwait.c
new file mode 100644
index 0000000..0d7c96c
--- /dev/null
+++ b/src/supervision/s6-svwait.c
@@ -0,0 +1,104 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint.h>
+#include <skalibs/bitarray.h>
+#include <skalibs/tai.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/iopause.h>
+#include <s6/ftrigr.h>
+#include <s6/s6-supervise.h>
+
+#define USAGE "s6-svwait [ -U | -u | -d ] [ -A | -a | -o ] [ -t timeout ] servicedir..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static inline int check (unsigned char const *ba, unsigned int n, int wantup, int or)
+{
+ return (bitarray_first(ba, n, or == wantup) < n) == or ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ tain_t deadline, tto ;
+ ftrigr_t a = FTRIGR_ZERO ;
+ uint32 options = FTRIGR_REPEAT ;
+ int or = 0 ;
+ int wantup = 1 ;
+ char re[4] = "u|d" ;
+ PROG = "s6-svwait" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ unsigned int t = 0 ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "uUdAaot:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'U' : wantup = 1 ; re[0] = 'U' ; break ;
+ case 'u' : wantup = 1 ; re[0] = 'u' ; break ;
+ case 'd' : wantup = 0 ; break ;
+ case 'A' : or = 0 ; options |= FTRIGR_REPEAT ; break ;
+ case 'a' : or = 0 ; options &= ~FTRIGR_REPEAT ; break ;
+ case 'o' : or = 1 ; options &= ~FTRIGR_REPEAT ; break ;
+ case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ;
+ }
+ if (!argc) dieusage() ;
+
+ tain_now_g() ;
+ tain_add_g(&deadline, &tto) ;
+
+ if (!ftrigr_startf_g(&a, &deadline)) strerr_diefu1sys(111, "ftrigr_startf") ;
+
+ {
+ iopause_fd x = { -1, IOPAUSE_READ, 0 } ;
+ unsigned int i = 0 ;
+ uint16 list[argc] ;
+ unsigned char states[bitarray_div8(argc)] ;
+ x.fd = ftrigr_fd(&a) ;
+ for (; i < (unsigned int)argc ; i++)
+ {
+ unsigned int len = str_len(argv[i]) ;
+ char s[len + 1 + sizeof(S6_SUPERVISE_EVENTDIR)] ;
+ byte_copy(s, len, argv[i]) ;
+ s[len] = '/' ;
+ byte_copy(s + len + 1, sizeof(S6_SUPERVISE_EVENTDIR), S6_SUPERVISE_EVENTDIR) ;
+ list[i] = ftrigr_subscribe_g(&a, s, re, options, &deadline) ;
+ if (!list[i]) strerr_diefu2sys(111, "ftrigr_subscribe to ", argv[i]) ;
+ }
+
+ for (i = 0 ; i < (unsigned int)argc ; i++)
+ {
+ s6_svstatus_t st = S6_SVSTATUS_ZERO ;
+ if (!s6_svstatus_read(argv[i], &st)) strerr_diefu1sys(111, "s6_svstatus_read") ;
+ bitarray_poke(states, i, !!st.pid) ;
+ }
+
+ for (;;)
+ {
+ if (check(states, argc, wantup, or)) break ;
+ {
+ register int r = iopause_g(&x, 1, &deadline) ;
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+ else if (!r) strerr_dief1x(1, "timed out") ;
+ }
+
+ if (ftrigr_update(&a) < 0) strerr_diefu1sys(111, "ftrigr_update") ;
+ for (i = 0 ; i < (unsigned int)argc ; i++)
+ {
+ char what ;
+ register int r = ftrigr_check(&a, list[i], &what) ;
+ if (r < 0) strerr_diefu1sys(111, "ftrigr_check") ;
+ if (r) bitarray_poke(states, i, what == re[0]) ;
+ }
+ }
+ }
+ return 0 ;
+}