diff options
-rw-r--r-- | INSTALL | 2 | ||||
-rw-r--r-- | doc/index.html | 5 | ||||
-rw-r--r-- | doc/trap.html | 98 | ||||
-rw-r--r-- | doc/upgrade.html | 7 | ||||
-rw-r--r-- | package/deps.mak | 3 | ||||
-rw-r--r-- | package/info | 2 | ||||
-rw-r--r-- | package/modes | 1 | ||||
-rw-r--r-- | package/targets.mak | 1 | ||||
-rw-r--r-- | src/execline/deps-exe/trap | 2 | ||||
-rw-r--r-- | src/execline/trap.c | 161 |
10 files changed, 278 insertions, 4 deletions
@@ -6,7 +6,7 @@ Build Instructions - A POSIX-compliant C development environment - GNU make version 4.0 or later - - skalibs version 2.3.3.0 or later: http://skarnet.org/software/skalibs/ + - skalibs version 2.3.4.0 or later: http://skarnet.org/software/skalibs/ This software will run on any operating system that implements POSIX.1-2008, available at: diff --git a/doc/index.html b/doc/index.html index dda1445..5fb4b3c 100644 --- a/doc/index.html +++ b/doc/index.html @@ -51,7 +51,7 @@ shell's syntax, and has no security issues. <li> GNU make, version 4.0 or later. Please be aware that execline will not build with an earlier version. </li> <li> <a href="http://skarnet.org/software/skalibs/">skalibs</a> version -2.3.3.0 or later. It's a build-time requirement. It's also a run-time +2.3.4.0 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> @@ -66,7 +66,7 @@ library. </li> <h3> Download </h3> <ul> - <li> The current released version of execline is <a href="execline-2.1.1.1.tar.gz">2.1.1.1</a>. </li> + <li> The current released version of execline is <a href="execline-2.1.2.0.tar.gz">2.1.2.0</a>. </li> <li> Alternatively, you can checkout a copy of the execline git repository: <pre> git clone git://git.skarnet.org/execline </pre> </li> </ul> @@ -136,6 +136,7 @@ to your installation: the shebang lines for your system might be something like <li><a href="exec.html">The <tt>exec</tt> program</a></li> <li><a href="tryexec.html">The <tt>tryexec</tt> program</a></li> <li><a href="exit.html">The <tt>exit</tt> program</a></li> +<li><a href="trap.html">The <tt>trap</tt> program</a></li> </ul> <p> (<a href="el_semicolon.html">Basic block management</a>) diff --git a/doc/trap.html b/doc/trap.html new file mode 100644 index 0000000..ff03c2e --- /dev/null +++ b/doc/trap.html @@ -0,0 +1,98 @@ +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="Content-Language" content="en" /> + <title>execline: the trap command</title> + <meta name="Description" content="execline: the trap command" /> + <meta name="Keywords" content="execline command trap signal" /> + <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> --> +</head> +<body> + +<p> +<a href="index.html">execline</a><br /> +<a href="http://skarnet.org/software/">Software</a><br /> +<a href="http://skarnet.org/">skarnet.org</a> +</p> + +<h1> The <tt>trap</tt> program </h1> + +<tt>trap</tt> traps signals and runs a variety of commands according +to the signals it catches. + +<h2> Interface </h2> + +<p> + In an <a href="execlineb.html">execlineb</a> script: +</p> + +<pre> + trap [ -x ] [ -t <em>millisecs</em> ] + { + [ timeout { <em>progtimeout...</em> } ] + [ SIGTERM { <em>progsigterm...</em> } ] + [ quit { <em>progsigquit...</em> } ] + [ 1 { <em>progsighup</em>... } ] + ... + } + <em>prog...</em> +</pre> + +<ul> + <li> <tt>trap</tt> reads a sequence of directives in a +<a href="el_semicolon.html">block</a>. It expects at least one +directive. </li> + <li> Each directive is a keyword followed by a block. </li> + <li> The keyword can be the special word <tt>timeout</tt>, a signal +name (case-insensitive, with or without the <tt>SIG</tt> prefix), + or a signal number. The block following it is a command line to +run when the specified event occurs. </li> + <li> <tt>trap</tt> sets traps for the various directives it reads. +A trap for <tt>SIGTERM</tt> will be triggered when the <tt>trap</tt> +program receives a SIGTERM. +A trap for <tt>timeout</tt> will be triggered when <em>millisecs</em> +milliseconds elapse without a signal being caught. </li> + <li> It spawns a child executing <em>prog...</em>. </li> + <li> It sets the <tt>!</tt> environment +variable to the pid of the <em>prog...</em> process. </li> + <li> Whenever it catches a signal, it spawns the program described in the +corresponding directive. It will not spawn a program for the same signal +twice: if the first subprocess is still active when another instance of the +same signal arrives, this second instance is ignored. </li> + <li> When <em>prog...</em> exits, <tt>trap</tt> exits with an +<a href="exitcodes.html">approximation</a> of the same exit code. </li> +</ul> + +<h2> Options </h2> + +<ul> + <li> <tt>-x</tt> : forward signals. If this option is given, +any signal that <tt>trap</tt> receives and that is not explicitly +trapped will be sent to <em>prog</em>. By default, <tt>trap</tt> does +not forward any signals, and does not ignore them either - for instance a +SIGTERM, unless caught by a <tt>SIGTERM</tt> directive, will kill the +<tt>trap</tt> process (and leave <em>prog</em> running). With the +<tt>-x</tt> option, without a <tt>SIGTERM</tt> directive, a SIGTERM +will be still be caught by <tt>trap</tt>, that will send it to +<em>prog</em>. </li> + <li> <tt>-t <em>millisecs</em></tt> : if a <tt>timeout</tt> +directive exists, the corresponding <em>progtimeout...</em> will be run +every time <em>millisecs</em> milliseconds elapse without <tt>trap</tt> +receiving a signal. By default, <em>millisecs</em> is 0, which means +infinite (i.e. a <tt>timeout</tt> directive will never trigger). </li> +</ul> + +<h2> Notes </h2> + +<ul> + <li> Programs defined in command line directives can start with +<tt><a href="import.html">import</a> !</tt> to retrieve the pid of +<em>prog</em> in <tt>${!}</tt> </li> + <li> <tt>trap</tt> is a standard shell builtin, with similar +functionality. It is more idiomatic, and probably more efficient, +to use that builtin in shell scripts, and to only use the +<tt>trap</tt> program in execline scripts. </li> +</ul> + +</body> +</html> diff --git a/doc/upgrade.html b/doc/upgrade.html index 121b210..ff1d40d 100644 --- a/doc/upgrade.html +++ b/doc/upgrade.html @@ -17,6 +17,13 @@ <h1> What has changed in execline </h1> +<h2> in 2.1.2.0 </h2> + +<ul> + <li> skalibs dependency bumped to 2.3.4.0 </li> + <li> new command: <a href="trap.html">trap</a> </li> +</ul> + <h2> in 2.1.1.1 </h2> <ul> diff --git a/package/deps.mak b/package/deps.mak index 6c926ae..fd4477b 100644 --- a/package/deps.mak +++ b/package/deps.mak @@ -41,6 +41,7 @@ src/execline/piperw.o src/execline/piperw.lo: src/execline/piperw.c src/execline/redirfd.o src/execline/redirfd.lo: src/execline/redirfd.c src/execline/runblock.o src/execline/runblock.lo: src/execline/runblock.c src/include/execline/execline.h src/execline/shift.o src/execline/shift.lo: src/execline/shift.c src/include/execline/execline.h +src/execline/trap.o src/execline/trap.lo: src/execline/trap.c src/include/execline/execline.h src/execline/tryexec.o src/execline/tryexec.lo: src/execline/tryexec.c src/include/execline/execline.h src/execline/umask.o src/execline/umask.lo: src/execline/umask.c src/execline/unexport.o src/execline/unexport.lo: src/execline/unexport.c @@ -147,6 +148,8 @@ runblock: private EXTRA_LIBS := runblock: src/execline/runblock.o ${LIBEXECLINE} -lskarnet shift: private EXTRA_LIBS := shift: src/execline/shift.o ${LIBEXECLINE} -lskarnet +trap: private EXTRA_LIBS := +trap: src/execline/trap.o ${LIBEXECLINE} -lskarnet tryexec: private EXTRA_LIBS := tryexec: src/execline/tryexec.o ${LIBEXECLINE} -lskarnet umask: private EXTRA_LIBS := diff --git a/package/info b/package/info index 4e34898..81cd31b 100644 --- a/package/info +++ b/package/info @@ -1,4 +1,4 @@ package=execline -version=2.1.1.1 +version=2.1.2.0 category=admin package_macro_name=EXECLINE diff --git a/package/modes b/package/modes index 8519ef5..b1d0883 100644 --- a/package/modes +++ b/package/modes @@ -37,6 +37,7 @@ pipeline 0755 redirfd 0755 runblock 0755 shift 0755 +trap 0755 tryexec 0755 umask 0755 unexport 0755 diff --git a/package/targets.mak b/package/targets.mak index 9601f69..4ffca0f 100644 --- a/package/targets.mak +++ b/package/targets.mak @@ -38,6 +38,7 @@ piperw \ redirfd \ runblock \ shift \ +trap \ tryexec \ umask \ unexport \ diff --git a/src/execline/deps-exe/trap b/src/execline/deps-exe/trap new file mode 100644 index 0000000..97021b5 --- /dev/null +++ b/src/execline/deps-exe/trap @@ -0,0 +1,2 @@ +${LIBEXECLINE} +-lskarnet diff --git a/src/execline/trap.c b/src/execline/trap.c new file mode 100644 index 0000000..bfb5733 --- /dev/null +++ b/src/execline/trap.c @@ -0,0 +1,161 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <errno.h> +#include <signal.h> +#include <skalibs/bytestr.h> +#include <skalibs/sgetopt.h> +#include <skalibs/uint.h> +#include <skalibs/nsig.h> +#include <skalibs/sig.h> +#include <skalibs/strerr2.h> +#include <skalibs/tai.h> +#include <skalibs/iopause.h> +#include <skalibs/selfpipe.h> +#include <skalibs/env.h> +#include <skalibs/djbunix.h> +#include <execline/execline.h> + +#define USAGE "trap [ -x ] [ -t timeout ] { signal { cmdline } ... } prog..." +#define dieusage() strerr_dieusage(100, USAGE) ; + +static pid_t pids[NSIG + 2] ; +static char const *const *argvs[NSIG + 1] ; + +static void action (unsigned int i, char const *const *envp) +{ + if (argvs[i]) + { + if (!pids[i]) + { + pids[i] = child_spawn0(argvs[i][0], argvs[i], envp) ; + if (!pids[i]) strerr_diefu2sys(111, "spawn ", argvs[i][0]) ; + } + } + else kill(pids[NSIG+1], i) ; +} + +int main (int argc, char const **argv, char const *const *envp) +{ + tain_t tto ; + int xfersigs = 0 ; + int argc1, spfd ; + unsigned int i = NSIG + 2 ; + PROG = "trap" ; + { + unsigned int t = 0 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "xt:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'x' : xfersigs = 1 ; 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() ; + argc1 = el_semicolon(argv) ; + if (!argc1) strerr_dief1x(100, "empty block") ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (argc1 + 1 == argc) dieusage() ; + argv[argc1] = 0 ; + while (i--) argvs[i] = 0 ; + i = 0 ; + tain_now_g() ; + spfd = selfpipe_init() ; + if (spfd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + + while (i < (unsigned int)argc1) + { + int argc2 ; + unsigned int sig = (unsigned int)sig_number(argv[i] + (case_diffb(argv[i], 3, "sig") ? 0 : 3)) ; + if (!sig && !uint0_scan(argv[i], &sig) && case_diffs(argv[i], "timeout")) + strerr_dief3x(100, "unrecognized", " directive: ", argv[i]) ; + argc2 = el_semicolon(argv + ++i) ; + if (!argc2) + strerr_dief3x(100, "empty", " internal block for directive ", argv[i-1]) ; + if (i + argc2 >= argc1) + strerr_dief3x(100, "unterminated", " internal block for directive ", argv[i-1]) ; + if (argvs[sig]) + strerr_dief3x(100, "duplicate", " directive: ", argv[i-1]) ; + if (sig && selfpipe_trap(sig) < 0) + strerr_diefu2sys(111, "trap ", argv[i-1]) ; + argv[i + argc2] = 0 ; + argvs[sig] = argv + i ; + i += argc2 + 1 ; + } + if (!argvs[SIGCHLD] && selfpipe_trap(SIGCHLD) < 0) + strerr_diefu2sys(111, "trap ", "SIGCHLD") ; + + if (xfersigs) + { + sigset_t full ; + sigfillset(&full) ; + sigdelset(&full, SIGCHLD) ; + for (i = 1 ; i <= NSIG ; i++) + if (!argvs[i] && sigismember(&full, i) > 0 && selfpipe_trap(i) < 0) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, i)] = 0 ; + strerr_diefu4sys(111, "auto-", "trap ", "signal ", fmt) ; + } + } + + pids[NSIG+1] = child_spawn0(argv[argc1 + 1], argv + argc1 + 1, envp) ; + if (!pids[NSIG+1]) strerr_diefu2sys(111, "spawn ", argv[argc1 + 1]) ; + + { + iopause_fd x = { .fd = spfd, .events = IOPAUSE_READ } ; + char modif[2 + UINT64_FMT] = "!=" ; + unsigned int envlen = env_len(envp) ; + char const *newenvp[envlen + 2] ; + i = 2 + uint64_fmt(modif + 2, pids[NSIG+1]) ; + modif[i++] = 0 ; + if (!env_merge(newenvp, envlen + 2, envp, envlen, modif, i)) + strerr_diefu1sys(111, "adjust environment") ; + for (;;) + { + tain_t deadline ; + register int r ; + tain_add_g(&deadline, &tto) ; + r = iopause_g(&x, 1, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + if (!r) action(0, newenvp) ; + else + { + int cont = 1 ; + while (cont) + { + r = selfpipe_read() ; + switch (r) + { + case -1 : strerr_diefu1sys(111, "selfpipe_read") ; + case 0 : cont = 0 ; break ; + case SIGCHLD : + for (;;) + { + int id, wstat ; + id = wait_pids_nohang(pids, NSIG + 2, &wstat) ; + if (id < 0 && errno != ECHILD) + strerr_diefu1sys(111, "wait") ; + if (id <= 0) break ; + pids[id - 1] = 0 ; + if (id == NSIG + 2) return wait_estatus(wstat) ; + } + if (!argvs[SIGCHLD]) break ; + default : + action(r, newenvp) ; + } + } + } + } + } +} |