diff options
author | Colin Walters <walters@verbum.org> | 2014-02-05 06:58:51 -0500 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2014-02-06 04:12:18 -0500 |
commit | 9363cfc28ede912e2f06d4ccb42a646bb8a4bd2e (patch) | |
tree | 94d03e2391e4cdee87a8345013e3ac5b6e21cef4 /src/gsystem-subprocess-context.c | |
parent | 856b8f9431a63c7807fb3859ed6de2a0f3abfb3b (diff) | |
download | libgsystem-9363cfc28ede912e2f06d4ccb42a646bb8a4bd2e.tar.gz |
Rework to be an installed libraryv2014.1
See https://mail.gnome.org/archives/desktop-devel-list/2014-February/msg00028.html
Diffstat (limited to 'src/gsystem-subprocess-context.c')
-rw-r--r-- | src/gsystem-subprocess-context.c | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/src/gsystem-subprocess-context.c b/src/gsystem-subprocess-context.c new file mode 100644 index 0000000..90ca716 --- /dev/null +++ b/src/gsystem-subprocess-context.c @@ -0,0 +1,501 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012 Colin Walters <walters@verbum.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "libgsystem.h" + +#if GLIB_CHECK_VERSION(2,34,0) + +#ifdef G_OS_UNIX +#include <gio/gunixoutputstream.h> +#include <gio/gfiledescriptorbased.h> +#include <gio/gunixinputstream.h> +#include <glib-unix.h> +#endif + +/** + * SECTION:gssubprocesscontext + * @title: GSSubprocess Context + * @short_description: Environment options for launching a child process + * + * This class contains a set of options for launching child processes, + * such as where its standard input and output will be directed, the + * argument list, the environment, and more. + * + * While the #GSSubprocess class has high level functions covering + * popular cases, use of this class allows access to more advanced + * options. It can also be used to launch multiple subprocesses with + * a similar configuration. + * + * Since: 2.36 + */ + +#include "config.h" + +#include "gsystem-subprocess-context-private.h" +#include "gsystem-subprocess.h" + +#include <string.h> + +typedef GObjectClass GSSubprocessContextClass; + +G_DEFINE_TYPE (GSSubprocessContext, gs_subprocess_context, G_TYPE_OBJECT); + +enum +{ + PROP_0, + PROP_ARGV, + N_PROPS +}; + +static GParamSpec *gs_subprocess_context_pspecs[N_PROPS]; + +/** + * gs_subprocess_context_new: + * @argv: Argument list + * + * Returns: (transfer full): A new instance of a #GSSubprocessContext. + */ +GSSubprocessContext * +gs_subprocess_context_new (gchar **argv) +{ + g_return_val_if_fail (argv != NULL && argv[0] != NULL, NULL); + + return g_object_new (GS_TYPE_SUBPROCESS_CONTEXT, + "argv", argv, + NULL); +} + +GSSubprocessContext * +gs_subprocess_context_newv (const gchar *first_arg, + ...) +{ + GSSubprocessContext *result; + va_list args; + + g_return_val_if_fail (first_arg != NULL, NULL); + + va_start (args, first_arg); + result = gs_subprocess_context_newa (first_arg, args); + va_end (args); + + return result; +} + +/** + * gs_subprocess_context_newa: + * @first_arg: First argument + * @args: a va_list + * + * Returns: (transfer full): A new instance of a #GSSubprocessContext. + */ +GSSubprocessContext * +gs_subprocess_context_newa (const gchar *first_arg, + va_list args) +{ + GSSubprocessContext *result; + GPtrArray *argv; + + g_return_val_if_fail (first_arg != NULL, NULL); + + argv = g_ptr_array_new (); + do + g_ptr_array_add (argv, (gchar*)first_arg); + while ((first_arg = va_arg (args, const gchar *)) != NULL); + g_ptr_array_add (argv, NULL); + + result = gs_subprocess_context_new ((gchar**)argv->pdata); + + return result; +} + +#ifdef G_OS_UNIX +GSSubprocessContext * +gs_subprocess_context_new_argv0 (const gchar *argv0, + gchar **argv) +{ + GSSubprocessContext *result; + GPtrArray *real_argv; + gchar **iter; + + g_return_val_if_fail (argv0 != NULL, NULL); + g_return_val_if_fail (argv != NULL && argv[0] != NULL, NULL); + + real_argv = g_ptr_array_new (); + g_ptr_array_add (real_argv, (gchar*)argv0); + for (iter = argv; *iter; iter++) + g_ptr_array_add (real_argv, (gchar*) *iter); + g_ptr_array_add (real_argv, NULL); + + result = g_object_new (GS_TYPE_SUBPROCESS_CONTEXT, + "argv", real_argv->pdata, + NULL); + result->has_argv0 = TRUE; + + return result; +} +#endif + +static void +gs_subprocess_context_init (GSSubprocessContext *self) +{ + self->stdin_fd = -1; + self->stdout_fd = -1; + self->stderr_fd = -1; + self->stdout_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT; + self->stderr_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT; + self->postfork_close_fds = g_array_new (FALSE, FALSE, sizeof (int)); + self->inherit_fds = g_array_new (FALSE, FALSE, sizeof (int)); +} + +static void +gs_subprocess_context_finalize (GObject *object) +{ + GSSubprocessContext *self = GS_SUBPROCESS_CONTEXT (object); + + g_strfreev (self->argv); + g_strfreev (self->envp); + g_free (self->cwd); + + g_free (self->stdin_path); + g_free (self->stdout_path); + g_free (self->stderr_path); + + g_array_unref (self->postfork_close_fds); + g_array_unref (self->inherit_fds); + + if (G_OBJECT_CLASS (gs_subprocess_context_parent_class)->finalize != NULL) + G_OBJECT_CLASS (gs_subprocess_context_parent_class)->finalize (object); +} + +static void +gs_subprocess_context_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSSubprocessContext *self = GS_SUBPROCESS_CONTEXT (object); + + switch (prop_id) + { + case PROP_ARGV: + self->argv = (gchar**) g_value_dup_boxed (value); + break; + + default: + g_assert_not_reached (); + } +} + +static void +gs_subprocess_context_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSSubprocessContext *self = GS_SUBPROCESS_CONTEXT (object); + + switch (prop_id) + { + case PROP_ARGV: + g_value_set_boxed (value, self->argv); + break; + + default: + g_assert_not_reached (); + } +} + +static void +gs_subprocess_context_class_init (GSSubprocessContextClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + gobject_class->finalize = gs_subprocess_context_finalize; + gobject_class->get_property = gs_subprocess_context_get_property; + gobject_class->set_property = gs_subprocess_context_set_property; + + /** + * GSSubprocessContext:argv: + * + * Array of arguments passed to child process; must have at least + * one element. The first element has special handling - if it is + * an not absolute path ( as determined by g_path_is_absolute() ), + * then the system search path will be used. See + * %G_SPAWN_SEARCH_PATH. + * + * Note that in order to use the Unix-specific argv0 functionality, + * you must use the setter function + * gs_subprocess_context_set_args_and_argv0(). For more information + * about this, see %G_SPAWN_FILE_AND_ARGV_ZERO. + * + * Since: 2.36 + */ + gs_subprocess_context_pspecs[PROP_ARGV] = g_param_spec_boxed ("argv", "Arguments", "Arguments for child process", G_TYPE_STRV, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, N_PROPS, gs_subprocess_context_pspecs); +} + +/** + * gs_subprocess_context_argv_append: + * @self: + * @arg: An argument + * + * Append an argument to the child's argument vector. + */ +void +gs_subprocess_context_argv_append (GSSubprocessContext *self, + const gchar *arg) +{ + GPtrArray *new_argv = g_ptr_array_new (); + gchar **iter; + + for (iter = self->argv; *iter; iter++) + g_ptr_array_add (new_argv, *iter); + g_ptr_array_add (new_argv, g_strdup (arg)); + g_ptr_array_add (new_argv, NULL); + + /* Don't free elements */ + g_free (self->argv); + self->argv = (char**)g_ptr_array_free (new_argv, FALSE); +} + +/* Environment */ + +/** + * gs_subprocess_context_set_environment: + * @self: + * @environ: (array zero-terminated=1) (element-type utf8): Environment KEY=VALUE pairs + * + * Replace the environment that will be used for the child process. + * The default is to inherit the current process. + */ +void +gs_subprocess_context_set_environment (GSSubprocessContext *self, + gchar **env) +{ + g_strfreev (self->envp); + self->envp = g_strdupv (env); +} + +void +gs_subprocess_context_set_cwd (GSSubprocessContext *self, + const gchar *cwd) +{ + g_free (self->cwd); + self->cwd = g_strdup (cwd); +} + +void +gs_subprocess_context_set_keep_descriptors (GSSubprocessContext *self, + gboolean keep_descriptors) + +{ + self->keep_descriptors = keep_descriptors ? 1 : 0; +} + +void +gs_subprocess_context_set_search_path (GSSubprocessContext *self, + gboolean search_path, + gboolean search_path_from_envp) +{ + self->search_path = search_path ? 1 : 0; + self->search_path_from_envp = search_path_from_envp ? 1 : 0; +} + +void +gs_subprocess_context_set_stdin_disposition (GSSubprocessContext *self, + GSSubprocessStreamDisposition disposition) +{ + g_return_if_fail (disposition != GS_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE); + self->stdin_disposition = disposition; +} + +void +gs_subprocess_context_set_stdout_disposition (GSSubprocessContext *self, + GSSubprocessStreamDisposition disposition) +{ + g_return_if_fail (disposition != GS_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE); + self->stdout_disposition = disposition; +} + +void +gs_subprocess_context_set_stderr_disposition (GSSubprocessContext *self, + GSSubprocessStreamDisposition disposition) +{ + self->stderr_disposition = disposition; +} + +#ifdef G_OS_UNIX +void +gs_subprocess_context_set_stdin_file_path (GSSubprocessContext *self, + const gchar *path) +{ + self->stdin_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL; + g_free (self->stdin_path); + self->stdin_path = g_strdup (path); +} + +void +gs_subprocess_context_set_stdin_fd (GSSubprocessContext *self, + gint fd) +{ + self->stdin_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL; + self->stdin_fd = fd; +} + +void +gs_subprocess_context_set_stdout_file_path (GSSubprocessContext *self, + const gchar *path) +{ + self->stdout_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL; + g_free (self->stdout_path); + self->stdout_path = g_strdup (path); +} + +void +gs_subprocess_context_set_stdout_fd (GSSubprocessContext *self, + gint fd) +{ + self->stdout_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL; + self->stdout_fd = fd; +} + +void +gs_subprocess_context_set_stderr_file_path (GSSubprocessContext *self, + const gchar *path) +{ + self->stderr_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL; + g_free (self->stderr_path); + self->stderr_path = g_strdup (path); +} + +void +gs_subprocess_context_set_stderr_fd (GSSubprocessContext *self, + gint fd) +{ + self->stderr_disposition = GS_SUBPROCESS_STREAM_DISPOSITION_NULL; + self->stderr_fd = fd; +} +#endif + +#ifdef G_OS_UNIX +/** + * gs_subprocess_context_set_child_setup: (skip) + * @self: + * @child_setup: Function to call in the newly forked child, before execve() + * @user_data: Data passed to child + * + * FIXME - note extensive restricitons on GSpawnChildSetupFunc here + */ +void +gs_subprocess_context_set_child_setup (GSSubprocessContext *self, + GSpawnChildSetupFunc child_setup, + gpointer user_data) +{ + self->child_setup_func = child_setup; + self->child_setup_data = user_data; +} + +static gboolean +open_pipe_internal (GSSubprocessContext *self, + gboolean for_read, + void **out_stream, + gint *out_fdno, + GError **error) +{ + int pipefds[2]; + + g_return_val_if_fail (out_stream != NULL, FALSE); + g_return_val_if_fail (out_fdno != NULL, FALSE); + + if (!g_unix_open_pipe (pipefds, FD_CLOEXEC, error)) + return FALSE; + + if (for_read) + { + *out_stream = g_unix_input_stream_new (pipefds[0], TRUE); + *out_fdno = pipefds[1]; + } + else + { + *out_stream = g_unix_output_stream_new (pipefds[1], TRUE); + *out_fdno = pipefds[0]; + } + g_array_append_val (self->inherit_fds, *out_fdno); + g_array_append_val (self->postfork_close_fds, *out_fdno); + + return TRUE; +} + +/** + * gs_subprocess_context_open_pipe_read: + * @self: + * @out_stream: (out) (transfer full): A newly referenced output stream + * @out_fdno: (out): File descriptor number for the subprocess side of the pipe + * + * This allows you to open a pipe between the parent and child + * processes, independent of the standard streams. For this function, + * the pipe is set up so that the parent can read, and the child can + * write. For the opposite version, see + * gs_subprocess_context_open_pipe_write(). + * + * The returned @out_fdno is the file descriptor number that the child + * will see; you need to communicate this number via a separate + * channel, such as the argument list. For example, if you're using + * this pipe to send a password, provide + * <literal>--password-fd=<fdno string></literal>. + * + * Returns: %TRUE on success, %FALSE on error (and @error will be set) + */ +gboolean +gs_subprocess_context_open_pipe_read (GSSubprocessContext *self, + GInputStream **out_stream, + gint *out_fdno, + GError **error) +{ + return open_pipe_internal (self, TRUE, (void**)out_stream, out_fdno, error); +} + +/** + * gs_subprocess_context_open_pipe_write: + * @self: + * @out_stream: (out) (transfer full): A newly referenced stream + * @out_fdno: (out): File descriptor number for the subprocess side of the pipe + * + * Like gs_subprocess_context_open_pipe_read(), but returns a writable + * channel from which the child process can read. + * + * Returns: %TRUE on success, %FALSE on error (and @error will be set) + */ +gboolean +gs_subprocess_context_open_pipe_write (GSSubprocessContext *self, + GOutputStream **out_stream, + gint *out_fdno, + GError **error) +{ + return open_pipe_internal (self, FALSE, (void**)out_stream, out_fdno, error); +} + +#endif + +#endif |