diff options
Diffstat (limited to 'gio/gsubprocess.c')
-rw-r--r-- | gio/gsubprocess.c | 839 |
1 files changed, 304 insertions, 535 deletions
diff --git a/gio/gsubprocess.c b/gio/gsubprocess.c index 93923ff86..0034ea0a6 100644 --- a/gio/gsubprocess.c +++ b/gio/gsubprocess.c @@ -1,6 +1,7 @@ /* 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 @@ -8,6 +9,9 @@ * your option) any later version. * * See the included COPYING file for more information. + * + * Authors: Colin Walters <walters@verbum.org> + * Ryan Lortie <desrt@desrt.ca> */ /** @@ -64,19 +68,9 @@ #define O_BINARY 0 #endif -typedef enum { - G_SUBPROCESS_STREAM_DISPOSITION_DEVNULL = 0, - G_SUBPROCESS_STREAM_DISPOSITION_INHERIT = 1, - G_SUBPROCESS_STREAM_DISPOSITION_PIPE = 2, - G_SUBPROCESS_STREAM_DISPOSITION_MERGE_STDOUT -#define G_SUBPROCESS_STREAM_DISPOSITION_LAST (G_SUBPROCESS_STREAM_DISPOSITION_MERGE_STDOUT) -} GSubprocessStreamDispositionKind; - -static GObject * get_disposition (GSubprocessStreamDispositionKind kind); - static void initable_iface_init (GInitableIface *initable_iface); -typedef struct _GSubprocessClass GSubprocessClass; +typedef GObjectClass GSubprocessClass; #ifdef G_OS_UNIX static void @@ -89,48 +83,34 @@ struct _GSubprocessStreamDispositionClass { GObjectClass parent_class; }; -struct _GSubprocessClass { - GObjectClass parent_class; -}; - struct _GSubprocess { GObject parent; + GSubprocessFlags flags; char **argv; - GSpawnFlags spawn_flags; char **envp; char *cwd; - GSpawnChildSetupFunc child_setup; - gpointer child_setup_user_data; - GObject *stdin_disposition; - GObject *stdout_disposition; - GObject *stderr_disposition; - GPid pid; - guint reaped_child : 1; - guint merge_stderr_to_stdout : 1; - guint reserved : 30; - int internal_stdin_fd; - int internal_stdout_fd; - int internal_stderr_fd; + /* These are the objects that are passed in to the constructor for the + * stdin, stdout and stderr properties (ie: GFileDescriptorBased or + * GFile). + */ + GObject *stdin; + GObject *stdout; + GObject *stderr; - GOutputStream *stdin_stream; - GInputStream *stdout_stream; - GInputStream *stderr_stream; -}; + gboolean reaped_child; -struct _GSubprocessStreamDisposition -{ - GObject parent; - GSubprocessStreamDispositionKind kind; + /* These are the streams created if a pipe is requested via flags. */ + GOutputStream *stdin_pipe; + GInputStream *stdout_pipe; + GInputStream *stderr_pipe; }; G_DEFINE_TYPE_WITH_CODE (GSubprocess, g_subprocess, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)); - -G_DEFINE_TYPE (GSubprocessStreamDisposition, g_subprocess_stream_disposition, G_TYPE_OBJECT); + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)); enum { @@ -138,29 +118,18 @@ enum PROP_ARGV, PROP_WORKING_DIRECTORY, PROP_ENVIRONMENT, - PROP_SPAWN_FLAGS, - PROP_CHILD_SETUP_FUNC, - PROP_CHILD_SETUP_USER_DATA, - PROP_STDIN_DISPOSITION, - PROP_STDOUT_DISPOSITION, - PROP_STDERR_DISPOSITION + PROP_FLAGS, + PROP_STDIN, + PROP_STDOUT, + PROP_STDERR, + N_PROPS }; -static void -g_subprocess_init (GSubprocess *self) -{ - self->stdin_disposition = get_disposition (G_SUBPROCESS_STREAM_DISPOSITION_DEVNULL); - self->stdout_disposition = get_disposition (G_SUBPROCESS_STREAM_DISPOSITION_INHERIT); - self->stderr_disposition = get_disposition (G_SUBPROCESS_STREAM_DISPOSITION_INHERIT); - self->internal_stdin_fd = -1; - self->internal_stdout_fd = -1; - self->internal_stderr_fd = -1; -} +static GParamSpec *g_subprocess_pspecs[N_PROPS]; static void -g_subprocess_stream_disposition_init (GSubprocessStreamDisposition *self) +g_subprocess_init (GSubprocess *self) { - self->kind = G_SUBPROCESS_STREAM_DISPOSITION_INHERIT; } static void @@ -178,13 +147,13 @@ g_subprocess_finalize (GObject *object) #endif g_spawn_close_pid (self->pid); - g_clear_object (&self->stdin_disposition); - g_clear_object (&self->stdout_disposition); - g_clear_object (&self->stderr_disposition); + g_clear_object (&self->stdin); + g_clear_object (&self->stdout); + g_clear_object (&self->stderr); - g_clear_object (&self->stdin_stream); - g_clear_object (&self->stdout_stream); - g_clear_object (&self->stderr_stream); + g_clear_object (&self->stdin); + g_clear_object (&self->stdout); + g_clear_object (&self->stderr); g_strfreev (self->argv); g_strfreev (self->envp); @@ -196,9 +165,9 @@ g_subprocess_finalize (GObject *object) static void g_subprocess_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) + guint prop_id, + const GValue *value, + GParamSpec *pspec) { GSubprocess *self = G_SUBPROCESS (object); @@ -216,28 +185,20 @@ g_subprocess_set_property (GObject *object, self->envp = g_value_dup_boxed (value); break; - case PROP_SPAWN_FLAGS: - self->spawn_flags = g_value_get_flags (value); + case PROP_FLAGS: + self->flags = g_value_get_flags (value); break; - case PROP_CHILD_SETUP_FUNC: - self->child_setup = g_value_get_pointer (value); + case PROP_STDIN: + self->stdin = g_value_dup_object (value); break; - case PROP_CHILD_SETUP_USER_DATA: - self->child_setup_user_data = g_value_get_pointer (value); + case PROP_STDOUT: + self->stdout = g_value_dup_object (value); break; - case PROP_STDIN_DISPOSITION: - self->stdin_disposition = g_value_dup_object (value); - break; - - case PROP_STDOUT_DISPOSITION: - self->stdout_disposition = g_value_dup_object (value); - break; - - case PROP_STDERR_DISPOSITION: - self->stderr_disposition = g_value_dup_object (value); + case PROP_STDERR: + self->stderr = g_value_dup_object (value); break; default: @@ -247,16 +208,16 @@ g_subprocess_set_property (GObject *object, static void g_subprocess_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) + guint prop_id, + GValue *value, + GParamSpec *pspec) { GSubprocess *self = G_SUBPROCESS (object); switch (prop_id) { case PROP_ARGV: - g_value_set_boxed (value, (gpointer)self->argv); + g_value_set_boxed (value, self->argv); break; case PROP_WORKING_DIRECTORY: @@ -264,31 +225,23 @@ g_subprocess_get_property (GObject *object, break; case PROP_ENVIRONMENT: - g_value_set_boxed (value, (gpointer)self->envp); - break; - - case PROP_SPAWN_FLAGS: - g_value_set_flags (value, self->spawn_flags); + g_value_set_boxed (value, self->envp); break; - case PROP_CHILD_SETUP_FUNC: - g_value_set_pointer (value, self->child_setup); + case PROP_FLAGS: + g_value_set_flags (value, self->flags); break; - case PROP_CHILD_SETUP_USER_DATA: - g_value_set_pointer (value, self->child_setup_user_data); + case PROP_STDIN: + g_value_set_object (value, self->stdin); break; - case PROP_STDIN_DISPOSITION: - g_value_set_object (value, self->stdin_disposition); + case PROP_STDOUT: + g_value_set_object (value, self->stdout); break; - case PROP_STDOUT_DISPOSITION: - g_value_set_object (value, self->stdout_disposition); - break; - - case PROP_STDERR_DISPOSITION: - g_value_set_object (value, self->stderr_disposition); + case PROP_STDERR: + g_value_set_object (value, self->stderr); break; default: @@ -314,12 +267,9 @@ g_subprocess_class_init (GSubprocessClass *class) * * Since: 2.34 */ - g_object_class_install_property (gobject_class, PROP_ARGV, - g_param_spec_boxed ("argv", - P_("Arguments"), - P_("Argument list"), - G_TYPE_STRV, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_subprocess_pspecs[PROP_ARGV] = g_param_spec_boxed ("argv", P_("Arguments"), P_("Argument list"), G_TYPE_STRV, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); /** * GSubprocess:working-directory: @@ -330,12 +280,10 @@ g_subprocess_class_init (GSubprocessClass *class) * * Since: 2.34 */ - g_object_class_install_property (gobject_class, PROP_WORKING_DIRECTORY, - g_param_spec_string ("working-directory", - P_("Working Directory"), - P_("Path to working directory"), - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_subprocess_pspecs[PROP_WORKING_DIRECTORY] = g_param_spec_string ("working-directory", P_("Working Directory"), + P_("Path to working directory"), NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); /** * GSubprocess:environment: @@ -345,12 +293,10 @@ g_subprocess_class_init (GSubprocessClass *class) * * Since: 2.34 */ - g_object_class_install_property (gobject_class, PROP_ENVIRONMENT, - g_param_spec_boxed ("environment", - P_("Environment"), - P_("Environment for child process"), - G_TYPE_STRV, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_subprocess_pspecs[PROP_ENVIRONMENT] = g_param_spec_boxed ("environment", P_("Environment"), + P_("Environment for child process"), G_TYPE_STRV, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); /** * GSubprocess:spawn-flags: @@ -359,89 +305,45 @@ g_subprocess_class_init (GSubprocessClass *class) * * Since: 2.34 */ - g_object_class_install_property (gobject_class, PROP_SPAWN_FLAGS, - g_param_spec_flags ("spawn-flags", - P_("Spawn Flags"), - P_("Additional flags conrolling the subprocess"), - G_TYPE_SPAWN_FLAGS, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - - - /** - * GSubprocess:child-setup-func: - * - * Only available on Unix; this function will be invoked between - * <literal>fork()</literal> and <literal>exec()</literal>. See the - * documentation of g_spawn_async_with_pipes() for more details. - * - * Since: 2.34 - */ - g_object_class_install_property (gobject_class, PROP_CHILD_SETUP_FUNC, - g_param_spec_pointer ("child-setup-func", - P_("Setup"), - P_("Child setup function"), - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_subprocess_pspecs[PROP_FLAGS] = g_param_spec_flags ("flags", P_("Spawn Flags"), + P_("Additional flags conrolling the subprocess"), + G_TYPE_SUBPROCESS_FLAGS, 0, G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** - * GSubprocess:child-setup-user-data: - * - * User data passed to the %GSubprocess:child-setup-func. - * - * Since: 2.34 - */ - g_object_class_install_property (gobject_class, PROP_CHILD_SETUP_USER_DATA, - g_param_spec_pointer ("child-setup-user-data", - P_("Data"), - P_("Child setup user data"), - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - - /** - * GSubprocess:stdin-disposition: + * GSubprocess:stdin: * * Controls the direction of standard input. * * Since: 2.34 */ - g_object_class_install_property (gobject_class, PROP_STDIN_DISPOSITION, - g_param_spec_object ("stdin-disposition", - P_("Stdin"), - P_("Disposition of standard input"), - G_TYPE_OBJECT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_subprocess_pspecs[PROP_STDIN] = g_param_spec_object ("stdin", P_("Stdin"), P_("Disposition of standard input"), + G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); /** - * GSubprocess:stdout-disposition: + * GSubprocess:stdout: * * Controls the direction of standard output. * * Since: 2.34 */ - g_object_class_install_property (gobject_class, PROP_STDOUT_DISPOSITION, - g_param_spec_object ("stdout-disposition", - P_("Stdout"), - P_("Disposition of standard output"), - G_TYPE_OBJECT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_subprocess_pspecs[PROP_STDOUT] = g_param_spec_object ("stdout", P_("Stdout"), P_("Disposition of standard output"), + G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); /** - * GSubprocess:stderr-disposition: + * GSubprocess:stderr: * * Controls the direction of standard error. * * Since: 2.34 */ - g_object_class_install_property (gobject_class, PROP_STDERR_DISPOSITION, - g_param_spec_object ("stderr-disposition", - P_("Stderr"), - P_("Disposition of standard error"), - G_TYPE_OBJECT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_subprocess_pspecs[PROP_STDERR] = g_param_spec_object ("stderr", P_("Stderr"), P_("Disposition of standard error"), + G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); -} - -static void -g_subprocess_stream_disposition_class_init (GSubprocessStreamDispositionClass *klass) -{ + g_object_class_install_properties (gobject_class, N_PROPS, g_subprocess_pspecs); } #ifdef G_OS_UNIX @@ -457,7 +359,7 @@ g_subprocess_unix_queue_waitpid (GSubprocess *self) { GMainContext *worker_context; GSource *waitpid_source; - + worker_context = GLIB_PRIVATE_CALL (g_get_worker_context) (); waitpid_source = g_child_watch_source_new (self->pid); g_source_set_callback (waitpid_source, g_subprocess_unix_waitpid_dummy, NULL, NULL); @@ -465,345 +367,267 @@ g_subprocess_unix_queue_waitpid (GSubprocess *self) g_source_unref (waitpid_source); } -static void -safe_dup2 (gint a, - gint b) -{ - gint ecode; - - if (a == b) - return; - - do - ecode = dup2 (a, b); - while (ecode == -1 && errno == EINTR); -} - -static void -g_subprocess_internal_child_setup (gpointer user_data) -{ - GSubprocess *self = user_data; - - if (self->internal_stdin_fd >= 0) - safe_dup2 (self->internal_stdin_fd, 0); - - if (self->internal_stdout_fd >= 0) - safe_dup2 (self->internal_stdout_fd, 1); - - if (self->internal_stderr_fd >= 0) - safe_dup2 (self->internal_stderr_fd, 2); - - if (self->merge_stderr_to_stdout) - safe_dup2 (1, 2); - - if (self->child_setup) - self->child_setup (self->child_setup_user_data); -} - #endif static GInputStream * -platform_input_stream_from_spawn_fd (gint fd) +platform_input_stream_from_spawn_fd (gint fd) { + if (fd < 0) + return NULL; + #ifdef G_OS_UNIX return g_unix_input_stream_new (fd, TRUE); #else return g_win32_input_stream_new_from_fd (fd, TRUE); -#endif +#endif } static GOutputStream * -platform_output_stream_from_spawn_fd (gint fd) +platform_output_stream_from_spawn_fd (gint fd) { + if (fd < 0) + return NULL; + #ifdef G_OS_UNIX return g_unix_output_stream_new (fd, TRUE); #else return g_win32_output_stream_new_from_fd (fd, TRUE); -#endif +#endif } -static void -set_open_error (const char *filename, - GError **error) +#ifdef G_OS_UNIX +static gint +unix_open_file (GFile *file, + gint mode, + GError **error) { - int errsv = errno; - char *display_name = g_filename_display_name (filename); - g_set_error (error, G_IO_ERROR, - g_io_error_from_errno (errsv), - _("Error opening file '%s': %s"), - display_name, g_strerror (errsv)); - g_free (display_name); + gchar *filename; + gint my_fd; -} + filename = g_file_get_path (file); -static int -open_file_write_append (const char *filename, - GError **error) -{ - int fd; - - fd = g_open (filename, O_WRONLY | O_CREAT | O_BINARY, 0666); - if (fd == -1) + if (filename == NULL) { - set_open_error (filename, error); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("File does not have a local path")); return -1; } - return fd; + + my_fd = g_open (filename, mode | O_BINARY | O_CLOEXEC, 0666); + + /* If we return -1 we should also set the error */ + if (my_fd < 0) + { + gint saved_errno = errno; + char *display_name; + + display_name = g_filename_display_name (filename); + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (saved_errno), + _("Error opening file '%s': %s"), display_name, + g_strerror (saved_errno)); + g_free (display_name); + /* fall through... */ + } + + g_free (filename); + + return my_fd; } static gboolean -get_file_path_or_fail (GFile *f, - char **out_path, - GError **error) +unix_get_fd_for_object_read (GObject *object, + gint *fd, + gint *close_fd, + GError **error) { - *out_path = g_file_get_path (f); - if (!*out_path) + gint my_fd; + + if (G_IS_FILE_DESCRIPTOR_BASED (object)) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "File does not have a local path"); - return FALSE; + *fd = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (object)); + return TRUE; } - return TRUE; -} -static void -set_unsupported_stream_error (GObject *object, - GError **error) -{ - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "Unsupported stream type %s", g_type_name (G_OBJECT_TYPE (object))); + g_assert (G_IS_FILE (object)); + + my_fd = unix_open_file (G_FILE (object), O_RDONLY, error); + + if (my_fd < 0) + return FALSE; + + *fd = *close_fd = my_fd; + + return TRUE; } static gboolean -unix_handle_open_fd_for_write (GObject *disposition, - gboolean *out_supported, - gint *out_fd, - gboolean *out_close_fd, - GError **error) +unix_get_fd_for_object_write (GObject *object, + gint *fd, + gint *close_fd, + GError **error) { - gboolean ret = FALSE; - char *temp_path = NULL; + gint my_fd; -#ifdef G_OS_UNIX - if (G_IS_FILE_DESCRIPTOR_BASED (disposition)) + if (G_IS_FILE_DESCRIPTOR_BASED (object)) { - *out_fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*) disposition); - *out_close_fd = FALSE; - *out_supported = TRUE; + *fd = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (object)); + return TRUE; } - else if (G_IS_FILE (disposition)) - { - if (!get_file_path_or_fail ((GFile*)disposition, &temp_path, error)) - goto out; - - *out_fd = open_file_write_append (temp_path, error); - if (*out_fd == -1) - goto out; - *out_close_fd = TRUE; - *out_supported = TRUE; - } - else - *out_supported = FALSE; - ret = TRUE; - out: - g_clear_pointer (&temp_path, g_free); - return ret; -#else - *out_supported = FALSE; + g_assert (G_IS_FILE (object)); + + my_fd = unix_open_file (G_FILE (object), O_CREAT | O_WRONLY, error); + + if (my_fd < 0) + return FALSE; + + *fd = *close_fd = my_fd; + return TRUE; +} +#endif + +typedef struct +{ + gint fds[3]; + GSubprocessFlags flags; +} ChildData; + +static void +child_setup (gpointer user_data) +{ + ChildData *child_data = user_data; + gint i; + + /* We're on the child side now. "Rename" the file descriptors in + * child_data.fds[] to stdin/stdout/stderr. + * + * We don't close the originals. It's possible that the originals + * should not be closed and if they should be closed then they should + * have been created O_CLOEXEC. + */ + for (i = 0; i < 3; i++) + if (child_data->fds[i] != -1 && child_data->fds[i] != i) + { + gint result; + + do + result = dup2 (child_data->fds[i], i); + while (result == -1 && errno == EINTR); + } + + if (child_data->flags & G_SUBPROCESS_FLAGS_TERM_WITH_PARENT) + /* XXX linux thing... */; + +#ifdef G_OS_UNIX + if (child_data->flags & G_SUBPROCESS_FLAGS_NEW_SESSION) + setsid (); #endif } static gboolean initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) + GCancellable *cancellable, + GError **error) { GSubprocess *self = G_SUBPROCESS (initable); - gboolean ret = FALSE; - char *temp_path = NULL; - gint stdin_pipe_fd = -1; - gint *stdin_arg = NULL; - gint stdout_pipe_fd = -1; - gint *stdout_arg = NULL; - gint stderr_pipe_fd = -1; - gint *stderr_arg = NULL; - gboolean disposition_supported; - GSpawnChildSetupFunc child_setup; - gpointer child_setup_user_data; - gboolean close_stdin_fd = FALSE; - gboolean close_stdout_fd = FALSE; - gboolean close_stderr_fd = FALSE; + ChildData child_data = { { -1, -1, -1 } }; + gint *pipe_ptrs[3] = { NULL, NULL, NULL }; + gint pipe_fds[3] = { -1, -1, -1 }; + gint close_fds[3] = { -1, -1, -1 }; + GSpawnFlags spawn_flags = 0; + gboolean success = FALSE; + gint i; g_return_val_if_fail (self->argv && self->argv[0], FALSE); - - if (self->stdin_disposition == NULL) - self->stdin_disposition = g_object_ref (g_subprocess_stream_devnull ()); - if (self->stdout_disposition == NULL) - self->stdout_disposition = g_object_ref (g_subprocess_stream_inherit ()); - if (self->stderr_disposition == NULL) - self->stderr_disposition = g_object_ref (g_subprocess_stream_inherit ()); - - if (self->spawn_flags & G_SPAWN_CHILD_INHERITS_STDIN) - { - g_clear_object (&self->stdin_disposition); - self->stdin_disposition = g_subprocess_stream_inherit (); - } - if (self->spawn_flags & G_SPAWN_STDOUT_TO_DEV_NULL) - { - g_clear_object (&self->stdout_disposition); - self->stdout_disposition = g_subprocess_stream_devnull (); - } - if (self->spawn_flags & G_SPAWN_STDERR_TO_DEV_NULL) - { - g_clear_object (&self->stderr_disposition); - self->stderr_disposition = g_subprocess_stream_devnull (); - } - - /* We always use this one */ - self->spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD; - + g_return_val_if_fail (!!(self->stdin) + + !!(self->flags & G_SUBPROCESS_FLAGS_STDIN_INHERIT) + + !!(self->flags & G_SUBPROCESS_FLAGS_STDIN_PIPE) < 2, FALSE); + g_return_val_if_fail (!!(self->stdout) + + !!(self->flags & G_SUBPROCESS_FLAGS_STDOUT_SILENCE) + + !!(self->flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE) < 2, FALSE); + g_return_val_if_fail (!!(self->stderr) + + !!(self->flags & G_SUBPROCESS_FLAGS_STDERR_SILENCE) + + !!(self->flags & G_SUBPROCESS_FLAGS_STDERR_PIPE) + + !!(self->flags & G_SUBPROCESS_FLAGS_STDERR_MERGE) < 2, FALSE); #ifdef G_OS_UNIX - child_setup = g_subprocess_internal_child_setup; - child_setup_user_data = self; + g_return_val_if_fail (!self->stdin || G_IS_FILE (self->stdin) || G_IS_FILE_DESCRIPTOR_BASED (self->stdin), FALSE); + g_return_val_if_fail (!self->stdout || G_IS_FILE (self->stdout) || G_IS_FILE_DESCRIPTOR_BASED (self->stdout), FALSE); + g_return_val_if_fail (!self->stderr || G_IS_FILE (self->stderr) || G_IS_FILE_DESCRIPTOR_BASED (self->stderr), FALSE); #else - child_setup = NULL; - child_setup_user_data = NULL; + g_return_val_if_fail (self->stdin == NULL, FALSE); + g_return_val_if_fail (self->stdout == NULL, FALSE); + g_return_val_if_fail (self->stderr == NULL, FALSE); #endif + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + /* We must setup the three fds that will end up in the child as stdin, + * stdout and stderr. + * + * First, stdin. + */ + if (self->flags & G_SUBPROCESS_FLAGS_STDIN_INHERIT) + spawn_flags |= G_SPAWN_CHILD_INHERITS_STDIN; + else if (self->flags & G_SUBPROCESS_FLAGS_STDIN_PIPE) + pipe_ptrs[0] = &pipe_fds[0]; #ifdef G_OS_UNIX - if (G_IS_FILE_DESCRIPTOR_BASED (self->stdin_disposition)) - { - self->internal_stdin_fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*) self->stdin_disposition); - } - else if (G_IS_FILE (self->stdin_disposition)) - { - if (!get_file_path_or_fail ((GFile*)self->stdin_disposition, &temp_path, error)) - goto out; - - self->internal_stdin_fd = g_open (temp_path, O_RDONLY | O_BINARY, 0); - if (self->internal_stdin_fd == -1) - { - set_open_error (temp_path, error); - goto out; - } - close_stdin_fd = TRUE; - - g_clear_pointer (&temp_path, g_free); - } - else + else if (self->stdin) + if (!unix_get_fd_for_object_read (self->stdin, &child_data.fds[0], &close_fds[0], error)) + goto out; #endif - if (self->stdin_disposition == g_subprocess_stream_devnull ()) - { - /* Default */ - } - else if (self->stdin_disposition == g_subprocess_stream_inherit ()) - { - self->spawn_flags |= G_SPAWN_CHILD_INHERITS_STDIN; - } - else if (self->stdin_disposition == g_subprocess_stream_pipe ()) - { - stdin_arg = &stdin_pipe_fd; - } - else - { - set_unsupported_stream_error (self->stdin_disposition, error); + + /* Next, stdout. */ + if (self->flags & G_SUBPROCESS_FLAGS_STDOUT_SILENCE) + spawn_flags |= G_SPAWN_STDOUT_TO_DEV_NULL; + else if (self->flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE) + pipe_ptrs[1] = &pipe_fds[1]; +#ifdef G_OS_UNIX + else if (self->stdout) + if (!unix_get_fd_for_object_write (self->stdout, &child_data.fds[1], &close_fds[1], error)) goto out; - } +#endif - /* Handle stdout */ + /* Finally, stderr. */ + if (self->flags & G_SUBPROCESS_FLAGS_STDERR_SILENCE) + spawn_flags |= G_SPAWN_STDERR_TO_DEV_NULL; + else if (self->flags & G_SUBPROCESS_FLAGS_STDERR_PIPE) + pipe_ptrs[2] = &pipe_fds[2]; + 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->stderr) + if (!unix_get_fd_for_object_write (self->stderr, &child_data.fds[2], &close_fds[2], error)) + goto out; +#endif - if (!unix_handle_open_fd_for_write (self->stdout_disposition, - &disposition_supported, - &self->internal_stdout_fd, - &close_stdout_fd, - error)) - goto out; - else if (!disposition_supported) - { - if (self->stdout_disposition == g_subprocess_stream_devnull ()) - { - self->spawn_flags |= G_SPAWN_STDOUT_TO_DEV_NULL; - } - else if (self->stdout_disposition == g_subprocess_stream_inherit ()) - { - /* Default */ - } - else if (self->stdout_disposition == g_subprocess_stream_pipe ()) - { - stdout_arg = &stdout_pipe_fd; - } - else - { - set_unsupported_stream_error (self->stdout_disposition, error); - goto out; - } - } + spawn_flags |= G_SPAWN_LEAVE_DESCRIPTORS_OPEN; - /* Handle stderr */ + if (self->flags & G_SUBPROCESS_FLAGS_SEARCH_PATH) + spawn_flags |= G_SPAWN_SEARCH_PATH; - if (!unix_handle_open_fd_for_write (self->stderr_disposition, - &disposition_supported, - &self->internal_stderr_fd, - &close_stderr_fd, - error)) - goto out; - else if (!disposition_supported) - { - if (self->stderr_disposition == g_subprocess_stream_devnull ()) - { - self->spawn_flags |= G_SPAWN_STDERR_TO_DEV_NULL; - } - else if (self->stderr_disposition == g_subprocess_stream_inherit ()) - { - /* Default */ - } - else if (self->stderr_disposition == g_subprocess_stream_pipe ()) - { - stderr_arg = &stderr_pipe_fd; - } - else if (self->stderr_disposition == g_subprocess_stream_merge_stdout ()) - { - self->merge_stderr_to_stdout = TRUE; - } - else - { - set_unsupported_stream_error (self->stderr_disposition, error); - goto out; - } - } + spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD; + spawn_flags |= G_SPAWN_CLOEXEC_PIPES; - if (!g_spawn_async_with_pipes (self->cwd, - self->argv, - self->envp, - self->spawn_flags, - child_setup, child_setup_user_data, - &self->pid, - stdin_arg, stdout_arg, stderr_arg, - error)) - goto out; + child_data.flags = self->flags; + success = g_spawn_async_with_pipes (self->cwd, self->argv, self->envp, + spawn_flags, + child_setup, &child_data, + &self->pid, + pipe_ptrs[0], pipe_ptrs[1], pipe_ptrs[2], + error); - ret = TRUE; +out: + for (i = 0; i < 3; i++) + if (close_fds[i] != -1) + close (close_fds[i]); - if (stdin_pipe_fd != -1) - self->stdin_stream = platform_output_stream_from_spawn_fd (stdin_pipe_fd); - if (stdout_pipe_fd != -1) - self->stdout_stream = platform_input_stream_from_spawn_fd (stdout_pipe_fd); - if (stderr_pipe_fd != -1) - self->stderr_stream = platform_input_stream_from_spawn_fd (stderr_pipe_fd); + self->stdin_pipe = platform_output_stream_from_spawn_fd (pipe_fds[0]); + self->stdout_pipe = platform_input_stream_from_spawn_fd (pipe_fds[1]); + self->stderr_pipe = platform_input_stream_from_spawn_fd (pipe_fds[2]); - out: - g_free (temp_path); -#ifdef G_OS_UNIX - if (close_stdin_fd) - (void) close (self->internal_stdin_fd); - if (close_stdout_fd) - (void) close (self->internal_stdout_fd); - if (close_stderr_fd) - (void) close (self->internal_stderr_fd); -#endif - return ret; + return success; } static void @@ -822,30 +646,20 @@ initable_iface_init (GInitableIface *initable_iface) * Since: 2.34 */ GLIB_AVAILABLE_IN_2_34 -GSubprocess * g_subprocess_new (gchar **argv, - const gchar *cwd, - gchar **env, - GSpawnFlags spawn_flags, - GSpawnChildSetupFunc child_setup, - gpointer user_data, - GObject *stdin_disposition, - GObject *stdout_disposition, - GObject *stderr_disposition, - GError **error) +GSubprocess * +g_subprocess_new (const gchar *cwd, + const gchar * const *argv, + const gchar * const *env, + GSubprocessFlags flags, + GError **error) { return g_initable_new (G_TYPE_SUBPROCESS, - NULL, - error, - "argv", argv, - "working-directory", cwd, - "environment", env, - "spawn-flags", spawn_flags, - "child-setup-func", child_setup, - "child-setup-user-data", user_data, - "stdin-disposition", stdin_disposition, - "stdout-disposition", stdout_disposition, - "stderr-disposition", stderr_disposition, - NULL); + NULL, error, + "argv", argv, + "working-directory", cwd, + "environment", env, + "flags", flags, + NULL); } /** @@ -876,80 +690,35 @@ GPid g_subprocess_get_pid (GSubprocess *self) { g_return_val_if_fail (G_IS_SUBPROCESS (self), 0); - - return self->pid; -} - -static GObject * -get_disposition (GSubprocessStreamDispositionKind kind) -{ - static gsize initialized = 0; - static GPtrArray* dispositions = NULL; - - if (g_once_init_enter (&initialized)) - { - int i; - dispositions = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); - for (i = 0; i < G_SUBPROCESS_STREAM_DISPOSITION_LAST+1; i++) - { - GSubprocessStreamDisposition *disposition = g_object_new (g_subprocess_stream_disposition_get_type (), NULL); - disposition->kind = (GSubprocessStreamDispositionKind)i; - g_ptr_array_add (dispositions, disposition); - } - g_once_init_leave (&initialized, TRUE); - } - return dispositions->pdata[(guint)kind]; -} - -GObject * -g_subprocess_stream_devnull (void) -{ - return get_disposition (G_SUBPROCESS_STREAM_DISPOSITION_DEVNULL); -} - -GObject * -g_subprocess_stream_inherit (void) -{ - return get_disposition (G_SUBPROCESS_STREAM_DISPOSITION_INHERIT); -} -GObject * -g_subprocess_stream_pipe (void) -{ - return get_disposition (G_SUBPROCESS_STREAM_DISPOSITION_PIPE); -} - -GObject * -g_subprocess_stream_merge_stdout (void) -{ - return get_disposition (G_SUBPROCESS_STREAM_DISPOSITION_MERGE_STDOUT); + return self->pid; } GOutputStream * g_subprocess_get_stdin_pipe (GSubprocess *self) { g_return_val_if_fail (G_IS_SUBPROCESS (self), NULL); - g_return_val_if_fail (self->stdin_disposition == g_subprocess_stream_pipe (), NULL); + g_return_val_if_fail (self->stdin_pipe, NULL); - return self->stdin_stream; + return self->stdin_pipe; } GInputStream * g_subprocess_get_stdout_pipe (GSubprocess *self) { g_return_val_if_fail (G_IS_SUBPROCESS (self), NULL); - g_return_val_if_fail (self->stdout_disposition == g_subprocess_stream_pipe (), NULL); + g_return_val_if_fail (self->stdout_pipe, NULL); - return self->stdout_stream; + return self->stdout_pipe; } GInputStream * g_subprocess_get_stderr_pipe (GSubprocess *self) { g_return_val_if_fail (G_IS_SUBPROCESS (self), NULL); - g_return_val_if_fail (self->stderr_disposition == g_subprocess_stream_pipe (), NULL); + g_return_val_if_fail (self->stderr_pipe, NULL); - return self->stderr_stream; + return self->stderr_pipe; } typedef struct { @@ -1216,7 +985,7 @@ g_subprocess_wait_sync_check (GSubprocess *self, * Since: 2.34 */ gboolean -g_subprocess_request_exit (GSubprocess *self) +g_subprocess_request_exit (GSubprocess *self) { g_return_val_if_fail (G_IS_SUBPROCESS (self), FALSE); @@ -1240,8 +1009,8 @@ g_subprocess_request_exit (GSubprocess *self) * * On Unix, this function sends %SIGKILL. */ -void -g_subprocess_force_exit (GSubprocess *self) +void +g_subprocess_force_exit (GSubprocess *self) { g_return_if_fail (G_IS_SUBPROCESS (self)); |