summaryrefslogtreecommitdiff
path: root/src/daemontools-extras
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2014-12-05 22:26:11 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2014-12-05 22:26:11 +0000
commit90b12bd71bb9fc79a4640b9112c13ef529d0196a (patch)
tree523b3f4ee2969e7a729bab2ba749c4b924ae62af /src/daemontools-extras
downloads6-90b12bd71bb9fc79a4640b9112c13ef529d0196a.tar.xz
Initial commit
Diffstat (limited to 'src/daemontools-extras')
-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
24 files changed, 1988 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 ;
+}