From: Pádraig Brady
Date: Tue, 28 Oct 2025 19:30:08 +0000 (+0000)
Subject: sort: fix silent exit upon SIGPIPE from --compress-program
X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b294aff3fe6c8ebeda02b8c77877ba618474de41;p=thirdparty%2Fcoreutils.git
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.
---
diff --git a/NEWS b/NEWS
index 96b54e108b..0cc760aeed 100644
--- a/NEWS
+++ b/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]
diff --git a/src/sort.c b/src/sort.c
index 7127f671b6..78b2f69f96 100644
--- a/src/sort.c
+++ b/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);
diff --git a/tests/sort/sort-compress-proc.sh b/tests/sort/sort-compress-proc.sh
index c410b7fce0..2033429244 100755
--- a/tests/sort/sort-compress-proc.sh
+++ b/tests/sort/sort-compress-proc.sh
@@ -17,7 +17,7 @@
# along with this program. If not, see