>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