From ef461fb39199f0c365c5cf272b12265db665db89 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 1 Apr 2024 14:41:33 +0100 Subject: [PATCH] gsubprocess: Globally ignore SIGPIPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like we already do in `GSocket`. This is necessary when using `g_subprocess_communicate()` with a subprocess which calls `close()` on its stdin FD at some point. `cat` does this just before exiting, for example. This causes a `write()` to the stdin pipe in the parent process to fail with `EPIPE` and `SIGPIPE`. The condition is not detectable in advance, because the `close()` call could happen after the `GMainContext` has dispatched a `g_subprocess_communicate()` callback. If it weren’t for the `SIGPIPE`,`g_subprocess_communicate()` would be able to handle the `EPIPE` just fine. `SIGPIPE` seems like a default error handling path which was useful in 1980 for writing pipe-heavy command line apps, but which is more of a broken stair for writing larger modern apps which have more than one data flow path. Signed-off-by: Philip Withnall Fixes: #3310 --- gio/gsubprocess.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/gio/gsubprocess.c b/gio/gsubprocess.c index 6af94db80..204b0a606 100644 --- a/gio/gsubprocess.c +++ b/gio/gsubprocess.c @@ -77,6 +77,12 @@ * checked using functions such as [method@Gio.Subprocess.get_if_exited] (which * are similar to the familiar `WIFEXITED`-style POSIX macros). * + * Note that as of GLib 2.82, creating a `GSubprocess` causes the signal + * `SIGPIPE` to be ignored for the remainder of the program. If you are writing + * a command-line utility that uses `GSubprocess`, you may need to take into + * account the fact that your program will not automatically be killed + * if it tries to write to `stdout` after it has been closed. + * * Since: 2.40 **/ @@ -493,6 +499,23 @@ g_subprocess_class_init (GSubprocessClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); +#ifdef SIGPIPE + /* There is no portable, thread-safe way to avoid having the process + * be killed by SIGPIPE when calling write() on a pipe to a subprocess, so we + * are forced to simply ignore the signal process-wide. + * + * This can happen if `G_SUBPROCESS_FLAGS_STDIN_PIPE` is used and the + * subprocess calls close() on its stdin FD while the parent process is + * running g_subprocess_communicate(). + * + * Even if we ignore it though, gdb will still stop if the app + * receives a SIGPIPE, which can be confusing and annoying. In `gsocket.c`, + * we can handily also set `MSG_NO_SIGNAL` / `SO_NOSIGPIPE`, but unfortunately + * there isn’t an equivalent of those for `pipe2`() FDs. + */ + signal (SIGPIPE, SIG_IGN); +#endif + gobject_class->finalize = g_subprocess_finalize; gobject_class->set_property = g_subprocess_set_property;