diff options
author | Ryan Lortie <desrt@desrt.ca> | 2012-07-30 17:58:13 +0200 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2012-08-14 18:37:13 -0400 |
commit | 6c0eb19f024aeda81ff2f35aee82f5f344619665 (patch) | |
tree | e9479a1850329e66e9dc900d7033bd86e9aac5d3 | |
parent | 0b38de0a384e50acacd7d5e8d5785af53a16637d (diff) | |
download | glib-wip/gsubprocess-desrt.tar.gz |
Ryan's GSubprocess proposalwip/gsubprocess-desrt
-rw-r--r-- | gio/gioenums.h | 22 | ||||
-rw-r--r-- | gio/glib-compile-resources.c | 12 | ||||
-rw-r--r-- | gio/gsubprocess.c | 839 | ||||
-rw-r--r-- | gio/gsubprocess.h | 27 | ||||
-rw-r--r-- | gio/tests/gsubprocess.c | 284 |
5 files changed, 401 insertions, 783 deletions
diff --git a/gio/gioenums.h b/gio/gioenums.h index fa7ae557d..88e0c8658 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -1653,6 +1653,28 @@ typedef enum /*< flags >*/ { G_TEST_DBUS_NONE = 0 } GTestDBusFlags; +/** + * GSubprocessFlags: + * @G_SUBPROCESS_FLAGS_NONE: No flags. + * + * Flags to define the behaviour of a #GSubprocess. + * + * Since: 2.34 + **/ +typedef enum { + G_SUBPROCESS_FLAGS_NONE = 0, + G_SUBPROCESS_FLAGS_SEARCH_PATH = (1u << 0), + G_SUBPROCESS_FLAGS_TERM_WITH_PARENT = (1u << 1), + G_SUBPROCESS_FLAGS_NEW_SESSION = (1u << 2), + G_SUBPROCESS_FLAGS_STDIN_PIPE = (1u << 3), + G_SUBPROCESS_FLAGS_STDIN_INHERIT = (1u << 4), + G_SUBPROCESS_FLAGS_STDOUT_PIPE = (1u << 5), + G_SUBPROCESS_FLAGS_STDOUT_SILENCE = (1u << 6), + G_SUBPROCESS_FLAGS_STDERR_PIPE = (1u << 7), + G_SUBPROCESS_FLAGS_STDERR_SILENCE = (1u << 8), + G_SUBPROCESS_FLAGS_STDERR_MERGE = (1u << 9) +} GSubprocessFlags; + G_END_DECLS #endif /* __GIO_ENUMS_H__ */ diff --git a/gio/glib-compile-resources.c b/gio/glib-compile-resources.c index 8225a9db5..a6066c5f2 100644 --- a/gio/glib-compile-resources.c +++ b/gio/glib-compile-resources.c @@ -315,11 +315,7 @@ end_element (GMarkupParseContext *context, g_ptr_array_addv (args, xmllint, "--nonet", "--noblanks", "--output", tmp_file, real_file, NULL); g_ptr_array_add (args, NULL); - proc = g_subprocess_new ((gchar**)args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_devnull (), - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const gchar**)args->pdata, NULL, 0, error); g_ptr_array_free (args, TRUE); g_free (real_file); real_file = NULL; @@ -369,11 +365,7 @@ end_element (GMarkupParseContext *context, args = g_ptr_array_new (); g_ptr_array_addv (args, gdk_pixbuf_pixdata, real_file, tmp_file2, NULL); g_ptr_array_add (args, NULL); - proc = g_subprocess_new ((gchar**)args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_devnull (), - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const gchar**)args->pdata, NULL, 0, error); g_ptr_array_free (args, TRUE); g_free (real_file); 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)); diff --git a/gio/gsubprocess.h b/gio/gsubprocess.h index 6a5706618..a0a398998 100644 --- a/gio/gsubprocess.h +++ b/gio/gsubprocess.h @@ -41,28 +41,11 @@ GType g_subprocess_get_type (void) G_GNUC_CONST; /**** Creation ****/ 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); - -GLIB_AVAILABLE_IN_2_34 -GObject * g_subprocess_stream_devnull (void); - -GLIB_AVAILABLE_IN_2_34 -GObject * g_subprocess_stream_inherit (void); - -GLIB_AVAILABLE_IN_2_34 -GObject * g_subprocess_stream_pipe (void); - -GLIB_AVAILABLE_IN_2_34 -GObject * g_subprocess_stream_merge_stdout (void); +GSubprocess * g_subprocess_new (const gchar *cwd, + const gchar * const *argv, + const gchar * const *env, + GSubprocessFlags flags, + GError **error); GLIB_AVAILABLE_IN_2_34 GOutputStream * g_subprocess_get_stdin_pipe (GSubprocess *self); diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c index 135fc9e53..7eb7beb85 100644 --- a/gio/tests/gsubprocess.c +++ b/gio/tests/gsubprocess.c @@ -53,11 +53,7 @@ test_noop (void) GSubprocess *proc; args = get_test_subprocess_args ("noop", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_devnull (), - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const char**) args->pdata, NULL, 0, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -76,11 +72,7 @@ test_noop_all_to_null (void) GSubprocess *proc; args = get_test_subprocess_args ("noop", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_devnull (), - g_subprocess_stream_devnull (), - g_subprocess_stream_devnull (), - error); + proc = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, 0, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -99,11 +91,7 @@ test_noop_no_wait (void) GSubprocess *proc; args = get_test_subprocess_args ("noop", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_devnull (), - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, 0, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -119,11 +107,7 @@ test_noop_stdin_inherit (void) GSubprocess *proc; args = get_test_subprocess_args ("noop", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, 0, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -146,11 +130,7 @@ test_search_path (void) args = g_ptr_array_new (); g_ptr_array_add (args, "true"); g_ptr_array_add (args, NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const char **) args->pdata, NULL, G_SUBPROCESS_FLAGS_SEARCH_PATH, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -170,11 +150,7 @@ test_exit1 (void) GSubprocess *proc; args = get_test_subprocess_args ("exit1", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_devnull (), - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const char **) args->pdata, NULL, G_SUBPROCESS_FLAGS_NONE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -219,11 +195,7 @@ test_echo1 (void) gchar *result; args = get_test_subprocess_args ("echo", "hello", "world!", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_devnull (), - g_subprocess_stream_pipe (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, G_SUBPROCESS_FLAGS_STDOUT_PIPE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -250,11 +222,8 @@ test_echo_merged (void) gchar *result; args = get_test_subprocess_args ("echo-stdout-and-stderr", "merge", "this", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_devnull (), - g_subprocess_stream_pipe (), - g_subprocess_stream_merge_stdout (), - error); + proc = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, + G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_MERGE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -309,11 +278,9 @@ test_cat_utf8 (void) data.loop = g_main_loop_new (NULL, TRUE); args = get_test_subprocess_args ("cat", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_pipe (), - g_subprocess_stream_pipe (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, + G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, + error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -352,165 +319,63 @@ test_cat_utf8 (void) g_object_unref (proc); } -#ifdef G_OS_UNIX -static void -test_file_redirection (void) +static gpointer +cancel_soon (gpointer user_data) { - GError *local_error = NULL; - GError **error = &local_error; - char *expected_buf = NULL; - GSubprocess *proc; - GPtrArray *args; - GBytes *input_buf; - GBytes *output_buf; - GInputStream *input_buf_stream = NULL; - GOutputStream *output_buf_stream = NULL; - GOutputStream *stdin_stream = NULL; - GInputStream *stdout_stream = NULL; - GFile *temp_file = NULL; - GFileIOStream *temp_file_io = NULL; - TestCatData data; + GCancellable *cancellable = user_data; - memset (&data, 0, sizeof (data)); - data.loop = g_main_loop_new (NULL, TRUE); - - expected_buf = g_strdup_printf ("this is a test file, written by pid:%lu at monotonic time:%" G_GUINT64_FORMAT, - (gulong) getpid (), g_get_monotonic_time ()); - - temp_file = g_file_new_tmp ("gsubprocess-tmpXXXXXX", - &temp_file_io, error); - g_assert_no_error (local_error); - - g_io_stream_close ((GIOStream*)temp_file_io, NULL, error); - g_assert_no_error (local_error); - - args = get_test_subprocess_args ("cat", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_pipe (), - (GObject*) temp_file, - g_subprocess_stream_inherit (), - error); - g_ptr_array_free (args, TRUE); - g_assert_no_error (local_error); + g_usleep (G_TIME_SPAN_SECOND); + g_cancellable_cancel (cancellable); + g_object_unref (cancellable); - stdin_stream = g_subprocess_get_stdin_pipe (proc); - - input_buf = g_bytes_new_take (expected_buf, strlen (expected_buf)); - expected_buf = NULL; - input_buf_stream = g_memory_input_stream_new_from_bytes (input_buf); - - g_output_stream_splice_async (stdin_stream, input_buf_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, - G_PRIORITY_DEFAULT, NULL, test_cat_on_input_splice_complete, - &data); - data.events_pending++; - - g_main_loop_run (data.loop); - - g_subprocess_wait_sync_check (proc, NULL, error); - g_assert_no_error (local_error); - - g_object_unref (proc); - g_object_unref (input_buf_stream); - - args = get_test_subprocess_args ("cat", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - (GObject*)temp_file, - g_subprocess_stream_pipe (), - g_subprocess_stream_inherit (), - error); - g_ptr_array_free (args, TRUE); - g_assert_no_error (local_error); - - stdout_stream = g_subprocess_get_stdout_pipe (proc); - output_buf_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); - - g_output_stream_splice_async (output_buf_stream, stdout_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, - G_PRIORITY_DEFAULT, NULL, test_cat_on_input_splice_complete, - &data); - data.events_pending++; - - g_main_loop_run (data.loop); - - output_buf = g_memory_output_stream_steal_as_bytes ((GMemoryOutputStream*)output_buf_stream); - - g_assert_cmpint (g_bytes_get_size (output_buf), ==, g_bytes_get_size (input_buf)); - g_assert_cmpint (memcmp (g_bytes_get_data (output_buf, NULL), - g_bytes_get_data (input_buf, NULL), g_bytes_get_size (input_buf)), ==, 0); - - g_bytes_unref (input_buf); - g_bytes_unref (output_buf); - g_main_loop_unref (data.loop); - g_object_unref (output_buf_stream); - g_object_unref (proc); - - g_file_delete (temp_file, NULL, error); - g_assert_no_error (local_error); - g_object_unref (temp_file); - g_object_unref (temp_file_io); + return NULL; } static void -test_unix_fd_passing (void) +test_cat_eof (void) { - GError *local_error = NULL; - GError **error = &local_error; - GSubprocess *proc; - GPtrArray *args; - GOutputStream *stdin; - GFile *temp_file = NULL; - GFileIOStream *temp_file_io = NULL; - GOutputStream *temp_file_out = NULL; - gsize bytes_written; - gchar *write_buf; - gchar *read_buf; - - temp_file = g_file_new_tmp ("gsubprocess-tmpXXXXXX", - &temp_file_io, error); - - temp_file_out = g_io_stream_get_output_stream ((GIOStream*)temp_file_io); - g_assert (G_IS_FILE_DESCRIPTOR_BASED (temp_file_out)); - - args = get_test_subprocess_args ("cat", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_pipe (), - (GObject*)temp_file_out, - g_subprocess_stream_inherit (), - error); - g_ptr_array_free (args, TRUE); - g_assert_no_error (local_error); - - g_io_stream_close ((GIOStream*)temp_file_io, NULL, error); - g_assert_no_error (local_error); - - stdin = g_subprocess_get_stdin_pipe (proc); - - write_buf = g_strdup_printf ("fd-passing timestamp:%" G_GUINT64_FORMAT, - g_get_monotonic_time ()); - g_output_stream_write_all (stdin, write_buf, strlen (write_buf), &bytes_written, - NULL, error); - g_assert_no_error (local_error); - - g_output_stream_close (stdin, NULL, error); - g_assert_no_error (local_error); - - g_subprocess_wait_sync_check (proc, NULL, error); - g_assert_no_error (local_error); + const gchar *args[] = { "cat", NULL }; + GCancellable *cancellable; + GError *error = NULL; + GSubprocess *cat; + gint exit_status; + gboolean result; + gchar buffer; + gssize s; + + /* Spawn 'cat' */ + cat = g_subprocess_new (NULL, args, NULL, G_SUBPROCESS_FLAGS_SEARCH_PATH | + G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error); + g_assert_no_error (error); + g_assert (cat); + + /* Make sure that reading stdout blocks (until we cancel) */ + cancellable = g_cancellable_new (); + g_thread_unref (g_thread_new ("cancel thread", cancel_soon, g_object_ref (cancellable))); + s = g_input_stream_read (g_subprocess_get_stdout_pipe (cat), &buffer, sizeof buffer, cancellable, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert_cmpint (s, ==, -1); + g_object_unref (cancellable); + g_clear_error (&error); - g_file_load_contents (temp_file, NULL, &read_buf, &bytes_written, NULL, error); - g_assert_no_error (local_error); + /* Close the stream (EOF on cat's stdin) */ + result = g_output_stream_close (g_subprocess_get_stdin_pipe (cat), NULL, &error); + g_assert_no_error (error); + g_assert (result); - g_assert_cmpstr (read_buf, ==, write_buf); + /* Now check that reading cat's stdout gets us an EOF (since it quit) */ + s = g_input_stream_read (g_subprocess_get_stdout_pipe (cat), &buffer, sizeof buffer, NULL, &error); + g_assert_no_error (error); + g_assert (!s); - g_free (read_buf); - g_free (write_buf); - g_object_unref (proc); + /* Check that the process has exited as a result of the EOF */ + result = g_subprocess_wait_sync (cat, &exit_status, NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (exit_status, ==, 0); + g_assert (result); - g_file_delete (temp_file, NULL, error); - g_assert_no_error (local_error); - g_object_unref (temp_file); - g_object_unref (temp_file_io); + g_object_unref (cat); } -#endif typedef struct { guint events_pending; @@ -622,23 +487,17 @@ test_multi_1 (void) int splice_flags = G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET; args = get_test_subprocess_args ("cat", NULL); - first = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_pipe (), - g_subprocess_stream_pipe (), - g_subprocess_stream_inherit (), - error); + first = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, + G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, + error); g_assert_no_error (local_error); - second = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_pipe (), - g_subprocess_stream_pipe (), - g_subprocess_stream_inherit (), - error); + second = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, + G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, + error); g_assert_no_error (local_error); - third = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_pipe (), - g_subprocess_stream_pipe (), - g_subprocess_stream_inherit (), - error); + third = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, + G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, + error); g_assert_no_error (local_error); g_ptr_array_free (args, TRUE); @@ -754,11 +613,7 @@ test_terminate (void) GMainLoop *loop; args = get_test_subprocess_args ("sleep-forever", NULL); - proc = g_subprocess_new ((char**) args->pdata, NULL, NULL, 0, NULL, NULL, - g_subprocess_stream_devnull (), - g_subprocess_stream_inherit (), - g_subprocess_stream_inherit (), - error); + proc = g_subprocess_new (NULL, (const gchar **) args->pdata, NULL, G_SUBPROCESS_FLAGS_NONE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); @@ -794,10 +649,7 @@ main (int argc, char **argv) g_test_add_func ("/gsubprocess/echo-merged", test_echo_merged); #endif g_test_add_func ("/gsubprocess/cat-utf8", test_cat_utf8); -#ifdef G_OS_UNIX - g_test_add_func ("/gsubprocess/file-redirection", test_file_redirection); - g_test_add_func ("/gsubprocess/unix-fd-passing", test_unix_fd_passing); -#endif + g_test_add_func ("/gsubprocess/cat-eof", test_cat_eof); g_test_add_func ("/gsubprocess/multi1", test_multi_1); g_test_add_func ("/gsubprocess/terminate", test_terminate); /* g_test_add_func ("/gsubprocess/argv0", test_argv0); */ |