mirror of
https://https.git.savannah.gnu.org/git/coreutils.git
synced 2026-01-26 15:29:07 +00:00
sort: fix silent exit upon SIGPIPE from --compress-program
* src/sort.c (main): Ignore SIGPIPE so we've more control over how we handle for stdout and compression programs. (sort_die): Handle EPIPE from stdout and mimic a standard SIGPIPE, otherwise reverting to a standard exit(SORT_FAILURE); * tests/sort/sort-compress-proc.sh: Add a test case. * NEWS: Mention the bug fix.
This commit is contained in:
parent
7bd932c34e
commit
b294aff3fe
4
NEWS
4
NEWS
@ -28,6 +28,10 @@ GNU coreutils NEWS -*- outline -*-
|
||||
Although these directories are nonempty, 'rmdir DIR' succeeds on them.
|
||||
[bug introduced in coreutils-8.16]
|
||||
|
||||
'sort --compress-program' now diagnoses if it can't write more data to an
|
||||
exited compressor. Previously sort could have exited silently in this case.
|
||||
[bug introduced in coreutils-6.8]
|
||||
|
||||
'tail' outputs the correct number of lines again for non-small -n values.
|
||||
Previously it may have output too few lines.
|
||||
[bug introduced in coreutils-9.8]
|
||||
|
||||
89
src/sort.c
89
src/sort.c
@ -371,12 +371,57 @@ static bool debug;
|
||||
number are present, temp files will be used. */
|
||||
static unsigned int nmerge = NMERGE_DEFAULT;
|
||||
|
||||
/* Whether SIGPIPE had the default disposition at startup. */
|
||||
static bool default_SIGPIPE;
|
||||
|
||||
/* The list of temporary files. */
|
||||
struct tempnode
|
||||
{
|
||||
struct tempnode *volatile next;
|
||||
pid_t pid; /* The subprocess PID; undefined if state == UNCOMPRESSED. */
|
||||
char state;
|
||||
char name[FLEXIBLE_ARRAY_MEMBER];
|
||||
};
|
||||
static struct tempnode *volatile temphead;
|
||||
static struct tempnode *volatile *temptail = &temphead;
|
||||
|
||||
/* Clean up any remaining temporary files. */
|
||||
|
||||
static void
|
||||
cleanup (void)
|
||||
{
|
||||
struct tempnode const *node;
|
||||
|
||||
for (node = temphead; node; node = node->next)
|
||||
unlink (node->name);
|
||||
temphead = nullptr;
|
||||
}
|
||||
|
||||
/* Handle interrupts and hangups. */
|
||||
|
||||
static void
|
||||
sighandler (int sig)
|
||||
{
|
||||
if (! SA_NOCLDSTOP)
|
||||
signal (sig, SIG_IGN);
|
||||
|
||||
cleanup ();
|
||||
|
||||
signal (sig, SIG_DFL);
|
||||
raise (sig);
|
||||
}
|
||||
|
||||
/* Report MESSAGE for FILE, then clean up and exit.
|
||||
If FILE is null, it represents standard output. */
|
||||
|
||||
static void
|
||||
sort_die (char const *message, char const *file)
|
||||
{
|
||||
/* If we got EPIPE writing to stdout (from a previous fwrite() or fclose()
|
||||
and SIGPIPE was originally SIG_DFL, mimic standard SIGPIPE behavior. */
|
||||
if (errno == EPIPE && !file && default_SIGPIPE)
|
||||
sighandler (SIGPIPE);
|
||||
|
||||
error (SORT_FAILURE, errno, "%s: %s", message,
|
||||
quotef (file ? file : _("standard output")));
|
||||
}
|
||||
@ -631,17 +676,6 @@ cs_leave (struct cs_status const *status)
|
||||
the subprocess to finish. */
|
||||
enum { UNCOMPRESSED, UNREAPED, REAPED };
|
||||
|
||||
/* The list of temporary files. */
|
||||
struct tempnode
|
||||
{
|
||||
struct tempnode *volatile next;
|
||||
pid_t pid; /* The subprocess PID; undefined if state == UNCOMPRESSED. */
|
||||
char state;
|
||||
char name[FLEXIBLE_ARRAY_MEMBER];
|
||||
};
|
||||
static struct tempnode *volatile temphead;
|
||||
static struct tempnode *volatile *temptail = &temphead;
|
||||
|
||||
/* A file to be sorted. */
|
||||
struct sortfile
|
||||
{
|
||||
@ -780,18 +814,6 @@ reap_all (void)
|
||||
reap (-1);
|
||||
}
|
||||
|
||||
/* Clean up any remaining temporary files. */
|
||||
|
||||
static void
|
||||
cleanup (void)
|
||||
{
|
||||
struct tempnode const *node;
|
||||
|
||||
for (node = temphead; node; node = node->next)
|
||||
unlink (node->name);
|
||||
temphead = nullptr;
|
||||
}
|
||||
|
||||
/* Cleanup actions to take when exiting. */
|
||||
|
||||
static void
|
||||
@ -4262,20 +4284,6 @@ parse_field_count (char const *string, size_t *val, char const *msgid)
|
||||
return suffix;
|
||||
}
|
||||
|
||||
/* Handle interrupts and hangups. */
|
||||
|
||||
static void
|
||||
sighandler (int sig)
|
||||
{
|
||||
if (! SA_NOCLDSTOP)
|
||||
signal (sig, SIG_IGN);
|
||||
|
||||
cleanup ();
|
||||
|
||||
signal (sig, SIG_DFL);
|
||||
raise (sig);
|
||||
}
|
||||
|
||||
/* Set the ordering options for KEY specified in S.
|
||||
Return the address of the first character in S that
|
||||
is not a valid ordering option.
|
||||
@ -4409,7 +4417,7 @@ main (int argc, char **argv)
|
||||
static int const sig[] =
|
||||
{
|
||||
/* The usual suspects. */
|
||||
SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
|
||||
SIGALRM, SIGHUP, SIGINT, SIGQUIT, SIGTERM,
|
||||
#ifdef SIGPOLL
|
||||
SIGPOLL,
|
||||
#endif
|
||||
@ -4457,6 +4465,11 @@ main (int argc, char **argv)
|
||||
}
|
||||
signal (SIGCHLD, SIG_DFL); /* Don't inherit CHLD handling from parent. */
|
||||
|
||||
/* Ignore SIGPIPE so write failures are reported via EPIPE errno.
|
||||
For stdout, sort_die() will reraise SIGPIPE if it was originally SIG_DFL.
|
||||
For compression pipes, sort_die() will exit with SORT_FAILURE. */
|
||||
default_SIGPIPE = (signal (SIGPIPE, SIG_IGN) == SIG_DFL);
|
||||
|
||||
/* The signal mask is known, so it is safe to invoke exit_cleanup. */
|
||||
atexit (exit_cleanup);
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
|
||||
print_ver_ sort
|
||||
print_ver_ sort kill
|
||||
expensive_
|
||||
|
||||
# Terminate any background processes
|
||||
@ -25,9 +25,9 @@ cleanup_() { kill $pid 2>/dev/null && wait $pid; }
|
||||
|
||||
SORT_FAILURE=2
|
||||
|
||||
seq -w 2000 > exp || fail=1
|
||||
tac exp > in || fail=1
|
||||
insize=$(stat -c %s - <in) || fail=1
|
||||
seq -w 2000 > exp || framework_failure_
|
||||
tac exp > in || framework_failure_
|
||||
insize=$(stat -c %s - <in) || framework_failure_
|
||||
|
||||
# This compressor's behavior is adjustable via environment variables.
|
||||
export PRE_COMPRESS=
|
||||
@ -42,6 +42,24 @@ EOF
|
||||
|
||||
chmod +x compress
|
||||
|
||||
# "Early exit" test
|
||||
#
|
||||
# In this test, the compressor exits before reading all (any) data.
|
||||
# Until coreutils 9.9 'sort' could get a SIGPIPE writing to the
|
||||
# exited processes and silently exit. Note the same issue can happen
|
||||
# irrespective of exit status. It's more likely to happen in the
|
||||
# case of the child exiting with success, and if we write more data
|
||||
# (hence the --batch-size=30 and double "in"). Note we check sort doesn't
|
||||
# get SIGPIPE rather than if it returns SORT_FAILURE, because there is
|
||||
# the theoretical possibility that the kernel could buffer the
|
||||
# amount of data we're writing here and not issue the EPIPE to sort.
|
||||
# In other words we currently may not detect failures in the extreme edge case
|
||||
# of writing a small amount of data to a compressor that exits 0
|
||||
# while not reading all the data presented.
|
||||
PRE_COMPRESS='exit 0' \
|
||||
sort --compress-program=./compress -S 1k --batch-size=30 ./in ./in > out
|
||||
test $(env kill -l $?) = 'PIPE' && fail=1
|
||||
|
||||
# "Impatient exit" tests
|
||||
#
|
||||
# In these test cases, the biggest compressor (or decompressor) exits
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user