summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2012-07-30 17:58:13 +0200
committerRyan Lortie <desrt@desrt.ca>2012-08-14 18:37:13 -0400
commit6c0eb19f024aeda81ff2f35aee82f5f344619665 (patch)
treee9479a1850329e66e9dc900d7033bd86e9aac5d3
parent0b38de0a384e50acacd7d5e8d5785af53a16637d (diff)
downloadglib-wip/gsubprocess-desrt.tar.gz
Ryan's GSubprocess proposalwip/gsubprocess-desrt
-rw-r--r--gio/gioenums.h22
-rw-r--r--gio/glib-compile-resources.c12
-rw-r--r--gio/gsubprocess.c839
-rw-r--r--gio/gsubprocess.h27
-rw-r--r--gio/tests/gsubprocess.c284
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); */