gsubprocess: Globally ignore SIGPIPE

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 <pwithnall@gnome.org>

Fixes: #3310
This commit is contained in:
Philip Withnall 2024-04-01 14:41:33 +01:00
parent 573b4e35d1
commit ef461fb391

View File

@ -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 isnt 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;