* SECTION:gsubprocess
* @title: GSubprocess
* @short_description: Child processes
+ * @include: gio/gio.h
* @see_also: #GSubprocessLauncher
*
* #GSubprocess allows the creation of and interaction with child
* comprehensive API for asynchronous I/O, such
* g_output_stream_splice_async(). This makes GSubprocess
* significantly more powerful and flexible than equivalent APIs in
- * some other languages such as the <literal>subprocess.py</literal>
+ * some other languages such as the `subprocess.py`
* included with Python. For example, using #GSubprocess one could
* create two child processes, reading standard output from the first,
* processing it, and writing to the input stream of the second, all
* without blocking the main loop.
*
* A powerful g_subprocess_communicate() API is provided similar to the
- * <literal>communicate()</literal> method of
- * <literal>subprocess.py</literal>. This enables very easy interaction
- * with a subprocess that has been opened with pipes.
+ * `communicate()` method of `subprocess.py`. This enables very easy
+ * interaction with a subprocess that has been opened with pipes.
*
* #GSubprocess defaults to tight control over the file descriptors open
* in the child process, avoiding dangling-fd issues that are caused by
* change of working directory, child setup functions, etc).
*
* A typical use of #GSubprocess will involve calling
- * g_subprocess_new(), followed by g_subprocess_wait() or
- * g_subprocess_wait_sync(). After the process exits, the status can be
+ * g_subprocess_new(), followed by g_subprocess_wait_async() or
+ * g_subprocess_wait(). After the process exits, the status can be
* checked using functions such as g_subprocess_get_if_exited() (which
* are similar to the familiar WIFEXITED-style POSIX macros).
*
#include <fcntl.h>
#endif
#ifdef G_OS_WIN32
-#define _WIN32_WINNT 0x0500
#include <windows.h>
+#include <io.h>
#include "giowin32-priv.h"
#endif
#define O_BINARY 0
#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#else
+#define HAVE_O_CLOEXEC 1
+#endif
+
#define COMMUNICATE_READ_SIZE 4096
/* A GSubprocess can have two possible states: running and not.
N_PROPS
};
+#ifdef G_OS_UNIX
typedef struct
{
gint fds[3];
}
}
+static int
+dupfd_cloexec (int parent_fd)
+{
+ int fd;
+#ifdef F_DUPFD_CLOEXEC
+ do
+ fd = fcntl (parent_fd, F_DUPFD_CLOEXEC, 3);
+ while (fd == -1 && errno == EINTR);
+#else
+ /* OS X Snow Lion and earlier don't have F_DUPFD_CLOEXEC:
+ * https://bugzilla.gnome.org/show_bug.cgi?id=710962
+ */
+ int result, flags;
+ do
+ fd = fcntl (parent_fd, F_DUPFD, 3);
+ while (fd == -1 && errno == EINTR);
+ flags = fcntl (fd, F_GETFD, 0);
+ if (flags != -1)
+ {
+ flags |= FD_CLOEXEC;
+ do
+ result = fcntl (fd, F_SETFD, flags);
+ while (result == -1 && errno == EINTR);
+ }
+#endif
+ return fd;
+}
+
/**
* Based on code derived from
* gnome-terminal:src/terminal-screen.c:terminal_screen_child_setup(),
gint parent_fd = g_array_index (child_data->needdup_fd_assignments, int, i);
gint new_parent_fd;
- do
- new_parent_fd = fcntl (parent_fd, F_DUPFD_CLOEXEC, 3);
- while (parent_fd == -1 && errno == EINTR);
+ new_parent_fd = dupfd_cloexec (parent_fd);
g_array_index (child_data->needdup_fd_assignments, int, i) = new_parent_fd;
}
if (child_data->child_setup_func)
child_data->child_setup_func (child_data->child_setup_data);
}
+#endif
static GInputStream *
platform_input_stream_from_spawn_fd (gint fd)
g_free (display_name);
/* fall through... */
}
+#ifndef HAVE_O_CLOEXEC
+ else
+ fcntl (my_fd, F_SETFD, FD_CLOEXEC);
+#endif
return my_fd;
}
while (tasks)
{
g_task_return_boolean (tasks->data, TRUE);
+ g_object_unref (tasks->data);
tasks = g_slist_delete_link (tasks, tasks);
}
GError **error)
{
GSubprocess *self = G_SUBPROCESS (initable);
+#ifdef G_OS_UNIX
ChildData child_data = { { -1, -1, -1 }, 0 };
+#endif
gint *pipe_ptrs[3] = { NULL, NULL, NULL };
gint pipe_fds[3] = { -1, -1, -1 };
gint close_fds[3] = { -1, -1, -1 };
spawn_flags |= G_SPAWN_STDERR_TO_DEV_NULL;
else if (self->flags & G_SUBPROCESS_FLAGS_STDERR_PIPE)
pipe_ptrs[2] = &pipe_fds[2];
+#ifdef G_OS_UNIX
else if (self->flags & G_SUBPROCESS_FLAGS_STDERR_MERGE)
/* This will work because stderr gets setup after stdout. */
child_data.fds[2] = 1;
-#ifdef G_OS_UNIX
else if (self->launcher)
{
if (self->launcher->stderr_fd != -1)
spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD;
spawn_flags |= G_SPAWN_CLOEXEC_PIPES;
+#ifdef G_OS_UNIX
child_data.child_setup_func = self->launcher ? self->launcher->child_setup_func : NULL;
child_data.child_setup_data = self->launcher ? self->launcher->child_setup_user_data : NULL;
+#endif
+
success = g_spawn_async_with_pipes (self->launcher ? self->launcher->cwd : NULL,
self->argv,
self->launcher ? self->launcher->envp : NULL,
spawn_flags,
+#ifdef G_OS_UNIX
child_setup, &child_data,
+#else
+ NULL, NULL,
+#endif
&self->pid,
pipe_ptrs[0], pipe_ptrs[1], pipe_ptrs[2],
error);
identifier = (guint64) self->pid;
#endif
- s = snprintf (self->identifier, sizeof self->identifier, "%"G_GUINT64_FORMAT, identifier);
+ s = g_snprintf (self->identifier, sizeof self->identifier, "%"G_GUINT64_FORMAT, identifier);
g_assert (0 < s && s < sizeof self->identifier);
}
g_source_unref (source);
}
+#ifdef G_OS_UNIX
out:
+#endif
/* we don't need this past init... */
self->launcher = NULL;
g_clear_object (&self->stdin_pipe);
g_clear_object (&self->stdout_pipe);
g_clear_object (&self->stderr_pipe);
- g_free (self->argv);
+ g_strfreev (self->argv);
+
+ g_mutex_clear (&self->pending_waits_lock);
G_OBJECT_CLASS (g_subprocess_parent_class)->finalize (object);
}
static void
g_subprocess_init (GSubprocess *self)
{
+ g_mutex_init (&self->pending_waits_lock);
}
static void
* g_subprocess_new: (skip)
* @flags: flags that define the behaviour of the subprocess
* @error: (allow-none): return location for an error, or %NULL
- * @argv0: first commandline argument to pass to the subprocess,
- * followed by more arguments, followed by %NULL
+ * @argv0: first commandline argument to pass to the subprocess
+ * @...: more commandline arguments, followed by %NULL
*
- * Create a new process with the given flags and varargs argument list.
+ * Create a new process with the given flags and varargs argument
+ * list. By default, matching the g_spawn_async() defaults, the
+ * child's stdin will be set to the system null device, and
+ * stdout/stderr will be inherited from the parent. You can use
+ * @flags to control this behavior.
*
* The argument list must be terminated with %NULL.
*
while ((arg = va_arg (ap, const gchar *)))
g_ptr_array_add (args, (gchar *) arg);
g_ptr_array_add (args, NULL);
+ va_end (ap);
result = g_subprocess_newv ((const gchar * const *) args->pdata, flags, error);
/**
* g_subprocess_newv:
- * @argv: commandline arguments for the subprocess
+ * @argv: (array zero-terminated=1) (element-type utf8): commandline arguments for the subprocess
* @flags: flags that define the behaviour of the subprocess
* @error: (allow-none): return location for an error, or %NULL
*
NULL);
}
+/**
+ * g_subprocess_get_identifier:
+ * @subprocess: a #GSubprocess
+ *
+ * On UNIX, returns the process ID as a decimal string.
+ * On Windows, returns the result of GetProcessId() also as a string.
+ */
const gchar *
g_subprocess_get_identifier (GSubprocess *subprocess)
{
* The process must have been created with
* %G_SUBPROCESS_FLAGS_STDIN_PIPE.
*
- * Returns: the stdout pipe
+ * Returns: (transfer none): the stdout pipe
*
* Since: 2.40
**/
* The process must have been created with
* %G_SUBPROCESS_FLAGS_STDOUT_PIPE.
*
- * Returns: the stdout pipe
+ * Returns: (transfer none): the stdout pipe
*
* Since: 2.40
**/
* The process must have been created with
* %G_SUBPROCESS_FLAGS_STDERR_PIPE.
*
- * Returns: the stderr pipe
+ * Returns: (transfer none): the stderr pipe
*
* Since: 2.40
**/
g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE);
g_return_val_if_fail (subprocess->pid == 0, FALSE);
+#ifdef G_OS_UNIX
return WIFEXITED (subprocess->status) && WEXITSTATUS (subprocess->status) == 0;
+#else
+ return subprocess->status == 0;
+#endif
}
/**
g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE);
g_return_val_if_fail (subprocess->pid == 0, FALSE);
+#ifdef G_OS_UNIX
return WIFEXITED (subprocess->status);
+#else
+ return TRUE;
+#endif
}
/**
{
g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), 1);
g_return_val_if_fail (subprocess->pid == 0, 1);
+
+#ifdef G_OS_UNIX
g_return_val_if_fail (WIFEXITED (subprocess->status), 1);
return WEXITSTATUS (subprocess->status);
+#else
+ return subprocess->status;
+#endif
}
/**
g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE);
g_return_val_if_fail (subprocess->pid == 0, FALSE);
+#ifdef G_OS_UNIX
return WIFSIGNALED (subprocess->status);
+#else
+ return FALSE;
+#endif
}
/**
{
g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), 0);
g_return_val_if_fail (subprocess->pid == 0, 0);
+
+#ifdef G_OS_UNIX
g_return_val_if_fail (WIFSIGNALED (subprocess->status), 0);
return WTERMSIG (subprocess->status);
+#else
+ g_critical ("g_subprocess_get_term_sig() called on Windows, where "
+ "g_subprocess_get_if_signaled() always returns FALSE...");
+ return 0;
+#endif
}
/*< private >*/
source == state->stdout_buf ||
source == state->stderr_buf)
{
- if (!g_output_stream_splice_finish ((GOutputStream*)source, result, &error))
+ if (g_output_stream_splice_finish ((GOutputStream*) source, result, &error) == -1)
goto out;
if (source == state->stdout_buf ||
{
CommunicateState *state = data;
+ g_clear_object (&state->cancellable);
g_clear_object (&state->stdin_buf);
g_clear_object (&state->stdout_buf);
g_clear_object (&state->stderr_buf);
- if (!g_source_is_destroyed (state->cancellable_source))
- g_source_destroy (state->cancellable_source);
- g_source_unref (state->cancellable_source);
+ if (state->cancellable_source)
+ {
+ if (!g_source_is_destroyed (state->cancellable_source))
+ g_source_destroy (state->cancellable_source);
+ g_source_unref (state->cancellable_source);
+ }
g_slice_free (CommunicateState, state);
}
g_subprocess_communicate_made_progress, g_object_ref (task));
state->outstanding_ops++;
+ g_object_unref (task);
return state;
}
/**
* g_subprocess_communicate:
* @subprocess: a #GSubprocess
- * @stdin_buf: data to send to the stdin of the subprocess, or %NULL
+ * @stdin_buf: (allow-none): data to send to the stdin of the subprocess, or %NULL
* @cancellable: a #GCancellable
* @stdout_buf: (out): data read from the subprocess stdout
* @stderr_buf: (out): data read from the subprocess stderr
* Communicate with the subprocess until it terminates, and all input
* and output has been completed.
*
- * If @stdin is given, the subprocess must have been created with
+ * If @stdin_buf is given, the subprocess must have been created with
* %G_SUBPROCESS_FLAGS_STDIN_PIPE. The given data is fed to the
* stdin of the subprocess and the pipe is closed (ie: EOF).
*
/**
* g_subprocess_communicate_async:
* @subprocess: Self
- * @stdin_buf: Input data
- * @cancellable: Cancellable
+ * @stdin_buf: (allow-none): Input data, or %NULL
+ * @cancellable: (allow-none): Cancellable
* @callback: Callback
* @user_data: User data
*
/**
* g_subprocess_communicate_utf8:
* @subprocess: a #GSubprocess
- * @stdin_buf: data to send to the stdin of the subprocess, or %NULL
+ * @stdin_buf: (allow-none): data to send to the stdin of the subprocess, or %NULL
* @cancellable: a #GCancellable
* @stdout_buf: (out): data read from the subprocess stdout
* @stderr_buf: (out): data read from the subprocess stderr
GAsyncResult *result = NULL;
gboolean success;
GBytes *stdin_bytes;
+ size_t stdin_buf_len = 0;
g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE);
g_return_val_if_fail (stdin_buf == NULL || (subprocess->flags & G_SUBPROCESS_FLAGS_STDIN_PIPE), FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- stdin_bytes = g_bytes_new (stdin_buf, strlen (stdin_buf));
+ if (stdin_buf != NULL)
+ stdin_buf_len = strlen (stdin_buf);
+ stdin_bytes = g_bytes_new (stdin_buf, stdin_buf_len);
g_subprocess_sync_setup ();
g_subprocess_communicate_internal (subprocess, TRUE, stdin_bytes, cancellable,
/**
* g_subprocess_communicate_utf8_async:
* @subprocess: Self
- * @stdin_buf: Input data
+ * @stdin_buf: (allow-none): Input data, or %NULL
* @cancellable: Cancellable
* @callback: Callback
* @user_data: User data
*
- * Asynchronous version of g_subprocess_communicate_utf(). Complete
+ * Asynchronous version of g_subprocess_communicate_utf8(). Complete
* invocation with g_subprocess_communicate_utf8_finish().
*/
void
gpointer user_data)
{
GBytes *stdin_bytes;
+ size_t stdin_buf_len = 0;
g_return_if_fail (G_IS_SUBPROCESS (subprocess));
g_return_if_fail (stdin_buf == NULL || (subprocess->flags & G_SUBPROCESS_FLAGS_STDIN_PIPE));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- stdin_bytes = g_bytes_new (stdin_buf, strlen (stdin_buf));
+ if (stdin_buf != NULL)
+ stdin_buf_len = strlen (stdin_buf);
+ stdin_bytes = g_bytes_new (stdin_buf, stdin_buf_len);
+
g_subprocess_communicate_internal (subprocess, TRUE, stdin_bytes, cancellable, callback, user_data);
+
g_bytes_unref (stdin_bytes);
}