>From d65ddf38cd5cf60ba6fc4f1bf60f7324a3e6bebd Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Fri, 15 Feb 2019 12:31:48 -0700 Subject: [PATCH] env: new option -D/--default-signal=SIG [FIXME] See https://bugs.gnu.org/34488#8 . --- src/env.c | 90 +++++++++++++++++++++++++++++++++++++++- src/local.mk | 1 + tests/local.mk | 1 + tests/misc/env-signal-handler.sh | 68 ++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 1 deletion(-) create mode 100755 tests/misc/env-signal-handler.sh diff --git a/src/env.c b/src/env.c index 3a1a3869e..ebda91589 100644 --- a/src/env.c +++ b/src/env.c @@ -21,12 +21,16 @@ #include #include #include +#include +#include #include #include "system.h" #include "die.h" #include "error.h" +#include "operand2sig.h" #include "quote.h" +#include "sig2str.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "env" @@ -48,7 +52,15 @@ static bool dev_debug; static char *varname; static size_t vnlen; -static char const shortopts[] = "+C:iS:u:v0 \t"; +/* if true, at least one signal handler should be reset. */ +static bool reset_signals ; + +/* if element [SIGNUM] is true, the signal handler's should be reset + to its defaut. */ +static bool signal_handlers[SIGNUM_BOUND]; + + +static char const shortopts[] = "+C:iPS:u:v0 \t"; static struct option const longopts[] = { @@ -56,6 +68,7 @@ static struct option const longopts[] = {"null", no_argument, NULL, '0'}, {"unset", required_argument, NULL, 'u'}, {"chdir", required_argument, NULL, 'C'}, + {"default-signal", optional_argument, NULL, 'P'}, {"debug", no_argument, NULL, 'v'}, {"split-string", required_argument, NULL, 'S'}, {GETOPT_HELP_OPTION_DECL}, @@ -88,8 +101,17 @@ Set each NAME to VALUE in the environment and run COMMAND.\n\ -C, --chdir=DIR change working directory to DIR\n\ "), stdout); fputs (_("\ + --default-signal=SIG reset signal SIG to its default signal handler.\n\ + multiple signals can be comma-separated.\n\ +"), stdout); + fputs (_("\ -S, --split-string=S process and split S into separate arguments;\n\ used to pass multiple arguments on shebang lines\n\ +"), stdout); + fputs (_("\ + -P same as --default-signal=PIPE\n\ +"), stdout); + fputs (_("\ -v, --debug print verbose information for each processing step\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); @@ -525,6 +547,63 @@ parse_split_string (const char* str, int /*out*/ *orig_optind, *orig_optind = 0; /* tell getopt to restart from first argument */ } +static void +parse_signal_params (const char* optarg) +{ + char signame[SIG2STR_MAX]; + char *opt_sig; + char *optarg_writable = xstrdup (optarg); + + opt_sig = strtok (optarg_writable, ","); + while (opt_sig) + { + int signum = operand2sig (opt_sig, signame); + if (signum < 0) + usage (EXIT_FAILURE); + + signal_handlers[signum] = true; + + opt_sig = strtok (NULL, ","); + } + + free (optarg_writable); +} + +static void +reset_signal_handlers (void) +{ + + if (!reset_signals) + return; + + if (dev_debug) + devmsg ("Resetting signal handlers:\n"); + + for (int i=0; i. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ seq +trap_sigpipe_or_skip_ + +# Paraphrasing http://bugs.gnu.org/34488#8: +# POSIX requires that sh started with an inherited ignored SIGPIPE must +# silently ignore all attempts from within the shell to restore SIGPIPE +# handling to child processes of the shell: +# +# $ (trap '' PIPE; bash -c 'trap - PIPE; seq inf | head -n1') +# 1 +# seq: write error: Broken pipe +# +# With 'env --default-signal=PIPE', the signal handler can be reset to its +# default. + +# Baseline Test +# ------------- +# Ensure this results in a "broken pipe" error (the first 'trap' +# sets SIGPIPE to ignore, and the second 'trap' becomes a no-op instead +# of resetting SIGPIPE to its default). Upon a SIGPIPE 'seq' will not be +# terminated, instead its write(2) call will return an error. +(trap '' PIPE; sh -c 'trap - PIPE; seq 999999 2>err1t | head -n1 > out1') + +# The exact broken pipe message depends on the operating system, just ensure +# there was a 'write error' message in stderr: +sed 's/^\(seq: write error:\) .*/\1/' err1t > err1 || framework_failure_ + +printf "1\n" > exp-out || framework_failure_ +printf "seq: write error:\n" > exp-err1 || framework_failure_ + +compare exp-out out1 || framework_failure_ +compare exp-err1 err1 || framework_failure_ + + +# env test +# -------- +# With env resetting the signal handler to its defaults, there should be no +# error message (because the default SIGPIPE action is to terminate the +# 'seq' program): +(trap '' PIPE; + env --default-signal=PIPE \ + sh -c 'trap - PIPE; seq 999999 2>err2 | head -n1 > out2') + +compare exp-out out2 || fail=1 +compare /dev/null err2 || fail=1 + + + +Exit $fail -- 2.11.0