1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
#!/bin/sh
# Test env --default-signal=PIPE feature.
# Copyright (C) 2019-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ env seq test timeout printf
trap_sigpipe_or_skip_
# /bin/sh has an intermittent failure in ignoring SIGPIPE on OpenIndiana 11
# so we require bash as discussed at:
# https://lists.gnu.org/archive/html/coreutils/2020-03/msg00004.html
# This test also relies on bash's 'kill' builtin which allows the signal name
# "RTMIN" and "RTMAX" if those real-time signals are supported.
require_bash_as_SHELL_
# Paraphrasing https://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 - default signal handler
# --------------------------------------
# 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; $SHELL -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 - default signal handler
# ---------------------------------
# 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 \
$SHELL -c 'trap - PIPE; seq 999999 2>err2 | head -n1 > out2')
compare exp-out out2 || fail=1
compare /dev/null err2 || fail=1
# env test - default signal handler (3)
# -------------------------------------
# Repeat the previous test, using --default-signal with no signal names,
# i.e., all signals.
(trap '' PIPE;
env --default-signal \
$SHELL -c 'trap - PIPE; seq 999999 2>err4 | head -n1 > out4')
compare exp-out out4 || fail=1
compare /dev/null err4 || fail=1
# env test - block signal handler
env --block-signal true || fail=1
env_ignore_delay_()
{
local delay="$1"
# The first 'env' is just to ensure timeout is not a shell built-in.
env timeout --verbose --kill-after=.1 --signal=$timeout_sig $delay \
env $env_opt sleep 10 > /dev/null 2>outt
# check only the first two lines from stderr, which are printed by timeout.
# (operating systems might add more messages, like "killed").
sed -n '1,2p' outt > out || framework_failure_
compare exp out
}
# Baseline test - ignore signal handler
# -------------------------------------
# Terminate 'sleep' with SIGINT
# (SIGINT's default action is to terminate a program).
cat <<\EOF >exp || framework_failure_
timeout: sending signal INT to command 'env'
EOF
timeout_sig=INT env_opt='' \
retry_delay_ env_ignore_delay_ .1 6 || fail=1
# env test - ignore signal handler
# --------------------------------
# Use env to ignore SIGINT - "sleep" should continue running
# after timeout sends SIGINT, and be killed using SIGKILL.
cat <<\EOF >exp || framework_failure_
timeout: sending signal INT to command 'env'
timeout: sending signal KILL to command 'env'
EOF
timeout_sig=INT env_opt='--ignore-signal=INT' \
retry_delay_ env_ignore_delay_ .1 6 || fail=1
timeout_sig=INT env_opt='--ignore-signal' \
retry_delay_ env_ignore_delay_ .1 6 || fail=1
# env test - ignore signal handler for PIPE
# SIGPIPE is often incorrectly interfered with, as per:
# http://www.pixelbeat.org/programming/sigpipe_handling.html
cat <<\EOF >exp || framework_failure_
timeout: sending signal PIPE to command 'env'
timeout: sending signal KILL to command 'env'
EOF
timeout_sig=PIPE env_opt='--ignore-signal=PIPE' \
retry_delay_ env_ignore_delay_ .1 6 || fail=1
if kill -l RTMIN RTMAX; then
for sig in RTMIN RTMAX; do
# Baseline test - ignore signal handler
# -------------------------------------
# Terminate 'sleep' with $sig
# All real-time signals terminate the program by default.
cat <<EOF >exp || framework_failure_
timeout: sending signal $sig to command 'env'
EOF
timeout_sig=$sig env_opt='' \
retry_delay_ env_ignore_delay_ .1 6 || fail=1
# env test - ignore signal handler
# --------------------------------
# Use env to ignore $sig - "sleep" should continue running
# after timeout sends $sig, and be killed using SIGKILL.
cat <<EOF >exp || framework_failure_
timeout: sending signal $sig to command 'env'
timeout: sending signal KILL to command 'env'
EOF
timeout_sig=$sig env_opt="--ignore-signal=$sig" \
retry_delay_ env_ignore_delay_ .1 6 || fail=1
timeout_sig=$sig env_opt='--ignore-signal' \
retry_delay_ env_ignore_delay_ .1 6 || fail=1
done
fi
# env test --list-signal-handling
for sig in INT PIPE; do
env --default-signal --ignore-signal=$sig --list-signal-handling true \
2> err8t || fail=1
sed 's/ *(.*)//' err8t > err8 || framework_failure_
env printf '%s: IGNORE\n' "$sig" > exp-err8 || framework_failure_
compare exp-err8 err8 || fail=1
done
Exit $fail
|