aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2019-08-21 11:27:56 +1000
committerSteve Bennett <steveb@workware.net.au>2019-09-06 20:08:58 +1000
commitca4fa3865a730857b385e6e34af67db3471e0089 (patch)
treef0489c0f53f865c747461455a3955e98e0ae9d62
parent4dc539a582764fc4ae6788fc3f1dc0e6e7cd95d4 (diff)
downloadjimtcl-ca4fa3865a730857b385e6e34af67db3471e0089.zip
jimtcl-ca4fa3865a730857b385e6e34af67db3471e0089.tar.gz
jimtcl-ca4fa3865a730857b385e6e34af67db3471e0089.tar.bz2
signal: Add 'signal block' support
This allows a signal to be blocked by setting it's handler to SIG_IGN Can be used to block SIGPIPE for exec Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r--jim-signal.c46
-rw-r--r--jim_tcl.txt11
-rw-r--r--tests/exec2.test32
3 files changed, 65 insertions, 24 deletions
diff --git a/jim-signal.c b/jim-signal.c
index cec541a..7d02e68 100644
--- a/jim-signal.c
+++ b/jim-signal.c
@@ -22,7 +22,7 @@
#endif
static jim_wide *sigloc;
-static jim_wide sigsblocked;
+static jim_wide sigsignored;
static struct sigaction *sa_old;
static struct {
int status;
@@ -34,16 +34,16 @@ static struct {
static void signal_handler(int sig)
{
- /* We just remember which signals occurred. Jim_Eval() will
- * notice this as soon as it can and throw an error
+ /* Remember which signals occurred and store in *sigloc.
+ * Jim_Eval() will notice this as soon as it can and throw an error
*/
*sigloc |= sig_to_bit(sig);
}
static void signal_ignorer(int sig)
{
- /* We just remember which signals occurred */
- sigsblocked |= sig_to_bit(sig);
+ /* Remember which signals occurred for access by 'signal check' */
+ sigsignored |= sig_to_bit(sig);
}
static void signal_init_names(void)
@@ -169,6 +169,7 @@ static int find_signal_by_name(Jim_Interp *interp, const char *name)
#define SIGNAL_ACTION_HANDLE 1
#define SIGNAL_ACTION_IGNORE -1
+#define SIGNAL_ACTION_BLOCK -2
#define SIGNAL_ACTION_DEFAULT 0
static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *const *argv)
@@ -194,9 +195,13 @@ static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *cons
if (action == SIGNAL_ACTION_HANDLE) {
sa.sa_handler = signal_handler;
}
- else {
+ else if (action == SIGNAL_ACTION_IGNORE) {
sa.sa_handler = signal_ignorer;
}
+ else {
+ /* SIGNAL_ACTION_BLOCK */
+ sa.sa_handler = SIG_IGN;
+ }
}
/* Iterate through the provided signals */
@@ -209,6 +214,7 @@ static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *cons
if (action != siginfo[sig].status) {
/* Need to change the action for this signal */
switch (action) {
+ case SIGNAL_ACTION_BLOCK:
case SIGNAL_ACTION_HANDLE:
case SIGNAL_ACTION_IGNORE:
if (siginfo[sig].status == SIGNAL_ACTION_DEFAULT) {
@@ -246,6 +252,11 @@ static int signal_cmd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return do_signal_cmd(interp, SIGNAL_ACTION_IGNORE, argc, argv);
}
+static int signal_cmd_block(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ return do_signal_cmd(interp, SIGNAL_ACTION_BLOCK, argc, argv);
+}
+
static int signal_cmd_default(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return do_signal_cmd(interp, SIGNAL_ACTION_DEFAULT, argc, argv);
@@ -269,7 +280,7 @@ static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int clear = 0;
jim_wide mask = 0;
- jim_wide blocked;
+ jim_wide ignored;
if (argc > 0 && Jim_CompareStringImmediate(interp, argv[0], "-clear")) {
clear++;
@@ -292,17 +303,13 @@ static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
mask = ~mask;
}
- if ((sigsblocked & mask) == 0) {
- /* No matching signals, so empty result and nothing to do */
- return JIM_OK;
- }
/* Be careful we don't have a race condition where signals are cleared but not returned */
- blocked = sigsblocked & mask;
+ ignored = sigsignored & mask;
if (clear) {
- sigsblocked &= ~blocked;
+ sigsignored &= ~ignored;
}
/* Set the result */
- signal_set_sigmask_result(interp, blocked);
+ signal_set_sigmask_result(interp, ignored);
return JIM_OK;
}
@@ -316,9 +323,9 @@ static int signal_cmd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
}
}
- /* If the signal is ignored (blocked) ... */
+ /* If the signal is ignored ... */
if (siginfo[sig].status == SIGNAL_ACTION_IGNORE) {
- sigsblocked |= sig_to_bit(sig);
+ sigsignored |= sig_to_bit(sig);
return JIM_OK;
}
@@ -370,6 +377,13 @@ static const jim_subcmd_type signal_command_table[] = {
-1,
/* Description: Lists ignored signals, or adds to ignored signals */
},
+ { "block",
+ "?signals ...?",
+ signal_cmd_block,
+ 0,
+ -1,
+ /* Description: Lists blocked signals, or adds to blocked signals */
+ },
{ "default",
"?signals ...?",
signal_cmd_default,
diff --git a/jim_tcl.txt b/jim_tcl.txt
index 247ad7e..352071e 100644
--- a/jim_tcl.txt
+++ b/jim_tcl.txt
@@ -57,6 +57,7 @@ Changes since 0.78
1. Add `file mtimeus` for high resolution file timestamps
2. `aio` now supports datagram Unix-Domain sockets
3. Add support for `aio lock -wait`
+4. Add `signal block` to prevent delivery of signals
Changes between 0.77 and 0.78
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3798,8 +3799,16 @@ Commands which return a list of signal names do so using the canonical form:
`signal check` to determine which signals have occurred but
been ignored.
++*signal block* ?'signals \...'?+::
+ If no signals are given, returns a lists all signals which are currently
+ being blocked.
+ If signals are specified, these are added to the list of signals
+ currently being blocked. These signals are not delivered to the process.
+ This can be useful for signals such as +SIGPIPE+, especially in conjunction
+ with `exec` as child processes inherit the parent's signal disposition.
+
+*signal default* ?'signals \...'?+::
- If no signals are given, returns a lists all signals which are currently have
+ If no signals are given, returns a lists all signals which currently have
the default behaviour.
If signals are specified, these are added to the list of signals which have
the default behaviour.
diff --git a/tests/exec2.test b/tests/exec2.test
index 08f3d11..b4b42cc 100644
--- a/tests/exec2.test
+++ b/tests/exec2.test
@@ -5,12 +5,15 @@
source [file dirname [info script]]/testing.tcl
needs cmd exec
+foreach i {pipe signal wait} {
+ testConstraint $i [expr {[info commands $i] ne ""}]
+}
# Some Windows platforms (e.g. AppVeyor) produce ENOSPC rather than killing
# the child with SIGPIPE). So turn off this test for that platform
-if {[info commands pipe] ne "" && [env MSYSTEM ""] ne "MINGW32"} {
- testConstraint pipe 1
+if {[info exists env(MSYSTEM)] && $env(MSYSTEM) eq "MINGW32"} {
+ testConstraint nomingw32 0
} else {
- testConstraint pipe 0
+ testConstraint nomingw32 1
}
set d \"
@@ -58,7 +61,7 @@ test exec2-3.1 "close pipeline return value" {
list $rc $msg $status $exitcode
} {1 {child process exited abnormally} CHILDSTATUS 1}
-test exec2-3.2 "close pipeline return value" -constraints pipe -body {
+test exec2-3.2 "close pipeline return value" -constraints {pipe nomingw32} -body {
# Create a pipe and immediately close the read end
lassign [pipe] r w
close $r
@@ -71,7 +74,23 @@ test exec2-3.2 "close pipeline return value" -constraints pipe -body {
list $rc $msg $status $exitcode
} -match glob -result {1 {child killed*} CHILDKILLED SIGPIPE}
-test exec2-3.4 "wait for background task" {
+test exec2-3.3 "close pipeline with SIGPIPE blocked" -constraints {pipe signal nomingw32} -body {
+ # Create a pipe and immediately close the read end
+ lassign [pipe] r w
+ close $r
+ signal block SIGPIPE
+ # Write more than 64KB which is maximum size of the pipe buffers
+ # on all systems we have seen
+ set bigstring [string repeat a 100000]
+ set f [open [list |cat << $bigstring >$@w 2>/dev/null]]
+ set rc [catch {close $f} msg opts]
+ lassign [dict get $opts -errorcode] status pid exitcode
+ list $rc $msg $status $exitcode
+} -match glob -result {1 {child process exited*} CHILDSTATUS 1} -cleanup {
+ signal default SIGPIPE
+}
+
+test exec2-3.4 "wait for background task" -constraints wait -body {
set pid [exec sleep 0.1 &]
lassign [wait $pid] status newpid exitcode
if {$pid != $newpid} {
@@ -79,7 +98,6 @@ test exec2-3.4 "wait for background task" {
} else {
list $status $exitcode
}
-} {CHILDSTATUS 0}
-
+} -result {CHILDSTATUS 0}
testreport