gsubprocess: Fix up communicate

We weren't closing the streams after we were done reading or writing,
which is kind of essential.  The easy way to fix this is to just use
g_output_stream_splice() to a GMemoryOutputStream rather than
hand-rolling it.  This results in a substantial reduction of code
complexity.

A second serious issue is that we were marking the task as complete when
the process exits, but that's racy - there could still be data to read
from stdout.  Fix this by just refcounting outstanding operations.

This code, not surprisingly, looks a lot like the "multi" test.

Next, because processes output binary data, I'd be forced to annotate
the char*/length pairs as (array) (element-type uint8).  But rather than
doing that, it's *far* simpler to just use GBytes.

We need a version of this that actually validates as UTF-8, that will be
in the next patch.
This commit is contained in:
Colin Walters
2013-10-15 00:12:22 +01:00
committed by Ryan Lortie
parent 5b48dc40cc
commit 0e1a3ee345
5 changed files with 198 additions and 344 deletions

View File

@@ -130,6 +130,7 @@ static void
g_subprocess_launcher_finalize (GObject *object)
{
GSubprocessLauncher *self = G_SUBPROCESS_LAUNCHER (object);
guint i;
g_strfreev (self->envp);
g_free (self->cwd);
@@ -148,8 +149,18 @@ g_subprocess_launcher_finalize (GObject *object)
if (self->stderr_fd != -1)
close (self->stderr_fd);
g_clear_pointer (&self->basic_fd_assignments, g_array_unref);
g_clear_pointer (&self->needdup_fd_assignments, g_array_unref);
if (self->basic_fd_assignments)
{
for (i = 0; i < self->basic_fd_assignments->len; i++)
(void) close (g_array_index (self->basic_fd_assignments, int, i));
g_array_unref (self->basic_fd_assignments);
}
if (self->needdup_fd_assignments)
{
for (i = 0; i < self->needdup_fd_assignments->len; i += 2)
(void) close (g_array_index (self->needdup_fd_assignments, int, i));
g_array_unref (self->needdup_fd_assignments);
}
#endif
if (self->child_setup_destroy_notify)
@@ -570,23 +581,26 @@ g_subprocess_launcher_take_stderr_fd (GSubprocessLauncher *self,
}
/**
* g_subprocess_launcher_pass_fd:
* g_subprocess_launcher_take_fd:
* @self: a #GSubprocessLauncher
* @source_fd: File descriptor in parent process
* @target_fd: Target descriptor for child process
*
* Pass an arbitrary file descriptor from parent process to
* the child. By default, all file descriptors from the parent
* will be closed. This function allows you to create (for example)
* a custom pipe() or socketpair() before launching the process, and
* choose the target descriptor in the child.
* Transfer an arbitrary file descriptor from parent process to the
* child. This function takes "ownership" of the fd; it will be closed
* in the parent when @self is freed.
*
* By default, all file descriptors from the parent will be closed.
* This function allows you to create (for example) a custom pipe() or
* socketpair() before launching the process, and choose the target
* descriptor in the child.
*
* An example use case is GNUPG, which has a command line argument
* --passphrase-fd providing a file descriptor number where it expects
* the passphrase to be written.
*/
void
g_subprocess_launcher_pass_fd (GSubprocessLauncher *self,
g_subprocess_launcher_take_fd (GSubprocessLauncher *self,
gint source_fd,
gint target_fd)
{