diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2014-12-05 22:26:11 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2014-12-05 22:26:11 +0000 |
commit | 90b12bd71bb9fc79a4640b9112c13ef529d0196a (patch) | |
tree | 523b3f4ee2969e7a729bab2ba749c4b924ae62af /src | |
download | s6-90b12bd71bb9fc79a4640b9112c13ef529d0196a.tar.xz |
Initial commit
Diffstat (limited to 'src')
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 ; +} |