From d8913abf3b05f701da53cbe39f661a68ffb4d217 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Fri, 18 Jan 2013 13:01:11 -0500 Subject: [PATCH] subprocess WIP --- gio/Makefile.am | 6 +- gio/gio.h | 2 +- gio/gioenums.h | 26 +- gio/giotypes.h | 4 +- gio/gsubprocess.c | 246 ++++---- gio/gsubprocess.h | 70 +-- gio/gsubprocesscontext.c | 307 ---------- gio/gsubprocesscontext.h | 118 ---- ...rivate.h => gsubprocesslauncher-private.h} | 22 +- gio/gsubprocesslauncher.c | 536 ++++++++++++++++++ gio/gsubprocesslauncher.h | 102 ++++ 11 files changed, 831 insertions(+), 608 deletions(-) delete mode 100644 gio/gsubprocesscontext.c delete mode 100644 gio/gsubprocesscontext.h rename gio/{gsubprocesscontext-private.h => gsubprocesslauncher-private.h} (73%) create mode 100644 gio/gsubprocesslauncher.c create mode 100644 gio/gsubprocesslauncher.h diff --git a/gio/Makefile.am b/gio/Makefile.am index fd0e4c0bf..57767d87a 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -424,9 +424,9 @@ libgio_2_0_la_SOURCES = \ gsocketlistener.c \ gsocketoutputstream.c \ gsocketoutputstream.h \ + gsubprocesslauncher.c \ gsubprocess.c \ - gsubprocesscontext.c \ - gsubprocesscontext-private.h \ + gsubprocesslauncher-private.h \ gproxy.c \ gproxyaddress.c \ gproxyaddressenumerator.c \ @@ -592,7 +592,7 @@ gio_headers = \ gsrvtarget.h \ gtask.h \ gsubprocess.h \ - gsubprocesscontext.h \ + gsubprocesslauncher.h \ gtcpconnection.h \ gtcpwrapperconnection.h \ gthreadedsocketservice.h\ diff --git a/gio/gio.h b/gio/gio.h index f52e01b0e..45e9effa3 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -124,7 +124,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/gio/gioenums.h b/gio/gioenums.h index 53798aa5b..7b7dd6b59 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -1659,23 +1659,25 @@ typedef enum /*< flags >*/ { } GTestDBusFlags; /** - * GSubprocessStreamDisposition: - * @G_SUBPROCESS_STREAM_DISPOSITION_NULL: Redirect to operating system's null output stream - * @G_SUBPROCESS_STREAM_DISPOSITION_INHERIT: Keep the stream from the parent process - * @G_SUBPROCESS_STREAM_DISPOSITION_PIPE: Open a private unidirectional channel between the processes - * @G_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE: Only applicable to standard error; causes it to be merged with standard output + * GSubprocessFlags: + * @G_SUBPROCESS_FLAGS_NONE: No flags. * - * Flags to define the behaviour of the standard input/output/error of - * a #GSubprocess. + * Flags to define the behaviour of a #GSubprocess. * * Since: 2.36 **/ typedef enum { - G_SUBPROCESS_STREAM_DISPOSITION_NULL, - G_SUBPROCESS_STREAM_DISPOSITION_INHERIT, - G_SUBPROCESS_STREAM_DISPOSITION_PIPE, - G_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE -} GSubprocessStreamDisposition; + G_SUBPROCESS_FLAGS_NONE = 0, + G_SUBPROCESS_FLAGS_SEARCH_PATH = (1u << 0), + G_SUBPROCESS_FLAGS_SEARCH_PATH_FROM_ENVP = (1u << 1), + G_SUBPROCESS_FLAGS_STDIN_PIPE = (1u << 2), + G_SUBPROCESS_FLAGS_STDIN_INHERIT = (1u << 3), + G_SUBPROCESS_FLAGS_STDOUT_PIPE = (1u << 4), + G_SUBPROCESS_FLAGS_STDOUT_SILENCE = (1u << 5), + G_SUBPROCESS_FLAGS_STDERR_PIPE = (1u << 6), + G_SUBPROCESS_FLAGS_STDERR_SILENCE = (1u << 7), + G_SUBPROCESS_FLAGS_STDERR_MERGE = (1u << 8) +} GSubprocessFlags; G_END_DECLS diff --git a/gio/giotypes.h b/gio/giotypes.h index 30c6701dd..733bc48ad 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -478,13 +478,13 @@ typedef struct _GTestDBus GTestDBus; */ typedef struct _GSubprocess GSubprocess; /** - * GSubprocessContext: + * GSubprocessLauncher: * * Options for launching a child process. * * Since: 2.36 */ -typedef struct _GSubprocessContext GSubprocessContext; +typedef struct _GSubprocessLauncher GSubprocessLauncher; G_END_DECLS diff --git a/gio/gsubprocess.c b/gio/gsubprocess.c index 8e70aaa9b..a2e4105c9 100644 --- a/gio/gsubprocess.c +++ b/gio/gsubprocess.c @@ -38,7 +38,7 @@ #include "config.h" #include "gsubprocess.h" -#include "gsubprocesscontext-private.h" +#include "gsubprocesslauncher-private.h" #include "gasyncresult.h" #include "giostream.h" #include "gmemoryinputstream.h" @@ -77,7 +77,11 @@ struct _GSubprocess { GObject parent; - GSubprocessContext *context; + /* only used during construction */ + GSubprocessFlags flags; + gchar **argv; + + GSubprocessLauncher *launcher; GPid pid; guint pid_valid : 1; @@ -96,7 +100,8 @@ G_DEFINE_TYPE_WITH_CODE (GSubprocess, g_subprocess, G_TYPE_OBJECT, enum { PROP_0, - PROP_CONTEXT, + PROP_FLAGS, + PROP_ARGV, N_PROPS }; @@ -143,27 +148,12 @@ g_subprocess_set_property (GObject *object, switch (prop_id) { - case PROP_CONTEXT: - self->context = g_value_dup_object (value); + case PROP_FLAGS: + self->flags = g_value_get_flags (value); break; - default: - g_assert_not_reached (); - } -} - -static void -g_subprocess_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GSubprocess *self = G_SUBPROCESS (object); - - switch (prop_id) - { - case PROP_CONTEXT: - g_value_set_object (value, self->context); + case PROP_ARGV: + self->argv = g_value_dup_boxed (value); break; default: @@ -177,18 +167,14 @@ g_subprocess_class_init (GSubprocessClass *class) GObjectClass *gobject_class = G_OBJECT_CLASS (class); gobject_class->finalize = g_subprocess_finalize; - gobject_class->get_property = g_subprocess_get_property; gobject_class->set_property = g_subprocess_set_property; - /** - * GSubprocess:context: - * - * - * Since: 2.36 - */ - g_subprocess_pspecs[PROP_CONTEXT] = g_param_spec_object ("context", P_("Context"), P_("Subprocess options"), G_TYPE_SUBPROCESS_CONTEXT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); + g_subprocess_pspecs[PROP_FLAGS] = g_param_spec_flags ("flags", P_("Flags"), P_("Subprocess flags"), + G_TYPE_SUBPROCESS_FLAGS, 0, G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_subprocess_pspecs[PROP_ARGV] = g_param_spec_boxed ("argv", P_("Arguments"), P_("Argument vector"), + G_TYPE_STRV, G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, N_PROPS, g_subprocess_pspecs); } @@ -326,92 +312,79 @@ initable_init (GInitable *initable, * * First, stdin. */ -#ifdef G_OS_UNIX - if (self->context->stdin_fd != -1) - child_data.fds[0] = self->context->stdin_fd; - else if (self->context->stdin_path != NULL) - { - child_data.fds[0] = close_fds[0] = unix_open_file (self->context->stdin_path, - O_RDONLY, error); - if (child_data.fds[0] == -1) - goto out; - } - else -#endif - if (self->context->stdin_disposition == G_SUBPROCESS_STREAM_DISPOSITION_NULL) - ; /* nothing */ - else if (self->context->stdin_disposition == G_SUBPROCESS_STREAM_DISPOSITION_INHERIT) + if (self->flags & G_SUBPROCESS_FLAGS_STDIN_INHERIT) spawn_flags |= G_SPAWN_CHILD_INHERITS_STDIN; - else if (self->context->stdin_disposition == G_SUBPROCESS_STREAM_DISPOSITION_PIPE) + else if (self->flags & G_SUBPROCESS_FLAGS_STDIN_PIPE) pipe_ptrs[0] = &pipe_fds[0]; - else - g_assert_not_reached (); +#ifdef G_OS_UNIX + else if (self->launcher) + { + if (self->launcher->stdin_fd != -1) + child_data.fds[0] = self->launcher->stdin_fd; + else if (self->launcher->stdin_path != NULL) + { + child_data.fds[0] = close_fds[0] = unix_open_file (self->launcher->stdin_path, O_RDONLY, error); + if (child_data.fds[0] == -1) + goto out; + } + } +#endif /* Next, stdout. */ -#ifdef G_OS_UNIX - if (self->context->stdout_fd != -1) - child_data.fds[1] = self->context->stdout_fd; - else if (self->context->stdout_path != NULL) - { - child_data.fds[1] = close_fds[1] = unix_open_file (self->context->stdout_path, - O_CREAT | O_WRONLY, error); - if (child_data.fds[1] == -1) - goto out; - } - else -#endif - if (self->context->stdout_disposition == G_SUBPROCESS_STREAM_DISPOSITION_NULL) + if (self->flags & G_SUBPROCESS_FLAGS_STDOUT_SILENCE) spawn_flags |= G_SPAWN_STDOUT_TO_DEV_NULL; - else if (self->context->stdout_disposition == G_SUBPROCESS_STREAM_DISPOSITION_INHERIT) - ; /* Nothing */ - else if (self->context->stdout_disposition == G_SUBPROCESS_STREAM_DISPOSITION_PIPE) + else if (self->flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE) pipe_ptrs[1] = &pipe_fds[1]; - else - g_assert_not_reached (); +#ifdef G_OS_UNIX + else if (self->launcher) + { + if (self->launcher->stdout_fd != -1) + child_data.fds[1] = self->launcher->stdout_fd; + else if (self->launcher->stdout_path != NULL) + { + child_data.fds[1] = close_fds[1] = unix_open_file (self->launcher->stdout_path, + O_CREAT | O_WRONLY, error); + if (child_data.fds[1] == -1) + goto out; + } + } +#endif /* Finally, stderr. */ -#ifdef G_OS_UNIX - if (self->context->stderr_fd != -1) - child_data.fds[2] = self->context->stderr_fd; - else if (self->context->stderr_path != NULL) - { - child_data.fds[2] = close_fds[2] = unix_open_file (self->context->stderr_path, - O_CREAT | O_WRONLY, error); - if (child_data.fds[2] == -1) - goto out; - } - else -#endif - if (self->context->stderr_disposition == G_SUBPROCESS_STREAM_DISPOSITION_NULL) + if (self->flags & G_SUBPROCESS_FLAGS_STDERR_SILENCE) spawn_flags |= G_SPAWN_STDERR_TO_DEV_NULL; - else if (self->context->stderr_disposition == G_SUBPROCESS_STREAM_DISPOSITION_INHERIT) - ; /* Nothing */ - else if (self->context->stderr_disposition == G_SUBPROCESS_STREAM_DISPOSITION_PIPE) + else if (self->flags & G_SUBPROCESS_FLAGS_STDERR_PIPE) pipe_ptrs[2] = &pipe_fds[2]; - else if (self->context->stderr_disposition == G_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE) - /* This will work because stderr gets setup after stdout. */ - child_data.fds[2] = 1; - else - g_assert_not_reached (); +#ifdef G_OS_UNIX + if (self->launcher) + { + if (self->launcher->stderr_fd != -1) + child_data.fds[2] = self->launcher->stderr_fd; + else if (self->launcher->stderr_path != NULL) + { + child_data.fds[2] = close_fds[2] = unix_open_file (self->launcher->stderr_path, + O_CREAT | O_WRONLY, error); + if (child_data.fds[2] == -1) + goto out; + } + } +#endif - if (self->context->keep_descriptors) - spawn_flags |= G_SPAWN_LEAVE_DESCRIPTORS_OPEN; - - if (self->context->search_path) + if (self->flags & G_SUBPROCESS_FLAGS_SEARCH_PATH) spawn_flags |= G_SPAWN_SEARCH_PATH; - else if (self->context->search_path_from_envp) + + else if (self->flags & G_SUBPROCESS_FLAGS_SEARCH_PATH_FROM_ENVP) spawn_flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP; - else if (!g_path_is_absolute (((gchar**)self->context->argv->pdata)[0])) - spawn_flags |= G_SPAWN_SEARCH_PATH; + spawn_flags |= G_SPAWN_LEAVE_DESCRIPTORS_OPEN; spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD; spawn_flags |= G_SPAWN_CLOEXEC_PIPES; - child_data.child_setup_func = self->context->child_setup_func; - child_data.child_setup_data = self->context->child_setup_data; - success = g_spawn_async_with_pipes (self->context->cwd, - (char**)self->context->argv->pdata, - self->context->envp, + 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; + success = g_spawn_async_with_pipes (self->launcher ? self->launcher->cwd : NULL, + self->argv, + self->launcher ? self->launcher->envp : NULL, spawn_flags, child_setup, &child_data, &self->pid, @@ -439,23 +412,66 @@ initable_iface_init (GInitableIface *initable_iface) } /** - * g_subprocess_new: + * g_subprocess_new: (skip) * - * Create a new process, using the parameters specified by - * GSubprocessContext. + * Create a new process with the given flags and varargs argument list. * - * Returns: (transfer full): A newly created %GSubprocess, or %NULL on error (and @error will be set) + * The argument list must be terminated with %NULL. + * + * Returns: A newly created #GSubprocess, or %NULL on error (and @error + * will be set) * * Since: 2.36 */ -GLIB_AVAILABLE_IN_2_36 GSubprocess * -g_subprocess_new (GSubprocessContext *context, - GError **error) +g_subprocess_new (GSubprocessFlags flags, + GError **error, + const gchar *argv0, + ...) { - return g_initable_new (G_TYPE_SUBPROCESS, - NULL, error, - "context", context, + GSubprocess *result; + GPtrArray *args; + const gchar *arg; + va_list ap; + + g_return_val_if_fail (argv0 != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + args = g_ptr_array_new (); + + va_start (ap, argv0); + g_ptr_array_add (args, (gchar *) argv0); + while ((arg = va_arg (ap, const gchar *))) + g_ptr_array_add (args, (gchar *) arg); + + result = g_subprocess_newv ((const gchar * const *) args->pdata, flags, error); + + g_ptr_array_free (args, TRUE); + + return result; +} + +/** + * g_subprocess_newv: + * + * Create a new process with the given flags and argument list. + * + * The argument list is expected to be %NULL-terminated. + * + * Returns: A newly created #GSubprocess, or %NULL on error (and @error + * will be set) + * + * Since: 2.36 + * Rename to: g_subprocess_new + */ +GSubprocess * +g_subprocess_newv (const gchar * const *argv, + GSubprocessFlags flags, + GError **error) +{ + return g_initable_new (G_TYPE_SUBPROCESS, NULL, error, + "argv", argv, + "flags", flags, NULL); } @@ -566,10 +582,10 @@ g_subprocess_on_child_exited (GPid pid, * Since: 2.36 */ void -g_subprocess_wait (GSubprocess *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_subprocess_wait_async (GSubprocess *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GSource *source; GSubprocessWatchData *data; diff --git a/gio/gsubprocess.h b/gio/gsubprocess.h index 2af5363b2..acd29a273 100644 --- a/gio/gsubprocess.h +++ b/gio/gsubprocess.h @@ -36,68 +36,68 @@ G_BEGIN_DECLS #define G_IS_SUBPROCESS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SUBPROCESS)) GLIB_AVAILABLE_IN_2_36 -GType g_subprocess_get_type (void) G_GNUC_CONST; +GType g_subprocess_get_type (void) G_GNUC_CONST; /**** Core API ****/ GLIB_AVAILABLE_IN_2_36 -GSubprocess * g_subprocess_new (GSubprocessContext *context, - GError **error); +GSubprocess * g_subprocess_new (GSubprocessFlags flags, + GError **error, + const gchar *argv0, + ...) G_GNUC_NULL_TERMINATED; +GLIB_AVAILABLE_IN_2_36 +GSubprocess * g_subprocess_newv (const gchar * const *argv, + GSubprocessFlags flags, + GError **error); GLIB_AVAILABLE_IN_2_36 -GOutputStream * g_subprocess_get_stdin_pipe (GSubprocess *self); +GOutputStream * g_subprocess_get_stdin_pipe (GSubprocess *self); GLIB_AVAILABLE_IN_2_36 -GInputStream * g_subprocess_get_stdout_pipe (GSubprocess *self); +GInputStream * g_subprocess_get_stdout_pipe (GSubprocess *self); GLIB_AVAILABLE_IN_2_36 -GInputStream * g_subprocess_get_stderr_pipe (GSubprocess *self); +GInputStream * g_subprocess_get_stderr_pipe (GSubprocess *self); GLIB_AVAILABLE_IN_2_36 -void g_subprocess_wait (GSubprocess *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); +GPid g_subprocess_get_pid (GSubprocess *self); GLIB_AVAILABLE_IN_2_36 -gboolean g_subprocess_wait_finish (GSubprocess *self, - GAsyncResult *result, - int *out_exit_status, - GError **error); +void g_subprocess_request_exit (GSubprocess *self); GLIB_AVAILABLE_IN_2_36 -gboolean g_subprocess_wait_sync (GSubprocess *self, - int *out_exit_status, - GCancellable *cancellable, - GError **error); +void g_subprocess_force_exit (GSubprocess *self); GLIB_AVAILABLE_IN_2_36 -gboolean g_subprocess_wait_sync_check (GSubprocess *self, - GCancellable *cancellable, - GError **error); +gboolean g_subprocess_wait (GSubprocess *self, + GCancellable *cancellable, + GError **error); GLIB_AVAILABLE_IN_2_36 -GPid g_subprocess_get_pid (GSubprocess *self); +void g_subprocess_wait_async (GSubprocess *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); GLIB_AVAILABLE_IN_2_36 -gboolean g_subprocess_request_exit (GSubprocess *self); +gboolean g_subprocess_wait_finish (GSubprocess *self, + GAsyncResult *result, + GError **error); GLIB_AVAILABLE_IN_2_36 -void g_subprocess_force_exit (GSubprocess *self); - -/** High level helpers **/ +gboolean g_subprocess_get_successful (GSubprocess *self); GLIB_AVAILABLE_IN_2_36 -GSubprocess * g_subprocess_new_simple_argl (GSubprocessStreamDisposition stdout_disposition, - GSubprocessStreamDisposition stderr_disposition, - GError **error, - const char *first_arg, - ...) G_GNUC_NULL_TERMINATED; +gboolean g_subprocess_get_if_exited (GSubprocess *self); + GLIB_AVAILABLE_IN_2_36 -GSubprocess * g_subprocess_new_simple_argv (char **argv, - GSubprocessStreamDisposition stdout_disposition, - GSubprocessStreamDisposition stderr_disposition, - GError **error); +gint g_subprocess_get_exit_status (GSubprocess *self); + +GLIB_AVAILABLE_IN_2_36 +gboolean g_subprocess_get_if_signaled (GSubprocess *self); + +GLIB_AVAILABLE_IN_2_36 +gint g_subprocess_get_term_signal (GSubprocess *self); G_END_DECLS diff --git a/gio/gsubprocesscontext.c b/gio/gsubprocesscontext.c deleted file mode 100644 index c2a0d6942..000000000 --- a/gio/gsubprocesscontext.c +++ /dev/null @@ -1,307 +0,0 @@ -/* GIO - GLib Input, Output and Streaming Library - * - * Copyright © 2012 Red Hat, Inc. - * Copyright © 2012 Canonical Limited - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2 of the licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Authors: Colin Walters - * Ryan Lortie - */ - -/** - * SECTION:gsubprocess - * @title: GSubprocess Context - * @short_description: Environment options for launching a child process - * - * This class contains a set of options for launching child processes, - * such as where its standard input and output will be directed, the - * argument list, the environment, and more. - * - * While the #GSubprocess class has high level functions covering - * popular cases, use of this class allows access to more advanced - * options. It can also be used to launch multiple subprocesses with - * a similar configuration. - * - * Since: 2.36 - */ - -#include "config.h" -#include "gsubprocesscontext-private.h" -#include "gsubprocess.h" -#include "gasyncresult.h" -#include "glibintl.h" -#include "glib-private.h" - -#include - -typedef GObjectClass GSubprocessContextClass; - -G_DEFINE_TYPE (GSubprocessContext, g_subprocess_context, G_TYPE_OBJECT); - -enum -{ - PROP_0, - PROP_ARGV, - N_PROPS -}; - -static GParamSpec *g_subprocess_context_pspecs[N_PROPS]; - -GSubprocessContext * -g_subprocess_context_new (gchar **argv) -{ - return g_object_new (G_TYPE_SUBPROCESS_CONTEXT, - "argv", argv, - NULL); -} - -static void -g_subprocess_context_init (GSubprocessContext *self) -{ - self->argv = g_ptr_array_new_with_free_func (g_free); - self->stdin_fd = -1; - self->stdout_fd = -1; - self->stderr_fd = -1; -} - -static void -g_subprocess_context_finalize (GObject *object) -{ - GSubprocessContext *self = G_SUBPROCESS_CONTEXT (object); - - g_ptr_array_unref (self->argv); - g_strfreev (self->envp); - g_free (self->cwd); - - g_free (self->stdin_path); - g_free (self->stdout_path); - g_free (self->stderr_path); - - if (G_OBJECT_CLASS (g_subprocess_context_parent_class)->finalize != NULL) - G_OBJECT_CLASS (g_subprocess_context_parent_class)->finalize (object); -} - -static void -g_subprocess_context_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GSubprocessContext *self = G_SUBPROCESS_CONTEXT (object); - - switch (prop_id) - { - case PROP_ARGV: - g_subprocess_context_set_args (self, (char**)g_value_get_boxed (value)); - break; - - default: - g_assert_not_reached (); - } -} - -static void -g_subprocess_context_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GSubprocessContext *self = G_SUBPROCESS_CONTEXT (object); - - switch (prop_id) - { - case PROP_ARGV: - g_value_set_boxed (value, self->argv->pdata); - break; - - default: - g_assert_not_reached (); - } -} - -static void -g_subprocess_context_class_init (GSubprocessContextClass *class) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (class); - - gobject_class->finalize = g_subprocess_context_finalize; - gobject_class->get_property = g_subprocess_context_get_property; - gobject_class->set_property = g_subprocess_context_set_property; - - /** - * GSubprocessContext:argv: - * - * Array of arguments passed to child process; must have at least - * one element. The first element has special handling - if it is - * an not absolute path ( as determined by g_path_is_absolute() ), - * then the system search path will be used. See - * %G_SPAWN_SEARCH_PATH. - * - * Note that in order to use the Unix-specific argv0 functionality, - * you must use the setter function - * g_subprocess_context_set_args_and_argv0(). For more information - * about this, see %G_SPAWN_FILE_AND_ARGV_ZERO. - * - * Since: 2.36 - */ - g_subprocess_context_pspecs[PROP_ARGV] = g_param_spec_boxed ("argv", P_("Arguments"), P_("Arguments for child process"), G_TYPE_STRV, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (gobject_class, N_PROPS, g_subprocess_context_pspecs); -} - -/* Only exported on Unix */ -#ifndef G_OS_UNIX -static -#endif -void -g_subprocess_context_set_args_and_argv0 (GSubprocessContext *self, - const gchar *argv0, - gchar **args) -{ - gchar **iter; - - g_ptr_array_set_size (self->argv, 0); - - if (argv0) - g_ptr_array_add (self->argv, g_strdup (argv0)); - - for (iter = args; *iter; iter++) - g_ptr_array_add (self->argv, g_strdup (*iter)); - g_ptr_array_add (self->argv, NULL); -} - -void -g_subprocess_context_set_args (GSubprocessContext *self, - gchar **args) -{ - g_subprocess_context_set_args_and_argv0 (self, NULL, args); -} - -/* Environment */ - -void -g_subprocess_context_set_environment (GSubprocessContext *self, - gchar **environ) -{ - g_strfreev (self->envp); - self->envp = g_strdupv (environ); -} - -void -g_subprocess_context_set_cwd (GSubprocessContext *self, - const gchar *cwd) -{ - g_free (self->cwd); - self->cwd = g_strdup (cwd); -} - -void -g_subprocess_context_set_keep_descriptors (GSubprocessContext *self, - gboolean keep_descriptors) - -{ - self->keep_descriptors = keep_descriptors ? 1 : 0; -} - -void -g_subprocess_context_set_search_path (GSubprocessContext *self, - gboolean search_path, - gboolean search_path_from_envp) -{ - self->search_path = search_path ? 1 : 0; - self->search_path_from_envp = search_path_from_envp ? 1 : 0; -} - -void -g_subprocess_context_set_stdin_disposition (GSubprocessContext *self, - GSubprocessStreamDisposition disposition) -{ - g_return_if_fail (disposition != G_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE); - self->stdin_disposition = disposition; -} - -void -g_subprocess_context_set_stdout_disposition (GSubprocessContext *self, - GSubprocessStreamDisposition disposition) -{ - g_return_if_fail (disposition != G_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE); - self->stdout_disposition = disposition; -} - -void -g_subprocess_context_set_stderr_disposition (GSubprocessContext *self, - GSubprocessStreamDisposition disposition) -{ - self->stderr_disposition = disposition; -} - -#ifdef G_OS_UNIX -void -g_subprocess_context_set_stdin_file_path (GSubprocessContext *self, - const gchar *path) -{ - self->stdin_disposition = G_SUBPROCESS_STREAM_DISPOSITION_NULL; - g_free (self->stdin_path); - self->stdin_path = g_strdup (path); -} - -void -g_subprocess_context_set_stdin_fd (GSubprocessContext *self, - gint fd) -{ - self->stdin_disposition = G_SUBPROCESS_STREAM_DISPOSITION_NULL; - self->stdin_fd = fd; -} - -void -g_subprocess_context_set_stdout_file_path (GSubprocessContext *self, - const gchar *path) -{ - self->stdout_disposition = G_SUBPROCESS_STREAM_DISPOSITION_NULL; - g_free (self->stdout_path); - self->stdout_path = g_strdup (path); -} - -void -g_subprocess_context_set_stdout_fd (GSubprocessContext *self, - gint fd) -{ - self->stdout_disposition = G_SUBPROCESS_STREAM_DISPOSITION_NULL; - self->stdout_fd = fd; -} - -void -g_subprocess_context_set_stderr_file_path (GSubprocessContext *self, - const gchar *path) -{ - self->stderr_disposition = G_SUBPROCESS_STREAM_DISPOSITION_NULL; - g_free (self->stderr_path); - self->stderr_path = g_strdup (path); -} - -void -g_subprocess_context_set_stderr_fd (GSubprocessContext *self, - gint fd) -{ - self->stderr_disposition = G_SUBPROCESS_STREAM_DISPOSITION_NULL; - self->stderr_fd = fd; -} -#endif - -#ifdef G_OS_UNIX -void -g_subprocess_context_set_child_setup (GSubprocessContext *self, - GSpawnChildSetupFunc child_setup, - gpointer user_data) -{ - self->child_setup_func = child_setup; - self->child_setup_data = user_data; -} -#endif diff --git a/gio/gsubprocesscontext.h b/gio/gsubprocesscontext.h deleted file mode 100644 index 118aef973..000000000 --- a/gio/gsubprocesscontext.h +++ /dev/null @@ -1,118 +0,0 @@ -/* GIO - GLib Input, Output and Streaming Library - * - * Copyright © 2012 Colin Walters - * Copyright © 2012 Canonical Limited - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: Ryan Lortie - * Author: Colin Walters - */ - -#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __G_SUBPROCESS_CONTEXT_H__ -#define __G_SUBPROCESS_CONTEXT_H__ - -#include - -G_BEGIN_DECLS - -#define G_TYPE_SUBPROCESS_CONTEXT (g_subprocess_context_get_type ()) -#define G_SUBPROCESS_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SUBPROCESS_CONTEXT, GSubprocessContext)) -#define G_IS_SUBPROCESS_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SUBPROCESS_CONTEXT)) - -GLIB_AVAILABLE_IN_2_36 -GType g_subprocess_context_get_type (void) G_GNUC_CONST; - -GLIB_AVAILABLE_IN_2_36 -GSubprocessContext * g_subprocess_context_new (gchar **argv); - -/* Argument control */ -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_args (GSubprocessContext *self, - gchar **args); -#ifdef G_OS_UNIX -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_args_and_argv0 (GSubprocessContext *self, - const gchar *argv0, - gchar **args); -#endif - -/* Environment */ - -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_environment (GSubprocessContext *self, - gchar **environ); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_cwd (GSubprocessContext *self, - const gchar *cwd); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_keep_descriptors (GSubprocessContext *self, - gboolean keep_descriptors); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_search_path (GSubprocessContext *self, - gboolean search_path, - gboolean search_path_from_envp); - -/* Basic I/O control */ - -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_stdin_disposition (GSubprocessContext *self, - GSubprocessStreamDisposition disposition); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_stdout_disposition (GSubprocessContext *self, - GSubprocessStreamDisposition disposition); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_stderr_disposition (GSubprocessContext *self, - GSubprocessStreamDisposition disposition); - -/* Extended I/O control, only available on UNIX */ - -#ifdef G_OS_UNIX -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_stdin_file_path (GSubprocessContext *self, - const gchar *path); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_stdin_fd (GSubprocessContext *self, - gint fd); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_stdout_file_path (GSubprocessContext *self, - const gchar *path); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_stdout_fd (GSubprocessContext *self, - gint fd); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_stderr_file_path (GSubprocessContext *self, - const gchar *path); -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_stderr_fd (GSubprocessContext *self, - gint fd); -#endif - -/* Child setup, only available on UNIX */ -#ifdef G_OS_UNIX -GLIB_AVAILABLE_IN_2_36 -void g_subprocess_context_set_child_setup (GSubprocessContext *self, - GSpawnChildSetupFunc child_setup, - gpointer user_data); -#endif - -G_END_DECLS - -#endif /* __G_SUBPROCESS_H__ */ diff --git a/gio/gsubprocesscontext-private.h b/gio/gsubprocesslauncher-private.h similarity index 73% rename from gio/gsubprocesscontext-private.h rename to gio/gsubprocesslauncher-private.h index 39ceafa4c..812722feb 100644 --- a/gio/gsubprocesscontext-private.h +++ b/gio/gsubprocesslauncher-private.h @@ -21,29 +21,19 @@ #ifndef __G_SUBPROCESS_CONTEXT_PRIVATE_H__ #define __G_SUBPROCESS_CONTEXT_PRIVATE_H__ -#include "gsubprocesscontext.h" +#include "gsubprocesslauncher.h" G_BEGIN_DECLS -struct _GSubprocessContext +struct _GSubprocessLauncher { GObject parent; - GSpawnFlags flags; - GPtrArray *argv; - gboolean has_argv0; + GSubprocessFlags flags; char **envp; char *cwd; - GSubprocessStreamDisposition stdin_disposition; - GSubprocessStreamDisposition stdout_disposition; - GSubprocessStreamDisposition stderr_disposition; - - guint keep_descriptors : 1; - guint search_path : 1; - guint search_path_from_envp : 1; - guint unused_flags : 29; - +#ifdef G_OS_UNIX gint stdin_fd; gchar *stdin_path; @@ -54,7 +44,9 @@ struct _GSubprocessContext gchar *stderr_path; GSpawnChildSetupFunc child_setup_func; - gpointer child_setup_data; + gpointer child_setup_user_data; + GDestroyNotify child_setup_destroy_notify; +#endif }; G_END_DECLS diff --git a/gio/gsubprocesslauncher.c b/gio/gsubprocesslauncher.c new file mode 100644 index 000000000..9ebb9e861 --- /dev/null +++ b/gio/gsubprocesslauncher.c @@ -0,0 +1,536 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2012 Red Hat, Inc. + * Copyright © 2012 Canonical Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Authors: Colin Walters + * Ryan Lortie + */ + +/** + * SECTION:gsubprocess + * @title: GSubprocess Launcher + * @short_description: Environment options for launching a child process + * + * This class contains a set of options for launching child processes, + * such as where its standard input and output will be directed, the + * argument list, the environment, and more. + * + * While the #GSubprocess class has high level functions covering + * popular cases, use of this class allows access to more advanced + * options. It can also be used to launch multiple subprocesses with + * a similar configuration. + * + * Since: 2.36 + */ + +#define ALL_STDIN_FLAGS (G_SUBPROCESS_FLAGS_STDIN_PIPE | \ + G_SUBPROCESS_FLAGS_STDIN_INHERIT) +#define ALL_STDOUT_FLAGS (G_SUBPROCESS_FLAGS_STDOUT_PIPE | \ + G_SUBPROCESS_FLAGS_STDOUT_SILENCE) +#define ALL_STDERR_FLAGS (G_SUBPROCESS_FLAGS_STDERR_PIPE | \ + G_SUBPROCESS_FLAGS_STDERR_SILENCE | \ + G_SUBPROCESS_FLAGS_STDERR_MERGE) + +#include "config.h" + +#include "gsubprocesslauncher-private.h" +#include "gioenumtypes.h" + +typedef GObjectClass GSubprocessLauncherClass; + +G_DEFINE_TYPE (GSubprocessLauncher, g_subprocess_launcher, G_TYPE_OBJECT); + +static void +g_subprocess_launcher_finalize (GObject *object) +{ + GSubprocessLauncher *self = G_SUBPROCESS_LAUNCHER (object); + + g_strfreev (self->envp); + g_free (self->cwd); + + g_free (self->stdin_path); + g_free (self->stdout_path); + g_free (self->stderr_path); + + if (self->child_setup_destroy_notify) + (* self->child_setup_destroy_notify) (self->child_setup_user_data); + + if (G_OBJECT_CLASS (g_subprocess_launcher_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_subprocess_launcher_parent_class)->finalize (object); +} + +static void +g_subprocess_launcher_init (GSubprocessLauncher *self) +{ + self->envp = g_listenv (); + + self->stdin_fd = -1; + self->stdout_fd = -1; + self->stderr_fd = -1; +} + +static void +g_subprocess_launcher_class_init (GSubprocessLauncherClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + gobject_class->finalize = g_subprocess_launcher_finalize; +} + +/** + * g_subprocess_launcher_new: + * + * Creates a new #GSubprocessLauncher. + * + * The launcher is created with the default options. A copy of the + * environment of the calling process is made at the time of this call + * and will be used as the environment that the process is launched in. + * + * Since: 2.36 + **/ +GSubprocessLauncher * +g_subprocess_launcher_new (void) +{ + return g_object_new (G_TYPE_SUBPROCESS_LAUNCHER, NULL); +} + + +/** + * g_subprocess_launcher_set_environ: + * @self: a #GSubprocess + * @environ: the replacement environment + * + * Replace the entire environment of processes launched from this + * launcher with the given 'environ' variable. + * + * Typically you will build this variable by using g_listenv() to copy + * the process 'environ' and using the functions g_environ_setenv(), + * g_environ_unsetenv(), etc. + * + * As an alternative, you can use g_subprocess_launcher_setenv(), + * g_subprocess_launcher_unsetenv(), etc. + * + * All strings in this array are expected to be in the GLib file name + * encoding. On UNIX, this means that they can be arbitrary byte + * strings. On Windows, they should be in UTF-8. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_environ (GSubprocessLauncher *self, + gchar **environ) +{ + g_strfreev (self->envp); + self->envp = g_strdupv (environ); +} + +/** + * g_subprocess_launcher_setenv: + * @self: a #GSubprocess + * @variable: the environment variable to set, must not contain '=' + * @value: the new value for the variable + * @overwrite: whether to change the variable if it already exists + * + * Sets the environment variable @variable in the environment of + * processes launched from this launcher. + * + * Both the variable's name and value should be in the GLib file name + * encoding. On UNIX, this means that they can be arbitrary byte + * strings. On Windows, they should be in UTF-8. + * + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_setenv (GSubprocessLauncher *self, + const gchar *variable, + const gchar *value, + gboolean overwrite) +{ + self->envp = g_environ_setenv (self->envp, variable, value, overwrite); +} + +/** + * g_subprocess_launcher_unsetsenv: + * @self: a #GSubprocess + * @variable: the environment variable to unset, must not contain '=' + * + * Removes the environment variable @variable from the environment of + * processes launched from this launcher. + * + * The variable name should be in the GLib file name encoding. On UNIX, + * this means that they can be arbitrary byte strings. On Windows, they + * should be in UTF-8. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_unsetenv (GSubprocessLauncher *self, + const gchar *variable) +{ + self->envp = g_environ_unsetenv (self->envp, variable); +} + +/** + * g_subprocess_launcher_getenv: + * @self: a #GSubprocess + * @variable: the environment variable to get + * + * Returns the value of the environment variable @variable in the + * environment of processes launched from this launcher. + * + * The returned string is in the GLib file name encoding. On UNIX, this + * means that it can be an arbitrary byte string. On Windows, it will + * be UTF-8. + * + * Returns: the value of the environment variable, %NULL if unset + * + * Since: 2.36 + **/ +const gchar * +g_subprocess_launcher_getenv (GSubprocessLauncher *self, + const gchar *variable) +{ + return g_environ_getenv (self->envp, variable); +} + +/** + * g_subprocess_launcher_set_cwd: + * @self: a #GSubprocess + * @cwd: the cwd for launched processes + * + * Sets the current working directory that processes will be launched + * with. + * + * By default processes are launched with the current working directory + * of the launching process at the time of launch. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_cwd (GSubprocessLauncher *self, + const gchar *cwd) +{ + g_free (self->cwd); + self->cwd = g_strdup (cwd); +} + +static gboolean +verify_disposition (const gchar *stream_name, + GSubprocessFlags filtered_flags, + gint fd, + const gchar *filename) +{ + guint n_bits; + + if (!filtered_flags) + n_bits = 0; + else if (((filtered_flags - 1) & filtered_flags) == 0) + n_bits = 1; + else + n_bits = 2; /* ...or more */ + + if (n_bits + (fd >= 0) + (filename != NULL) > 1) + { + GString *err; + + err = g_string_new (NULL); + if (n_bits) + { + GFlagsClass *class; + GFlagsValue *value; + + class = g_type_class_peek (G_TYPE_SUBPROCESS_FLAGS); + while ((value = g_flags_get_first_value (class, filtered_flags))) + { + g_string_append_printf (err, " %s", value->value_name); + filtered_flags &= value->value; + } + + g_type_class_unref (class); + } + + if (fd >= 0) + g_string_append_printf (err, " g_subprocess_launcher_set_%s_fd()", stream_name); + + if (filename) + g_string_append_printf (err, " g_subprocess_launcher_set_%s_file_path()", stream_name); + + g_critical ("You may specify at most one disposition for the %s stream, but you specified:%s.", + stream_name, err->str); + g_string_free (err, TRUE); + + return FALSE; + } + + return TRUE; +} + +/** + * g_subprocess_launcher_set_flags: + * @self: a #GSubprocessLauncher + * @flags: #GSubprocessFlags + * + * Sets the flags on the launcher. + * + * The default flags are %G_SUBPROCESS_FLAGS_NONE. + * + * You may not set flags that specify conflicting options for how to + * handle a particular stdio stream (eg: specifying both + * %G_SUBPROCESS_FLAGS_STDIN_PIPE and + * %G_SUBPROCESS_FLAGS_STDIN_INHERIT). + * + * You may also not set a flag that conflicts with a previous call to a + * function like g_subprocess_launcher_set_stdin_file_path() or + * g_subprocess_launcher_set_stdout_fd(). + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_flags (GSubprocessLauncher *self, + GSubprocessFlags flags) +{ + if (verify_disposition ("stdin", flags & ALL_STDIN_FLAGS, self->stdin_fd, self->stdin_path) && + verify_disposition ("stdout", flags & ALL_STDOUT_FLAGS, self->stdout_fd, self->stdout_path) && + verify_disposition ("stderr", flags & ALL_STDERR_FLAGS, self->stderr_fd, self->stderr_path)) + self->flags = flags; +} + +#ifdef G_OS_UNIX +/** + * g_subprocess_launcher_set_stdin_file_path: + * @self: a #GSubprocessLauncher + * @path: a filename or %NULL + * + * Sets the file path to use as the stdin for spawned processes. + * + * If @path is %NULL then any previously given path is unset. + * + * The file must exist or spawning the process will fail. + * + * You may not set a stdin file path if a stdin fd is already set or if + * the launcher flags contain any flags directing stdin elsewhere. + * + * This feature is only available on UNIX. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_stdin_file_path (GSubprocessLauncher *self, + const gchar *path) +{ + if (verify_disposition ("stdin", self->flags & ALL_STDIN_FLAGS, self->stdin_fd, path)) + { + g_free (self->stdin_path); + self->stdin_path = g_strdup (path); + } +} + +/** + * g_subprocess_launcher_set_stdin_fd: + * @self: a #GSubprocessLauncher + * @fd: a file descriptor, or -1 + * + * Sets the file descriptor to use as the stdin for spawned processes. + * + * If @fd is -1 then any previously given fd is unset. + * + * Note that if your intention is to have the stdin of the calling + * process inherited by the child then %G_SUBPROCESS_FLAGS_STDIN_INHERIT + * is a better way to go about doing that. + * + * The passed @fd is noted but will not be touched in the current + * process. It is therefore necessary that it be kept open by the + * caller until the subprocess is spawned. The file descriptor will + * also not be explicitly closed on the child side, so it must be marked + * O_CLOEXEC if that's what you want. + * + * You may not set a stdin fd if a stdin file path is already set or if + * the launcher flags contain any flags directing stdin elsewhere. + * + * This feature is only available on UNIX. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_stdin_fd (GSubprocessLauncher *self, + gint fd) +{ + if (verify_disposition ("stdin", self->flags & ALL_STDIN_FLAGS, fd, self->stdin_path)) + self->stdin_fd = fd; +} + +/** + * g_subprocess_launcher_set_stdout_file_path: + * @self: a #GSubprocessLauncher + * @path: a filename or %NULL + * + * Sets the file path to use as the stdout for spawned processes. + * + * If @path is %NULL then any previously given path is unset. + * + * The file will be created or truncated when the process is spawned, as + * would be the case if using '>' at the shell. + * + * You may not set a stdout file path if a stdout fd is already set or + * if the launcher flags contain any flags directing stdout elsewhere. + * + * This feature is only available on UNIX. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_stdout_file_path (GSubprocessLauncher *self, + const gchar *path) +{ + if (verify_disposition ("stdout", self->flags & ALL_STDIN_FLAGS, self->stdout_fd, path)) + { + g_free (self->stdout_path); + self->stdout_path = g_strdup (path); + } +} + +/** + * g_subprocess_launcher_set_stdout_fd: + * @self: a #GSubprocessLauncher + * @fd: a file descriptor, or -1 + * + * Sets the file descriptor to use as the stdout for spawned processes. + * + * If @fd is -1 then any previously given fd is unset. + * + * Note that the default behaviour is to pass stdout through to the + * stdout of the parent process. + * + * The passed @fd is noted but will not be touched in the current + * process. It is therefore necessary that it be kept open by the + * caller until the subprocess is spawned. The file descriptor will + * also not be explicitly closed on the child side, so it must be marked + * O_CLOEXEC if that's what you want. + * + * You may not set a stdout fd if a stdout file path is already set or + * if the launcher flags contain any flags directing stdout elsewhere. + * + * This feature is only available on UNIX. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_stdout_fd (GSubprocessLauncher *self, + gint fd) +{ + if (verify_disposition ("stdin", self->flags & ALL_STDIN_FLAGS, fd, self->stdin_path)) + self->stdout_fd = fd; +} + +/** + * g_subprocess_launcher_set_stderr_file_path: + * @self: a #GSubprocessLauncher + * @path: a filename or %NULL + * + * Sets the file path to use as the stderr for spawned processes. + * + * If @path is %NULL then any previously given path is unset. + * + * The file will be created or truncated when the process is spawned, as + * would be the case if using '2>' at the shell. + * + * If you want to send both stdout and stderr to the same file then use + * %G_SUBPROCESS_FLAGS_STDERR_MERGE. + * + * You may not set a stderr file path if a stderr fd is already set or + * if the launcher flags contain any flags directing stderr elsewhere. + * + * This feature is only available on UNIX. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_stderr_file_path (GSubprocessLauncher *self, + const gchar *path) +{ + if (verify_disposition ("stderr", self->flags & ALL_STDIN_FLAGS, self->stderr_fd, path)) + { + g_free (self->stderr_path); + self->stderr_path = g_strdup (path); + } +} + +/** + * g_subprocess_launcher_set_stderr_fd: + * @self: a #GSubprocessLauncher + * @fd: a file descriptor, or -1 + * + * Sets the file descriptor to use as the stderr for spawned processes. + * + * If @fd is -1 then any previously given fd is unset. + * + * Note that the default behaviour is to pass stderr through to the + * stderr of the parent process. + * + * The passed @fd is noted but will not be touched in the current + * process. It is therefore necessary that it be kept open by the + * caller until the subprocess is spawned. The file descriptor will + * also not be explicitly closed on the child side, so it must be marked + * O_CLOEXEC if that's what you want. + * + * You may not set a stderr fd if a stderr file path is already set or + * if the launcher flags contain any flags directing stderr elsewhere. + * + * This feature is only available on UNIX. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_stderr_fd (GSubprocessLauncher *self, + gint fd) +{ + if (verify_disposition ("stdin", self->flags & ALL_STDIN_FLAGS, fd, self->stdin_path)) + self->stderr_fd = fd; +} + +/** + * g_subprocess_launcher_set_child_setup: + * @self: a #GSubprocessLauncher + * @child_setup: a #GSpawnChildSetupFunc to use as the child setup function + * @user_data: user data for @child_setup + * @destroy_notify: a #GDestroyNotify for @user_data + * + * Sets up a child setup function. + * + * The child setup function will be called after fork() but before + * exec() on the child's side. + * + * @destroy_notify will not be automatically called on the child's side + * of the fork(). It will only be called when the last reference on the + * #GSubprocessLauncher is dropped or when a new child setup function is + * given. + * + * %NULL can be given as @child_setup to disable the functionality. + * + * Child setup functions are only available on UNIX. + * + * Since: 2.36 + **/ +void +g_subprocess_launcher_set_child_setup (GSubprocessLauncher *self, + GSpawnChildSetupFunc child_setup, + gpointer user_data, + GDestroyNotify destroy_notify) +{ + if (self->child_setup_destroy_notify) + (* self->child_setup_destroy_notify) (self->child_setup_user_data); + + self->child_setup_func = child_setup; + self->child_setup_user_data = user_data; + self->child_setup_destroy_notify = destroy_notify; +} +#endif diff --git a/gio/gsubprocesslauncher.h b/gio/gsubprocesslauncher.h new file mode 100644 index 000000000..642f8bc17 --- /dev/null +++ b/gio/gsubprocesslauncher.h @@ -0,0 +1,102 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2012 Colin Walters + * Copyright © 2012 Canonical Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Ryan Lortie + * Author: Colin Walters + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_SUBPROCESS_LAUNCHER_H__ +#define __G_SUBPROCESS_LAUNCHER_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_SUBPROCESS_LAUNCHER (g_subprocess_launcher_get_type ()) +#define G_SUBPROCESS_LAUNCHER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SUBPROCESS_LAUNCHER, GSubprocessLauncher)) +#define G_IS_SUBPROCESS_LAUNCHER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SUBPROCESS_LAUNCHER)) + +GLIB_AVAILABLE_IN_2_36 +GType g_subprocess_launcher_get_type (void) G_GNUC_CONST; + +GLIB_AVAILABLE_IN_2_36 +GSubprocessLauncher * g_subprocess_launcher_new (void); + +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_environ (GSubprocessLauncher *self, + gchar **environ); + +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_setenv (GSubprocessLauncher *self, + const gchar *variable, + const gchar *value, + gboolean overwrite); + +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_unsetenv (GSubprocessLauncher *self, + const gchar *variable); + +GLIB_AVAILABLE_IN_2_36 +const gchar * g_subprocess_launcher_getenv (GSubprocessLauncher *self, + const gchar *variable); + +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_cwd (GSubprocessLauncher *self, + const gchar *cwd); +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_flags (GSubprocessLauncher *self, + GSubprocessFlags flags); + +/* Extended I/O control, only available on UNIX */ +#ifdef G_OS_UNIX +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_stdin_file_path (GSubprocessLauncher *self, + const gchar *path); +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_stdin_fd (GSubprocessLauncher *self, + gint fd); +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_stdout_file_path (GSubprocessLauncher *self, + const gchar *path); +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_stdout_fd (GSubprocessLauncher *self, + gint fd); +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_stderr_file_path (GSubprocessLauncher *self, + const gchar *path); +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_stderr_fd (GSubprocessLauncher *self, + gint fd); + +/* Child setup, only available on UNIX */ +GLIB_AVAILABLE_IN_2_36 +void g_subprocess_launcher_set_child_setup (GSubprocessLauncher *self, + GSpawnChildSetupFunc child_setup, + gpointer user_data, + GDestroyNotify destroy_notify); +#endif + +G_END_DECLS + +#endif /* __G_SUBPROCESS_H__ */