summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2015-05-05 13:10:13 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2015-05-05 13:10:13 +0000
commit2c59f669553677838022d65f2433b31a04bc632d (patch)
tree90b723368a2be42aaa2f36c7dc9b351f98a93e12
parent820a98c94c134745b1da83d10b648e6c8f83791e (diff)
downloadexecline-2.1.2.0.tar.xz
- add trapv2.1.2.0
- rc for 2.1.2.0
-rw-r--r--INSTALL2
-rw-r--r--doc/index.html5
-rw-r--r--doc/trap.html98
-rw-r--r--doc/upgrade.html7
-rw-r--r--package/deps.mak3
-rw-r--r--package/info2
-rw-r--r--package/modes1
-rw-r--r--package/targets.mak1
-rw-r--r--src/execline/deps-exe/trap2
-rw-r--r--src/execline/trap.c161
10 files changed, 278 insertions, 4 deletions
diff --git a/INSTALL b/INSTALL
index 28d5af8..f912e75 100644
--- a/INSTALL
+++ b/INSTALL
@@ -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>&nbsp;: 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&nbsp;<em>millisecs</em></tt>&nbsp;: 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) ;
+ }
+ }
+ }
+ }
+ }
+}