summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--INSTALL2
-rw-r--r--doc/index.html6
-rw-r--r--doc/s6-devd.html4
-rw-r--r--doc/s6-uevent-listener.html99
-rw-r--r--doc/s6-uevent-spawner.html79
-rw-r--r--doc/upgrade.html11
-rw-r--r--package/deps.mak8
-rw-r--r--package/info2
-rw-r--r--package/modes30
-rw-r--r--package/targets.mak2
-rw-r--r--src/minutils/deps-exe/s6-uevent-listener1
-rw-r--r--src/minutils/deps-exe/s6-uevent-spawner1
-rw-r--r--src/minutils/s6-devd.c297
-rw-r--r--src/minutils/s6-uevent-listener.c233
-rw-r--r--src/minutils/s6-uevent-spawner.c234
15 files changed, 747 insertions, 262 deletions
diff --git a/INSTALL b/INSTALL
index 641c8f9..99ee0a1 100644
--- a/INSTALL
+++ b/INSTALL
@@ -6,7 +6,7 @@ Build Instructions
- A Linux-based system with a standard C development environment
- GNU make version 4.0 or later
- - skalibs version 2.3.1.0 or later: http://skarnet.org/software/skalibs/
+ - skalibs version 2.3.1.2 or later: http://skarnet.org/software/skalibs/
This software is Linux-specific. It will run on a Linux kernel,
version 2.6.32 or later.
diff --git a/doc/index.html b/doc/index.html
index a899be8..a0fe7d7 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -32,7 +32,7 @@
<li> A Linux-based system with a standard C development environment </li>
<li> GNU make, version 4.0 or later </li>
<li> <a href="http://skarnet.org/software/skalibs/">skalibs</a> version
-2.3.1.0 or later. It's a build-time requirement. It's also a run-time
+2.3.1.2 or later. It's a build-time requirement. It's also a run-time
requirement if you link against the shared version of the skalibs
library. </li>
</ul>
@@ -47,7 +47,7 @@ library. </li>
<h3> Download </h3>
<ul>
- <li> The current released version of s6-linux-utils is <a href="s6-linux-utils-2.0.1.0.tar.gz">2.0.1.0</a>. </li>
+ <li> The current released version of s6-linux-utils is <a href="s6-linux-utils-2.0.2.0.tar.gz">2.0.2.0</a>. </li>
<li> Alternatively, you can checkout a copy of the s6-linux-utils git repository:
<pre> git clone git://git.skarnet.org/s6-linux-utils </pre> </li>
</ul>
@@ -79,6 +79,8 @@ the previous versions of s6-linux-utils and the current one. </li>
<ul>
<li><a href="s6-chroot.html">The <tt>s6-chroot</tt> program</a></li>
<li><a href="s6-devd.html">The <tt>s6-devd</tt> program</a></li>
+<li><a href="s6-uevent-listener.html">The <tt>s6-uevent-listener</tt> program</a></li>
+<li><a href="s6-uevent-spawner.html">The <tt>s6-uevent-spawner</tt> program</a></li>
<li><a href="s6-freeramdisk.html">The <tt>s6-freeramdisk</tt> program</a></li>
<li><a href="s6-halt.html">The <tt>s6-halt</tt> program</a></li>
<li><a href="s6-hostname.html">The <tt>s6-hostname</tt> program</a></li>
diff --git a/doc/s6-devd.html b/doc/s6-devd.html
index 5ae6160..c67429a 100644
--- a/doc/s6-devd.html
+++ b/doc/s6-devd.html
@@ -36,7 +36,7 @@ hotplug events, as the <em>udev</em> program does. </li>
the event variables added to the environment, just as if <em>prog...</em>
had been registered in <tt>/proc/sys/kernel/hotplug</tt>. </li>
<li> However, unlike the kernel, s6-devd spawns the <em>prog...</em> helpers
-sequentially: it waits for an instance to finish before spawning another one. </li>
+sequentially: it waits for an instance to finish before spawning another one. </li>
<li> s6-devd is a long-lived program; it exits 0 when it receives a
SIGTERM. If a helper program is alive at that time, s6-devd waits for it to die
before exiting. </li>
@@ -68,7 +68,7 @@ fork and logs to stderr.) </li>
even if you are not using the <tt>-t</tt> option to s6-devd. Since helpers are
spawned sequentially, slow helpers can make events queue up and fill the netlink
kernel buffer. </li>
- <li> If you are using <a href="http://busybox.net/">busybox</a> and want a
+ <li> If you are using <a href="http://busybox.net/">busybox</a> and want a
minimal udev-style dynamic <tt>/dev</tt>
handling, <tt>/sbin/mdev</tt> is a suitable <em>prog...</em> helper. </li>
<li> The point of s6-devd is that it runs the helpers sequentially, so it solves
diff --git a/doc/s6-uevent-listener.html b/doc/s6-uevent-listener.html
new file mode 100644
index 0000000..9050e9f
--- /dev/null
+++ b/doc/s6-uevent-listener.html
@@ -0,0 +1,99 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-utils: the s6-uevent-listener program</title>
+ <meta name="Description" content="s6-linux-utils: the s6-uevent-listener program" />
+ <meta name="Keywords" content="s6 linux administration root utilities devd mdev udev s6-uevent-listener uevent" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-utils</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-uevent-listener</tt> program </h1>
+
+<p>
+<tt>s6-uevent-listener</tt> spawns a long-lived helper program.
+It then listens to the netlink interface for uevents (also called
+"hotplug" or "udev" events), and passes those events to the
+helper program's standard input, using a simple format.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-uevent-listener [ -v <em>verbosity</em> ] [ -b kbufsz ] <em>prog...</em>
+</pre>
+
+<ul>
+ <li> s6-uevent-listener spawns <em>prog...</em> with a pipe writing to
+<em>prog</em>' stdin. </li>
+ <li> s6-uevent-listener binds to the netlink interface and listens for
+hotplug events, as the <em>udev</em> program does. </li>
+ <li> It tranmits event information to <em>prog</em> via the pipe. </li>
+ <li> s6-uevent-listener, as well as <em>prog</em>, are long-lived programs.
+When it receives a SIGTERM, s6-uevent-listener stops listening; it will
+exit as soon as it has flushed its event queue to <em>prog</em>. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-v</tt>&nbsp;<em>verbosity</em>&nbsp;: be more or less verbose.
+Default verbosity is 1. 0 will only print fatal error messages, 3 will
+print warnings every time the netlink interface sends something
+unexpected. </li>
+ <li> <tt>-b</tt>&nbsp;<em>kbufsz</em>&nbsp;: try and reserve a kernel buffer of
+<em>kbufsz</em> bytes for the netlink queue. Too large a buffer wastes kernel memory;
+too small a buffer risks losing events. The default is 65536. </li>
+</ul>
+
+<h2> Protocol </h2>
+
+<ul>
+ <li> <em>prog</em> should read a series of events on its stdin, and exit
+on EOF. </li>
+ <li> An event is a series of null-terminated strings as they are sent by
+the kernel to the netlink; s6-uevent-listener adds a final empty string
+(i.e. an additional null character) to mark the end of the series. </li>
+ <li> The first string is a short description of the event; it normally
+contains the string "@/". Other strings after the first are of the form
+"VARIABLE=value", and describe the environment which a hotplug helper
+for the event (registered in <tt>/proc/sys/kernel/hotplug</tt>) would be
+spawned with. </li>
+ <li> Example (newlines added for clarity): <pre>
+add@/class/input/input9/mouse2\0
+ACTION=add\0
+DEVPATH=/class/input/input9/mouse2\0
+SUBSYSTEM=input\0
+SEQNUM=106\0
+PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.1/usb2/2­2/2­2:1.0
+PHYSDEVBUS=usb\0
+PHYSDEVDRIVER=usbhid\0
+MAJOR=13\0
+MINOR=34\0
+\0 </pre> </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> s6-uevent-listener is a daemon; it should be run under a proper supervision system such
+as <a href="http://skarnet.org/software/s6/">s6</a>. </li>
+<li> If you are running s6-uevent-listener, <em>prog...</em> should be the
+only program handling uevents, which means that
+<tt>/proc/sys/kernel/hotplug</tt> should be empty. </li>
+ <li> If you want the serialization benefit of the netlink, but still
+want to spawn a program such as <a href="http://busybox.net/">busybox</a>'s
+or <a href="http://landley.net/toybox/">toybox</a>'s <tt>mdev</tt>, use
+<tt><a href="s6-uevent-spawner.html">s6-uevent-spawner</a> mdev</tt> as
+s6-uevent-listener's <em>prog</em>. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-uevent-spawner.html b/doc/s6-uevent-spawner.html
new file mode 100644
index 0000000..e9be726
--- /dev/null
+++ b/doc/s6-uevent-spawner.html
@@ -0,0 +1,79 @@
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-utils: the s6-uevent-spawner program</title>
+ <meta name="Description" content="s6-linux-utils: the s6-uevent-spawner program" />
+ <meta name="Keywords" content="s6 linux administration root utilities devd mdev udev hotplug" />
+ <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-utils</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-uevent-spawner</tt> program </h1>
+
+<p>
+<tt>s6-uevent-spawner</tt> listens to its standard input for
+netlink-style events, and launches a helper program for every event,
+similarly to what the hotplug interface does.
+</p>
+
+<p>
+ It is meant to be used together with
+<a href="s6-uevent-listener.html">s6-uevent-listener</a>.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-uevent-spawner [ -v <em>verbosity</em> ] [ -l <em>linevar</em> ] [ -t <em>l</em>:<em>t</em>:<em>k</em> ] <em>prog...</em>
+</pre>
+
+<ul>
+ <li> s6-uevent-spawner listens to its stdin for a series of strings
+representing kernel uevents, as formatted by
+<a href="s6-uevent-listener.html">s6-uevent-listener</a>. </li>
+ <li> For every event it reads, it spawns <em>prog...</em> with
+the event variables added to the environment, just as if <em>prog...</em>
+had been registered in <tt>/proc/sys/kernel/hotplug</tt>. However,
+it reads the events sequentially, and waits for a <em>prog</em> instance
+to finish before spawning another one. </li>
+ <li> When s6-uevent-spawner receives EOF: if an instance of
+<em>prog</em> is alive, it first waits for it to die. Then it exits
+0. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-v</tt>&nbsp;<em>verbosity</em>&nbsp;: be more or less verbose.
+Default verbosity is 1. 0 only prints fatal error messages. 3 is too much. </li>
+ <li> <tt>-l</tt> <em>linevar</em>&nbsp;: make the event description
+(i.e. the first string in the netlink message announcing the event)
+available in <em>prog</em> under the environment variable <em>linevar</em>.
+By default, this first string is ignored - it is not needed, all the
+event information is already in the other variables. </li>
+ <li> <tt>-t</tt> <em>l:t:k</em>&nbsp;: If <em>l</em>, <em>t</em> or <em>k</em> is
+specified, they specify timeouts; by default, they are infinite.
+If <em>prog...</em> is still alive after <em>l</em> milliseconds, s6-devd sends
+it a SIGTERM. Then, if <em>prog...</em> is still alive after <em>t</em> more
+milliseconds, s6-devd sends it a SIGKILL. Then, if <em>prog...</em> is still
+alive after <em>k</em> more milliseconds, s6-uevent-spawner yells and exits 99. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> The <em>prog...</em> helper should be very short-lived,
+even if you are not using the <tt>-t</tt> option to s6-devd. Since helpers are
+spawned sequentially, slow helpers can make events queue up and fill up
+buffers. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index b9dcfeb..847324d 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -17,6 +17,17 @@
<h1> What has changed in s6-linux-utils </h1>
+<h2> in 2.0.2.0 </h2>
+
+<ul>
+ <li> skalibs dependency bumped to 2.3.1.1 </li>
+ <li> New commands:
+<a href="s6-uevent-listener.html">s6-uevent-listener</a> and
+<a href="s6-uevent-spawner.html">s6-uevent-spawner</a> <li>
+ <li> <a href="s6-devd.html">s6-devd</a> is now a wrapper
+around those commands. </li>
+</ul>
+
<h2> in 2.0.1.0 </h2>
<ul>
diff --git a/package/deps.mak b/package/deps.mak
index c85b123..c013b69 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -3,7 +3,7 @@
#
src/minutils/s6-chroot.o src/minutils/s6-chroot.lo: src/minutils/s6-chroot.c
-src/minutils/s6-devd.o src/minutils/s6-devd.lo: src/minutils/s6-devd.c
+src/minutils/s6-devd.o src/minutils/s6-devd.lo: src/minutils/s6-devd.c src/include/s6-linux-utils/config.h
src/minutils/s6-freeramdisk.o src/minutils/s6-freeramdisk.lo: src/minutils/s6-freeramdisk.c
src/minutils/s6-halt.o src/minutils/s6-halt.lo: src/minutils/s6-halt.c
src/minutils/s6-hiercopy.o src/minutils/s6-hiercopy.lo: src/minutils/s6-hiercopy.c
@@ -16,6 +16,8 @@ src/minutils/s6-ps.o src/minutils/s6-ps.lo: src/minutils/s6-ps.c src/minutils/s6
src/minutils/s6-reboot.o src/minutils/s6-reboot.lo: src/minutils/s6-reboot.c
src/minutils/s6-swapoff.o src/minutils/s6-swapoff.lo: src/minutils/s6-swapoff.c
src/minutils/s6-swapon.o src/minutils/s6-swapon.lo: src/minutils/s6-swapon.c
+src/minutils/s6-uevent-listener.o src/minutils/s6-uevent-listener.lo: src/minutils/s6-uevent-listener.c
+src/minutils/s6-uevent-spawner.o src/minutils/s6-uevent-spawner.lo: src/minutils/s6-uevent-spawner.c
src/minutils/s6-umount.o src/minutils/s6-umount.lo: src/minutils/s6-umount.c
src/minutils/s6ps_grcache.o src/minutils/s6ps_grcache.lo: src/minutils/s6ps_grcache.c src/minutils/s6-ps.h
src/minutils/s6ps_otree.o src/minutils/s6ps_otree.lo: src/minutils/s6ps_otree.c src/minutils/s6-ps.h
@@ -51,5 +53,9 @@ s6-swapoff: private EXTRA_LIBS :=
s6-swapoff: src/minutils/s6-swapoff.o -lskarnet
s6-swapon: private EXTRA_LIBS :=
s6-swapon: src/minutils/s6-swapon.o -lskarnet
+s6-uevent-listener: private EXTRA_LIBS :=
+s6-uevent-listener: src/minutils/s6-uevent-listener.o -lskarnet
+s6-uevent-spawner: private EXTRA_LIBS :=
+s6-uevent-spawner: src/minutils/s6-uevent-spawner.o -lskarnet
s6-umount: private EXTRA_LIBS :=
s6-umount: src/minutils/s6-umount.o -lskarnet
diff --git a/package/info b/package/info
index f55a688..79f07d5 100644
--- a/package/info
+++ b/package/info
@@ -1,4 +1,4 @@
package=s6-linux-utils
-version=2.0.1.0
+version=2.0.2.0
category=admin
package_macro_name=S6_LINUX_UTILS
diff --git a/package/modes b/package/modes
index 9a1c3b1..3905269 100644
--- a/package/modes
+++ b/package/modes
@@ -1,14 +1,16 @@
-s6-chroot 0700
-s6-devd 0700
-s6-freeramdisk 0700
-s6-halt 0700
-s6-hostname 0755
-s6-logwatch 0755
-s6-mount 0700
-s6-pivotchroot 0700
-s6-poweroff 0700
-s6-ps 0755
-s6-reboot 0700
-s6-swapoff 0700
-s6-swapon 0700
-s6-umount 0700
+s6-chroot 0700
+s6-devd 0700
+s6-uevent-listener 0700
+s6-uevent-spawner 0755
+s6-freeramdisk 0700
+s6-halt 0700
+s6-hostname 0755
+s6-logwatch 0755
+s6-mount 0700
+s6-pivotchroot 0700
+s6-poweroff 0700
+s6-ps 0755
+s6-reboot 0700
+s6-swapoff 0700
+s6-swapon 0700
+s6-umount 0700
diff --git a/package/targets.mak b/package/targets.mak
index f03db13..14bc019 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -1,6 +1,8 @@
BIN_TARGETS := \
s6-chroot \
s6-devd \
+s6-uevent-listener \
+s6-uevent-spawner \
s6-freeramdisk \
s6-halt \
s6-hostname \
diff --git a/src/minutils/deps-exe/s6-uevent-listener b/src/minutils/deps-exe/s6-uevent-listener
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/minutils/deps-exe/s6-uevent-listener
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/minutils/deps-exe/s6-uevent-spawner b/src/minutils/deps-exe/s6-uevent-spawner
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/minutils/deps-exe/s6-uevent-spawner
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/minutils/s6-devd.c b/src/minutils/s6-devd.c
index f51550c..9df5c80 100644
--- a/src/minutils/s6-devd.c
+++ b/src/minutils/s6-devd.c
@@ -1,281 +1,96 @@
/* ISC license. */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <spawn.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <errno.h>
-#include <skalibs/allreadwrite.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/djbunix.h>
-#include <skalibs/sig.h>
-#include <skalibs/selfpipe.h>
+#include <s6-linux-utils/config.h>
-#define USAGE "s6-devd [ -q | -v ] [ -b kbufsz ] [ -t maxlife:maxterm:maxkill ] helperprogram..."
+#define USAGE "s6-devd [ -q | -v ] [ -b kbufsz ] [ -l linevar ] [ -t maxlife:maxterm:maxkill ] helperprogram..."
#define dieusage() strerr_dieusage(100, USAGE)
-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 int fd_recvmsg (int fd, struct msghdr *hdr)
-{
- int r ;
- do r = recvmsg(fd, hdr, 0) ;
- while ((r == -1) && (errno == EINTR)) ;
- return r ;
-}
-
-static inline int netlink_init (unsigned int kbufsz)
-{
- struct sockaddr_nl nl = { .nl_family = AF_NETLINK, .nl_pad = 0, .nl_groups = 1, .nl_pid = 0 } ;
- int fd = socket_internal(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT, DJBUNIX_FLAG_NB|DJBUNIX_FLAG_COE) ;
- if (fd < 0) return -1 ;
- if (bind(fd, (struct sockaddr *)&nl, sizeof(struct sockaddr_nl)) < 0) goto err ;
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &kbufsz, sizeof(unsigned int)) < 0) goto err ;
- return fd ;
- err:
- {
- register int e = errno ;
- fd_close(fd) ;
- errno = e ;
- }
- return -1 ;
-}
-
-static inline void on_death (void)
-{
- pid = 0 ;
- state = 0 ;
- tain_add_g(&deadline, &tain_infinite_relative) ;
- if (cont == 2) cont = 0 ;
-}
-
-static inline void on_event (char const *const *argv, char const *const *envp, char const *s, unsigned int len)
-{
- 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_spawnp(&pid, argv[0], 0, 0, (char *const *)argv, (char * const *)v) ;
- if (e) { errno = e ; strerr_diefu2sys(111, "spawn ", argv[0]) ; }
- state = 1 ;
- tain_add_g(&deadline, &lifetto) ;
-}
-
-static inline void handle_timeout (void)
+static inline int check_targ (char const *s)
{
- 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 SIGTERM :
- cont = pid ? 2 : 0 ;
- break ;
- 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 state. Please submit a bug-report.") ;
- }
- }
-}
-
-static inline void handle_netlink (int fd, char const *const *argv, char const *const *envp)
-{
- char buf[4096] ;
- int r ;
- {
- struct sockaddr_nl nl;
- struct iovec iov = { .iov_base = &buf, .iov_len = sizeof(buf) } ;
- char ctlmsg[CMSG_SPACE(sizeof(struct ucred))] ;
- struct msghdr msg = {
- .msg_name = &nl,
- .msg_namelen = sizeof(struct sockaddr_nl),
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = ctlmsg,
- .msg_controllen = sizeof(ctlmsg),
- .msg_flags = 0
- } ;
- r = sanitize_read(fd_recvmsg(fd, &msg)) ;
- if (r < 0)
- {
- if (errno == EPIPE)
- {
- if (verbosity >= 2) strerr_warnw1x("received EOF on netlink") ;
- cont = 0 ;
- return ;
- }
- else strerr_diefu1sys(111, "receive netlink message") ;
- }
- if (!r) return ;
- if (r < 32 || r > 4096)
- {
- if (verbosity >= 2)
- strerr_warnw2x("received and ignored netlink message ", "with invalid length") ;
- return ;
- }
- if (nl.nl_pid)
- {
- if (verbosity >= 3)
- {
- char fmt[UINT_FMT] ;
- fmt[uint_fmt(fmt, nl.nl_pid)] = 0 ;
- strerr_warnw3x("received and ignored netlink message ", "from userspace process ", fmt) ;
- }
- return ;
- }
- }
- {
- unsigned int start = str_len(buf) + 1 ;
- if (start < 5 || start > (unsigned int)r)
- {
- if (verbosity >= 2)
- strerr_warnw3x("received and ignored netlink message ", "with invalid header", " length") ;
- return ;
- }
- if (str_strn(buf, start, "@/", 2) >= start)
- {
- if (verbosity >= 2)
- strerr_warnw2x("received and ignored netlink message ", "with invalid header") ;
- return ;
- }
- on_event(argv, envp, buf + start, r - start) ;
- }
-}
-
-static inline int make_ttos (char const *s)
-{
- unsigned int tlife = 0, tterm = 0, tkill = 0, pos = 0 ;
- pos += uint_scan(s + pos, &tlife) ;
+ unsigned int t = 0, pos = 0 ;
+ pos += uint_scan(s + pos, &t) ;
if (s[pos] && s[pos++] != ':') return 0 ;
- if (!tlife) return 1 ;
- tain_from_millisecs(&lifetto, tlife) ;
- pos += uint_scan(s + pos, &tterm) ;
+ if (!t) return 1 ;
+ pos += uint_scan(s + pos, &t) ;
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 (!t) return 1 ;
+ pos += uint_scan(s + pos, &t) ;
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] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ, 0 } } ;
+ unsigned int kbufsz = 65536, verbosity = 1 ;
+ char const *linevar = 0 ;
+ char const *targ = 0 ;
PROG = "s6-devd" ;
{
- unsigned int kbufsz = 65536 ;
subgetopt_t l = SUBGETOPT_ZERO ;
for (;;)
{
- register int opt = subgetopt_r(argc, argv, "qvb:t:", &l) ;
+ register int opt = subgetopt_r(argc, argv, "qvb:l:t:", &l) ;
if (opt == -1) break ;
switch (opt)
{
case 'q' : if (verbosity) verbosity-- ; break ;
case 'v' : verbosity++ ; break ;
case 'b' : if (!uint0_scan(l.arg, &kbufsz)) dieusage() ; break ;
- case 't' : if (!make_ttos(l.arg)) dieusage() ; break ;
+ case 'l' : linevar = l.arg ; break ;
+ case 't' : if (!check_targ(l.arg)) dieusage() ; targ = l.arg ; break ;
default : dieusage() ;
}
}
argc -= l.ind ; argv += l.ind ;
- if (!argc) strerr_dieusage(100, USAGE) ;
- close(0) ;
- if (open_readb("/dev/null") < 0) strerr_diefu1sys(111, "open /dev/null for reading") ;
- x[1].fd = netlink_init(kbufsz) ;
- if (x[1].fd < 0) strerr_diefu1sys(111, "init netlink") ;
- }
-
- 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, SIGCHLD) ;
- if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ;
}
+ if (!argc) strerr_dieusage(100, USAGE) ;
- tain_now_g() ;
- tain_add_g(&deadline, &tain_infinite_relative) ;
- if (verbosity >= 2) strerr_warni1x("starting") ;
-
- while (cont)
{
- register int r = iopause_g(x, 1 + !pid, &deadline) ;
- if (r < 0) strerr_diefu1sys(111, "iopause") ;
- else if (!r) handle_timeout() ;
- else
+ unsigned int m = 0, pos = 0 ;
+ char fmt[UINT_FMT * 3] ;
+ char const *newargv[argc + 15] ;
+ newargv[m++] = S6_LINUX_UTILS_BINPREFIX "s6-uevent-listener" ;
+ if (verbosity != 1)
+ {
+ newargv[m++] = "-v" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, verbosity) ;
+ fmt[pos++] = 0 ;
+ }
+ if (kbufsz != 65536)
+ {
+ newargv[m++] = "-k" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, kbufsz) ;
+ fmt[pos++] = 0 ;
+ }
+ newargv[m++] = "--" ;
+ newargv[m++] = S6_LINUX_UTILS_BINPREFIX "s6-uevent-spawner" ;
+ if (verbosity != 1)
+ {
+ newargv[m++] = "-v" ;
+ newargv[m++] = fmt + pos ;
+ pos += uint_fmt(fmt + pos, verbosity) ;
+ fmt[pos++] = 0 ;
+ }
+ if (linevar)
+ {
+ newargv[m++] = "-l" ;
+ newargv[m++] = linevar ;
+ }
+ if (targ)
{
- 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 (!pid && (x[1].revents & IOPAUSE_READ))
- handle_netlink(x[1].fd, argv, envp) ;
+ newargv[m++] = "-t" ;
+ newargv[m++] = targ ;
}
+ newargv[m++] = "--" ;
+ while (*argv) newargv[m++] = *argv++ ;
+ newargv[m++] = 0 ;
+ pathexec_run(newargv[0], newargv, envp) ;
+ strerr_dieexec(111, newargv[0]) ;
}
- if (verbosity >= 2) strerr_warni1x("exiting") ;
- return 0 ;
}
diff --git a/src/minutils/s6-uevent-listener.c b/src/minutils/s6-uevent-listener.c
new file mode 100644
index 0000000..b2cf455
--- /dev/null
+++ b/src/minutils/s6-uevent-listener.c
@@ -0,0 +1,233 @@
+/* ISC license. */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/uint.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/iopause.h>
+#include <skalibs/bufalloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/sig.h>
+#include <skalibs/selfpipe.h>
+
+#define USAGE "s6-uevent-listener [ -v verbosity ] [ -b kbufsz ] helperprogram..."
+#define dieusage() strerr_dieusage(100, USAGE)
+#define dienomem() strerr_diefu1sys(111, "build string") ;
+
+static unsigned int cont = 1, verbosity = 1 ;
+static pid_t pid ;
+
+static inline int fd_recvmsg (int fd, struct msghdr *hdr)
+{
+ int r ;
+ do r = recvmsg(fd, hdr, MSG_DONTWAIT) ;
+ while ((r == -1) && (errno == EINTR)) ;
+ return r ;
+}
+
+static inline int netlink_init_stdin (unsigned int kbufsz)
+{
+ struct sockaddr_nl nl = { .nl_family = AF_NETLINK, .nl_pad = 0, .nl_groups = 0, .nl_pid = 0 } ;
+ close(0) ;
+ return socket_internal(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT, DJBUNIX_FLAG_NB|DJBUNIX_FLAG_COE) == 0
+ && bind(0, (struct sockaddr *)&nl, sizeof(struct sockaddr_nl)) == 0
+ && setsockopt(0, SOL_SOCKET, SO_RCVBUFFORCE, &kbufsz, sizeof(unsigned int)) == 0 ;
+}
+
+static inline void handle_signals (void)
+{
+ for (;;)
+ {
+ char c = selfpipe_read() ;
+ switch (c)
+ {
+ case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
+ case 0 : return ;
+ case SIGTERM :
+ cont = 0 ;
+ fd_close(0) ;
+ break ;
+ case SIGCHLD :
+ {
+ char fmt[UINT_FMT] ;
+ 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 ;
+ if (WIFSIGNALED(wstat))
+ {
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_dief2x(1, "child crashed with signal ", fmt) ;
+ }
+ else
+ {
+ fmt[uint_fmt(fmt, WEXITSTATUS(wstat))] = 0 ;
+ strerr_dief2x(1, "child exited ", fmt) ;
+ }
+ }
+ default :
+ strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ;
+ }
+ }
+}
+
+static void doit (char const *s, unsigned int len)
+{
+ if (!bufalloc_put(bufalloc_1, s, len)) dienomem() ;
+}
+
+static void terminate (void)
+{
+ if (!bufalloc_put(bufalloc_1, "", 1)) dienomem() ;
+}
+
+static inline void handle_netlink (void)
+{
+ char buf[8192] ;
+ static int inmulti = 0 ;
+ struct sockaddr_nl nl;
+ struct iovec iov = { .iov_base = &buf, .iov_len = sizeof(buf) } ;
+ struct msghdr msg =
+ {
+ .msg_name = &nl,
+ .msg_namelen = sizeof(struct sockaddr_nl),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = 0,
+ .msg_controllen = 0,
+ .msg_flags = 0
+ } ;
+ struct nlmsghdr *nlh = (struct nlmsghdr *)buf ;
+ int r = sanitize_read(fd_recvmsg(0, &msg)) ;
+ if (r < 0)
+ {
+ if (errno == EPIPE)
+ {
+ if (verbosity >= 2) strerr_warnw1x("received EOF on netlink") ;
+ cont = 0 ;
+ fd_close(0) ;
+ return ;
+ }
+ else strerr_diefu1sys(111, "receive netlink message") ;
+ }
+ if (!r) return ;
+ if (msg.msg_flags & MSG_TRUNC)
+ strerr_diefu2x(111, "buffer too small for ", "netlink message") ;
+ if (nl.nl_pid)
+ {
+ if (verbosity >= 3)
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, nl.nl_pid)] = 0 ;
+ strerr_warnw3x("netlink message", " from userspace process ", fmt) ;
+ }
+ return ;
+ }
+
+ for (; NLMSG_OK(nlh, r) ; nlh = NLMSG_NEXT(nlh, r))
+ switch (nlh->nlmsg_type)
+ {
+ case NLMSG_NOOP : break ;
+ case NLMSG_ERROR :
+ if (verbosity >= 3)
+ strerr_warnw2x("spurious NLMSG_ERROR ", "netlink message") ;
+ break ;
+ case NLMSG_DONE :
+ if (inmulti)
+ {
+ inmulti = 0 ;
+ terminate() ;
+ }
+ else if (verbosity >= 3)
+ strerr_warnw2x("spurious NLMSG_DONE ", "netlink message") ;
+ break ;
+ default :
+ if (nlh->nlmsg_flags & NLM_F_MULTI) inmulti = 1 ;
+ else if (inmulti)
+ {
+ inmulti = 0 ;
+ terminate() ;
+ if (verbosity >= 3)
+ strerr_warnw2x("unterminated multipart ", "netlink message") ;
+ }
+ doit(NLMSG_DATA(nlh), (unsigned int)NLMSG_PAYLOAD(nlh, r)) ;
+ if (!inmulti) terminate() ;
+ break ;
+ }
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ iopause_fd x[3] = { { .events = IOPAUSE_READ }, { .fd = 1 }, { .fd = 0, .events = IOPAUSE_READ } } ;
+ PROG = "s6-uevent-listener" ;
+ {
+ unsigned int kbufsz = 65536 ;
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "v:b:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ;
+ case 'b' : if (!uint0_scan(l.arg, &kbufsz)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (!argc) strerr_dieusage(100, USAGE) ;
+ if (!netlink_init_stdin(kbufsz)) strerr_diefu1sys(111, "init netlink") ;
+ }
+
+ x[0].fd = selfpipe_init() ;
+ if (x[0].fd < 0) 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, SIGCHLD) ;
+ if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ;
+ }
+
+ {
+ int fd ;
+ pid = child_spawn1_pipe(argv[0], argv, envp, &fd, 0) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ;
+ if (fd_move(1, fd) < 0) strerr_diefu1sys(111, "move pipe to stdout") ;
+ }
+
+ if (verbosity >= 2) strerr_warni1x("starting") ;
+
+ while (cont || bufalloc_len(bufalloc_1))
+ {
+ register int r ;
+ x[1].events = bufalloc_len(bufalloc_1) ? IOPAUSE_WRITE : 0 ;
+ r = iopause(x, 2 + cont, 0, 0) ;
+ if (r < 0) strerr_diefu1sys(111, "iopause") ;
+ if (!r) continue ;
+ if (x[0].revents & IOPAUSE_EXCEPT)
+ strerr_diefu1x(111, "iopause: trouble with selfpipe") ;
+ if (x[0].revents & IOPAUSE_READ) handle_signals() ;
+ if (x[1].revents & IOPAUSE_WRITE)
+ if (!bufalloc_flush(bufalloc_1)) strerr_diefu1sys(111, "flush stdout") ;
+ if (cont && x[2].revents & IOPAUSE_READ) handle_netlink() ;
+ }
+ if (verbosity >= 2) strerr_warni1x("exiting") ;
+ return 0 ;
+}
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 ;
+}