[glib/wip/gsubprocess-desrt: 3/3] Ryan's GSubprocess proposal



commit 6c0eb19f024aeda81ff2f35aee82f5f344619665
Author: Ryan Lortie <desrt desrt ca>
Date:   Mon Jul 30 17:58:13 2012 +0200

    Ryan's GSubprocess proposal

 gio/gioenums.h               |   22 ++
 gio/glib-compile-resources.c |   12 +-
 gio/gsubprocess.c            |  839 +++++++++++++++---------------------------
 gio/gsubprocess.h            |   27 +--
 gio/tests/gsubprocess.c      |  284 ++++-----------
 5 files changed, 401 insertions(+), 783 deletions(-)
---
diff --git a/gio/gioenums.h b/gio/gioenums.h
index fa7ae55..88e0c86 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 8225a9d..a6066c5 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 93923ff..0034ea0 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 6a57066..a0a3989 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 135fc9e..7eb7beb 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); */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]