about summary refs log tree commit diff
path: root/src/minutils/s6-uevent-spawner.c
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2015-03-09 17:21:12 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2015-03-09 17:21:12 +0000
commit496b7282f2b8fb56cc88d7113202ef0e1d9ce22c (patch)
treec8601454f83c5bc44a721a3ca687735632b046dd /src/minutils/s6-uevent-spawner.c
parentff8d077d36a666cba38bf1509e4d55d8f2fd4812 (diff)
downloads6-linux-utils-496b7282f2b8fb56cc88d7113202ef0e1d9ce22c.tar.gz
s6-linux-utils-496b7282f2b8fb56cc88d7113202ef0e1d9ce22c.tar.xz
s6-linux-utils-496b7282f2b8fb56cc88d7113202ef0e1d9ce22c.zip
- Added s6-uevent-listener and s6-uevent-spawner
 - s6-devd is now a wrapper around the former two
 - version: bump to 2.0.2.0, still missing s6-devd doc
Diffstat (limited to 'src/minutils/s6-uevent-spawner.c')
-rw-r--r--src/minutils/s6-uevent-spawner.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/src/minutils/s6-uevent-spawner.c b/src/minutils/s6-uevent-spawner.c
new file mode 100644
index 0000000..b074f1f
--- /dev/null
+++ b/src/minutils/s6-uevent-spawner.c
@@ -0,0 +1,234 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <spawn.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <skalibs/config.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/uint.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/env.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/sig.h>
+#include <skalibs/selfpipe.h>
+#include <skalibs/skamisc.h>
+
+#define USAGE "s6-uevent-spawner [ -v verbosity ] [ -l linevar ] [ -t maxlife:maxterm:maxkill ] helperprogram..."
+#define dieusage() strerr_dieusage(100, USAGE)
+#define dienomem() strerr_diefu1sys(111, "build string") ;
+
+static unsigned int cont = 1, state = 0, verbosity = 1 ;
+static pid_t pid ;
+static tain_t lifetto = TAIN_INFINITE_RELATIVE,
+              termtto = TAIN_INFINITE_RELATIVE,
+              killtto = TAIN_INFINITE_RELATIVE,
+              deadline ;
+
+static inline void on_death (void)
+{
+  pid = 0 ;
+  state = 0 ;
+  tain_add_g(&deadline, &tain_infinite_relative) ;
+}
+
+static inline void on_event (char const *const *argv, char const *const *envp, char const *s, unsigned int len)
+{
+  posix_spawnattr_t attr ;
+  posix_spawn_file_actions_t actions ;
+  unsigned int envlen = env_len(envp) ;
+  unsigned int n = envlen + 1 + byte_count(s, len, '\0') ;
+  int e ;
+  char const *v[n] ;
+  if (!env_merge(v, n, envp, envlen, s, len))
+    strerr_diefu1sys(111, "env_merge") ;
+
+  e = posix_spawnattr_init(&attr) ;
+  if (e) { errno = e ; strerr_diefu1sys(111, "posix_spawnattr_init") ; }
+  {
+    sigset_t set ;
+    sigemptyset(&set) ;
+    e = posix_spawnattr_setsigmask(&attr, &set) ;
+    if (e) { errno = e ; strerr_diefu1sys(111, "posix_spawnattr_setsigmask") ; }
+    sigfillset(&set) ;
+    e = posix_spawnattr_setsigdefault(&attr, &set) ;
+    if (e) { errno = e ; strerr_diefu1sys(111, "posix_spawnattr_setsigdefault") ; }
+  }
+  e = posix_spawn_file_actions_init(&actions) ;
+  if (e) { errno = e ; strerr_diefu1sys(111, "posix_spawn_file_actions_init") ; }
+  e = posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_RDONLY, S_IRUSR) ;
+  if (e) { errno = e ; strerr_diefu1sys(111, "posix_spawn_file_actions_addopen") ; }
+  e = posix_spawnp(&pid, argv[0], &actions, &attr, (char *const *)argv, (char * const *)v) ;
+  if (e) { errno = e ; strerr_diefu2sys(111, "spawn ", argv[0]) ; }
+  posix_spawn_file_actions_destroy(&actions) ;
+  posix_spawnattr_destroy(&attr) ;
+  state = 1 ;
+  tain_add_g(&deadline, &lifetto) ;
+}
+
+static inline void handle_timeout (void)
+{
+  switch (state)
+  {
+    case 0 :
+      tain_add_g(&deadline, &tain_infinite_relative) ;
+      break ;
+    case 1 :
+      kill(pid, SIGTERM) ;
+      tain_add_g(&deadline, &termtto) ;
+      state++ ;
+      break ;
+    case 2 :
+      kill(pid, SIGKILL) ;
+      tain_add_g(&deadline, &killtto) ;
+      state++ ;
+      break ;
+    case 3 :
+      strerr_dief1x(99, "child resisted SIGKILL - check your kernel logs.") ;
+    default :
+      strerr_dief1x(101, "internal error: inconsistent state. Please submit a bug-report.") ;
+  }
+}
+
+static inline void handle_signals (void)
+{
+  for (;;)
+  {
+    char c = selfpipe_read() ;
+    switch (c)
+    {
+      case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
+      case 0 : return ;
+      case SIGCHLD :
+        if (!pid) wait_reap() ;
+        else
+        {
+          int wstat ;
+          int r = wait_pid_nohang(pid, &wstat) ;
+          if (r < 0)
+            if (errno != ECHILD) strerr_diefu1sys(111, "wait_pid_nohang") ;
+            else break ;
+          else if (!r) break ;
+          on_death() ;
+        }
+        break ;
+      default :
+        strerr_dief1x(101, "internal error: inconsistent signal handling. Please submit a bug-report.") ;
+    }
+  }
+}
+
+static inline void handle_stdin (stralloc *sa, char const *linevar, char const *const *argv, char const *const *envp)
+{
+  for (;;)
+  {
+    unsigned int start ;
+    register int r ;
+    if (!sa->len && linevar)
+      if (!stralloc_cats(sa, linevar) || !stralloc_catb(sa, "=", 1))
+        dienomem() ;
+    start = sa->len ;
+    r = sanitize_read(skagetln(buffer_0, sa, 0)) ;
+    if (r < 0)
+    {
+      cont = 0 ;
+      if (errno != EPIPE && verbosity) strerr_warnwu1sys("read from stdin") ;
+    }
+    if (r <= 0) break ;
+    if (sa->len == start + 1)
+    {
+      start = linevar ? 0 : str_len(sa->s) + 1 ;
+      if (start >= sa->len)
+      {
+        if (verbosity) strerr_warnw1x("empty event!") ;
+      }
+      else on_event(argv, envp, sa->s + start, sa->len - 1 - start) ;
+      sa->len = 0 ;
+    }
+  }
+}
+
+static inline int make_ttos (char const *s)
+{
+  unsigned int tlife = 0, tterm = 0, tkill = 0, pos = 0 ;
+  pos += uint_scan(s + pos, &tlife) ;
+  if (s[pos] && s[pos++] != ':') return 0 ;
+  if (!tlife) return 1 ;
+  tain_from_millisecs(&lifetto, tlife) ;
+  pos += uint_scan(s + pos, &tterm) ;
+  if (s[pos] && s[pos++] != ':') return 0 ;
+  if (!tterm) return 1 ;
+  tain_from_millisecs(&termtto, tterm) ;
+  tain_add(&termtto, &termtto, &lifetto) ;
+  pos += uint_scan(s + pos, &tkill) ;
+  if (s[pos]) return 0 ;
+  if (!tkill) return 1 ;
+  tain_from_millisecs(&killtto, tkill) ;
+  tain_add(&killtto, &killtto, &termtto) ;
+  return 1 ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+  iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .fd = 0, .events = IOPAUSE_READ } } ;
+  char const *linevar = 0 ;
+  stralloc sa = STRALLOC_ZERO ;
+  PROG = "s6-uevent-spawner" ;
+  {
+    subgetopt_t l = SUBGETOPT_ZERO ;
+    for (;;)
+    {
+      register int opt = subgetopt_r(argc, argv, "l:v:t:", &l) ;
+      if (opt == -1) break ;
+      switch (opt)
+      {
+        case 'l' : linevar = optarg ; break ;
+        case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ;
+        case 't' : if (!make_ttos(l.arg)) dieusage() ; break ;
+        default : dieusage() ;
+      }
+    }
+    argc -= l.ind ; argv += l.ind ;
+    if (!argc) strerr_dieusage(100, USAGE) ;
+  }
+  if (linevar && linevar[str_chr(linevar, '=')])
+    strerr_dief2x(100, "invalid variable: ", linevar) ;
+
+  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") ;
+  if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "trap SIGCHLD") ;
+  if (setenv("PATH", SKALIBS_DEFAULTPATH, 0) < 0)
+    strerr_diefu1sys(111, "setenv PATH") ;
+
+  tain_now_g() ;
+  tain_add_g(&deadline, &tain_infinite_relative) ;
+  if (verbosity >= 2) strerr_warni1x("starting") ;
+
+  while (cont || pid)
+  {
+    register int r = iopause_g(x, 1 + (!pid && cont), &deadline) ;
+    if (r < 0) strerr_diefu1sys(111, "iopause") ;
+    else if (!r) handle_timeout() ;
+    else
+    {
+      if (x[0].revents & IOPAUSE_EXCEPT)
+        strerr_diefu1x(111, "iopause: trouble with selfpipe") ;
+      if (x[0].revents & IOPAUSE_READ) handle_signals() ;
+      else if (!pid && cont && (x[1].revents & IOPAUSE_READ))
+        handle_stdin(&sa, linevar, argv, envp) ;
+    }
+  }
+  if (verbosity >= 2) strerr_warni1x("exiting") ;
+  return 0 ;
+}