summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2013-11-26 02:47:36 +0000
committerPádraig Brady <P@draigBrady.com>2013-11-27 01:21:23 +0000
commit39e0c9ba5d7a37401a30e1304282142e525e334c (patch)
tree2ad5539c95cb1b532f5a467a90ed8b2c778b7fd1
parent17bce8c63e9e0f85b48ca62b63413ce9102af5c1 (diff)
downloadcoreutils-39e0c9ba5d7a37401a30e1304282142e525e334c.tar.xz
sort: avoid issues when issuing diagnostics from child processes
* src/sort.c: (async_safe_die): A new limited version of error(), that outputs fixed strings and unconverted errnos to stderr. This is safe to call in the limited context of a signal handler, or in this particular case, between the fork() and exec() of a multithreaded process. (move_fd_or_die): Use the async_safe_die() rather than error(). (maybe_create_temp): Likewise. (open_temp): Likewise. Fixes http://bugs.gnu.org/15970
-rw-r--r--src/sort.c46
1 files changed, 39 insertions, 7 deletions
diff --git a/src/sort.c b/src/sort.c
index bb4e3136e..573d6cbad 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -373,6 +373,34 @@ static bool debug;
number are present, temp files will be used. */
static unsigned int nmerge = NMERGE_DEFAULT;
+/* Output an error to stderr using async-signal-safe routines, and _exit().
+ This can be used safely from signal handlers,
+ and between fork() and exec() of multithreaded processes. */
+
+static void async_safe_die (int, const char *) ATTRIBUTE_NORETURN;
+static void
+async_safe_die (int errnum, const char *errstr)
+{
+ ignore_value (write (STDERR_FILENO, errstr, strlen (errstr)));
+
+ /* Even if defined HAVE_STRERROR_R, we can't use it,
+ as it may return a translated string etc. and even if not
+ may malloc() which is unsafe. We might improve this
+ by testing for sys_errlist and using that if available.
+ For now just report the error number. */
+ if (errnum)
+ {
+ char errbuf[INT_BUFSIZE_BOUND (errnum)];
+ char *p = inttostr (errnum, errbuf);
+ ignore_value (write (STDERR_FILENO, ": errno ", 8));
+ ignore_value (write (STDERR_FILENO, p, strlen (p)));
+ }
+
+ ignore_value (write (STDERR_FILENO, "\n", 1));
+
+ _exit (SORT_FAILURE);
+}
+
/* Report MESSAGE for FILE, then clean up and exit.
If FILE is null, it represents standard output. */
@@ -982,8 +1010,8 @@ move_fd_or_die (int oldfd, int newfd)
{
if (oldfd != newfd)
{
- if (dup2 (oldfd, newfd) < 0)
- error (SORT_FAILURE, errno, _("dup2 failed"));
+ /* This should never fail for our usage. */
+ dup2 (oldfd, newfd);
close (oldfd);
}
}
@@ -1095,13 +1123,15 @@ maybe_create_temp (FILE **pfp, bool survive_fd_exhaustion)
}
else if (node->pid == 0)
{
+ /* Being the child of a multithreaded program before exec(),
+ we're restricted to calling async-signal-safe routines here. */
close (pipefds[1]);
move_fd_or_die (tempfd, STDOUT_FILENO);
move_fd_or_die (pipefds[0], STDIN_FILENO);
- if (execlp (compress_program, compress_program, (char *) NULL) < 0)
- error (SORT_FAILURE, errno, _("couldn't execute %s"),
- compress_program);
+ execlp (compress_program, compress_program, (char *) NULL);
+
+ async_safe_die (errno, "couldn't execute compress program");
}
}
@@ -1153,13 +1183,15 @@ open_temp (struct tempnode *temp)
break;
case 0:
+ /* Being the child of a multithreaded program before exec(),
+ we're restricted to calling async-signal-safe routines here. */
close (pipefds[0]);
move_fd_or_die (tempfd, STDIN_FILENO);
move_fd_or_die (pipefds[1], STDOUT_FILENO);
execlp (compress_program, compress_program, "-d", (char *) NULL);
- error (SORT_FAILURE, errno, _("couldn't execute %s -d"),
- compress_program);
+
+ async_safe_die (errno, "couldn't execute compress program (with -d)");
default:
temp->pid = child;