diff options
| author | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
| commit | 2c032d8f1c6292c1338a615e6ec40252889ba85c (patch) | |
| tree | 1f77182220b2b0264288ba4a476ab47e5bc48716 /libgsystem/gsystem-subprocess.c | |
| parent | 33491bc4279481db8ae47213e34a6d695a0e8830 (diff) | |
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'libgsystem/gsystem-subprocess.c')
| -rw-r--r-- | libgsystem/gsystem-subprocess.c | 966 |
1 files changed, 0 insertions, 966 deletions
diff --git a/libgsystem/gsystem-subprocess.c b/libgsystem/gsystem-subprocess.c deleted file mode 100644 index 4ec680f8..00000000 --- a/libgsystem/gsystem-subprocess.c +++ /dev/null @@ -1,966 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* 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 - * by the Free Software Foundation; either version 2 of the licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Authors: Colin Walters <walters@verbum.org> - * Ryan Lortie <desrt@desrt.ca> - */ - -#include "config.h" - -#define _GSYSTEM_NO_LOCAL_ALLOC -#include "libgsystem.h" - -#if GLIB_CHECK_VERSION(2,34,0) - -/** - * SECTION:gssubprocess - * @title: GSSubprocess - * @short_description: Create child processes and monitor their status - * - * This class wraps the lower-level g_spawn_async_with_pipes() API, - * providing a more modern GIO-style API, such as returning - * #GInputStream objects for child output pipes. - * - * One major advantage that GIO brings over the core GLib library is - * comprehensive API for asynchronous I/O, such - * g_output_stream_splice_async(). This makes GSubprocess - * significantly more powerful and flexible than equivalent APIs in - * some other languages such as the <literal>subprocess.py</literal> - * included with Python. For example, using #GSubprocess one could - * create two child processes, reading standard output from the first, - * processing it, and writing to the input stream of the second, all - * without blocking the main loop. - * - * Since: 2.36 - */ - -#include "config.h" - -#include "gsystem-subprocess.h" -#include "gsystem-subprocess-context-private.h" - -#include <string.h> -#ifdef G_OS_UNIX -#include <gio/gunixoutputstream.h> -#include <gio/gfiledescriptorbased.h> -#include <gio/gunixinputstream.h> -#include <glib-unix.h> -#endif -#include <fcntl.h> -#ifdef G_OS_WIN32 -#define _WIN32_WINNT 0x0500 -#include <windows.h> -#include "giowin32-priv.h" -#endif - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -static void initable_iface_init (GInitableIface *initable_iface); - -typedef GObjectClass GSSubprocessClass; - -#ifdef G_OS_UNIX -static void -gs_subprocess_unix_queue_waitpid (GSSubprocess *self); -#endif - -struct _GSSubprocess -{ - GObject parent; - - GSSubprocessContext *context; - GPid pid; - - guint pid_valid : 1; - guint reaped_child : 1; - guint unused : 30; - - /* 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 (GSSubprocess, gs_subprocess, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)); - -enum -{ - PROP_0, - PROP_CONTEXT, - N_PROPS -}; - -static GParamSpec *gs_subprocess_pspecs[N_PROPS]; - -static void -gs_subprocess_init (GSSubprocess *self) -{ -} - -static void -gs_subprocess_finalize (GObject *object) -{ - GSSubprocess *self = GS_SUBPROCESS (object); - - if (self->pid_valid) - { -#ifdef G_OS_UNIX - /* Here we need to actually call waitpid() to clean up the - * zombie. In case the child hasn't actually exited, defer this - * cleanup to the worker thread. - */ - if (!self->reaped_child) - gs_subprocess_unix_queue_waitpid (self); -#endif - g_spawn_close_pid (self->pid); - } - - g_clear_object (&self->stdin_pipe); - g_clear_object (&self->stdout_pipe); - g_clear_object (&self->stderr_pipe); - - if (G_OBJECT_CLASS (gs_subprocess_parent_class)->finalize != NULL) - G_OBJECT_CLASS (gs_subprocess_parent_class)->finalize (object); -} - -static void -gs_subprocess_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GSSubprocess *self = GS_SUBPROCESS (object); - - switch (prop_id) - { - case PROP_CONTEXT: - self->context = g_value_dup_object (value); - break; - - default: - g_assert_not_reached (); - } -} - -static void -gs_subprocess_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GSSubprocess *self = GS_SUBPROCESS (object); - - switch (prop_id) - { - case PROP_CONTEXT: - g_value_set_object (value, self->context); - break; - - default: - g_assert_not_reached (); - } -} - -static void -gs_subprocess_class_init (GSSubprocessClass *class) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (class); - - gobject_class->finalize = gs_subprocess_finalize; - gobject_class->get_property = gs_subprocess_get_property; - gobject_class->set_property = gs_subprocess_set_property; - - /** - * GSSubprocess:context: - * - * - * Since: 2.36 - */ - gs_subprocess_pspecs[PROP_CONTEXT] = g_param_spec_object ("context", "Context", "Subprocess options", GS_TYPE_SUBPROCESS_CONTEXT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (gobject_class, N_PROPS, gs_subprocess_pspecs); -} - -#ifdef G_OS_UNIX - -static gboolean -gs_subprocess_unix_waitpid_dummy (gpointer data) -{ - return FALSE; -} - -static void -gs_subprocess_unix_queue_waitpid (GSSubprocess *self) -{ - GMainContext *worker_context; - GSource *waitpid_source; - -#ifdef GLIB_COMPILATION - worker_context = GLIB_PRIVATE_CALL (g_get_worker_context) (); -#else - worker_context = g_main_context_get_thread_default (); -#endif - waitpid_source = g_child_watch_source_new (self->pid); - g_source_set_callback (waitpid_source, gs_subprocess_unix_waitpid_dummy, NULL, NULL); - g_source_attach (waitpid_source, worker_context); - g_source_unref (waitpid_source); -} - -#endif - -static GInputStream * -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 -} - -static GOutputStream * -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 -} - -#ifdef G_OS_UNIX -static gint -unix_open_file (const char *filename, - gint mode, - GError **error) -{ - gint my_fd; - - do - my_fd = open (filename, mode | O_BINARY | O_CLOEXEC, 0666); - while (my_fd == -1 && errno == EINTR); - - /* 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... */ - } - - return my_fd; -} -#endif - -typedef struct -{ - gint fds[3]; - GArray *inherit_fds; - GSpawnChildSetupFunc child_setup_func; - gpointer child_setup_data; -} ChildData; - -static void -child_setup (gpointer user_data) -{ - ChildData *child_data = user_data; - guint i; - gint result; - - /* 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] != (int) i) - { - do - result = dup2 (child_data->fds[i], i); - while (G_UNLIKELY (result == -1 && errno == EINTR)); - } - } - - /* Unset the CLOEXEC flag for the child *should* inherit */ - for (i = 0; i < child_data->inherit_fds->len; i++) - { - int fd = g_array_index (child_data->inherit_fds, int, i); - int flags; - - do - flags = fcntl (fd, F_GETFL); - while (G_UNLIKELY (flags == -1 && errno == EINTR)); - - flags &= ~FD_CLOEXEC; - - do - result = fcntl (fd, F_SETFD, flags); - while (G_UNLIKELY (result == -1 && errno == EINTR)); - } - - if (child_data->child_setup_func) - child_data->child_setup_func (child_data->child_setup_data); -} - -static gboolean -initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) -{ - GSSubprocess *self = GS_SUBPROCESS (initable); - 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; - guint i; - - 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. - */ -#ifdef G_OS_UNIX - if (self->context->stdin_fd != -1) - child_data.fds[0] = self->context->stdin_fd; - else if (self->context->stdin_path != NULL) - { - child_data.fds[0] = close_fds[0] = unix_open_file (self->context->stdin_path, - O_RDONLY, error); - if (child_data.fds[0] == -1) - goto out; - } - else -#endif - if (self->context->stdin_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_NULL) - ; /* nothing */ - else if (self->context->stdin_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT) - spawn_flags |= G_SPAWN_CHILD_INHERITS_STDIN; - else if (self->context->stdin_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_PIPE) - pipe_ptrs[0] = &pipe_fds[0]; - else - g_assert_not_reached (); - - /* Next, stdout. */ -#ifdef G_OS_UNIX - if (self->context->stdout_fd != -1) - child_data.fds[1] = self->context->stdout_fd; - else if (self->context->stdout_path != NULL) - { - child_data.fds[1] = close_fds[1] = unix_open_file (self->context->stdout_path, - O_CREAT | O_WRONLY, error); - if (child_data.fds[1] == -1) - goto out; - } - else -#endif - if (self->context->stdout_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_NULL) - spawn_flags |= G_SPAWN_STDOUT_TO_DEV_NULL; - else if (self->context->stdout_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT) - ; /* Nothing */ - else if (self->context->stdout_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_PIPE) - pipe_ptrs[1] = &pipe_fds[1]; - else - g_assert_not_reached (); - - /* Finally, stderr. */ -#ifdef G_OS_UNIX - if (self->context->stderr_fd != -1) - child_data.fds[2] = self->context->stderr_fd; - else if (self->context->stderr_path != NULL) - { - child_data.fds[2] = close_fds[2] = unix_open_file (self->context->stderr_path, - O_CREAT | O_WRONLY, error); - if (child_data.fds[2] == -1) - goto out; - } - else -#endif - if (self->context->stderr_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_NULL) - spawn_flags |= G_SPAWN_STDERR_TO_DEV_NULL; - else if (self->context->stderr_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT) - ; /* Nothing */ - else if (self->context->stderr_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_PIPE) - pipe_ptrs[2] = &pipe_fds[2]; - else if (self->context->stderr_disposition == GS_SUBPROCESS_STREAM_DISPOSITION_STDERR_MERGE) - /* This will work because stderr gets setup after stdout. */ - child_data.fds[2] = 1; - else - g_assert_not_reached (); - - child_data.inherit_fds = self->context->inherit_fds; - - if (self->context->keep_descriptors) - spawn_flags |= G_SPAWN_LEAVE_DESCRIPTORS_OPEN; - - if (self->context->search_path) - spawn_flags |= G_SPAWN_SEARCH_PATH; - else if (self->context->search_path_from_envp) - spawn_flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP; - else if (!g_path_is_absolute (((gchar**)self->context->argv)[0])) - spawn_flags |= G_SPAWN_SEARCH_PATH; - - if (self->context->has_argv0) - spawn_flags |= G_SPAWN_FILE_AND_ARGV_ZERO; - - spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD; -#ifdef GLIB_COMPILATION - spawn_flags |= G_SPAWN_CLOEXEC_PIPES; -#endif - - child_data.child_setup_func = self->context->child_setup_func; - child_data.child_setup_data = self->context->child_setup_data; - success = g_spawn_async_with_pipes (self->context->cwd, - (char**)self->context->argv, - self->context->envp, - spawn_flags, - child_setup, &child_data, - &self->pid, - pipe_ptrs[0], pipe_ptrs[1], pipe_ptrs[2], - error); - if (success) - self->pid_valid = TRUE; - -out: - for (i = 0; i < 3; i++) - if (close_fds[i] != -1) - close (close_fds[i]); - - for (i = 0; i < self->context->postfork_close_fds->len; i++) - (void) close (g_array_index (self->context->postfork_close_fds, int, i)); - - 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]); - - return success; -} - -static void -initable_iface_init (GInitableIface *initable_iface) -{ - initable_iface->init = initable_init; -} - -/** - * gs_subprocess_new: - * - * Create a new process, using the parameters specified by - * GSSubprocessContext. - * - * Returns: (transfer full): A newly created %GSSubprocess, or %NULL on error (and @error will be set) - * - * Since: 2.36 - */ -GSSubprocess * -gs_subprocess_new (GSSubprocessContext *context, - GCancellable *cancellable, - GError **error) -{ - return g_initable_new (GS_TYPE_SUBPROCESS, - cancellable, error, - "context", context, - NULL); -} - -/** - * gs_subprocess_get_pid: - * @self: a #GSSubprocess - * - * The identifier for this child process; it is valid as long as the - * process @self is referenced. In particular, do - * <emphasis>not</emphasis> call g_spawn_close_pid() on this value; - * that is handled internally. - * - * On some Unix versions, it is possible for there to be a race - * condition where waitpid() may have been called to collect the child - * before any watches (such as that installed by - * gs_subprocess_add_watch()) have fired. If you are planning to use - * native functions such as kill() on the pid, your program should - * gracefully handle an %ESRCH result to mitigate this. - * - * If you want to request process termination, using the high level - * gs_subprocess_request_exit() and gs_subprocess_force_exit() API is - * recommended. - * - * Returns: Operating-system specific identifier for child process - * - * Since: 2.36 - */ -GPid -gs_subprocess_get_pid (GSSubprocess *self) -{ - g_return_val_if_fail (GS_IS_SUBPROCESS (self), 0); - - return self->pid; -} - -/** - * gs_subprocess_get_stdin_pipe: - * - * Returns: (transfer none): Pipe - */ -GOutputStream * -gs_subprocess_get_stdin_pipe (GSSubprocess *self) -{ - g_return_val_if_fail (GS_IS_SUBPROCESS (self), NULL); - g_return_val_if_fail (self->stdin_pipe, NULL); - - return self->stdin_pipe; -} - -/** - * gs_subprocess_get_stdout_pipe: - * - * Returns: (transfer none): Pipe - */ -GInputStream * -gs_subprocess_get_stdout_pipe (GSSubprocess *self) -{ - g_return_val_if_fail (GS_IS_SUBPROCESS (self), NULL); - g_return_val_if_fail (self->stdout_pipe, NULL); - - return self->stdout_pipe; -} - -/** - * gs_subprocess_get_stderr_pipe: - * - * Returns: (transfer none): Pipe - */ -GInputStream * -gs_subprocess_get_stderr_pipe (GSSubprocess *self) -{ - g_return_val_if_fail (GS_IS_SUBPROCESS (self), NULL); - g_return_val_if_fail (self->stderr_pipe, NULL); - - return self->stderr_pipe; -} - -typedef struct { - GSSubprocess *self; - GCancellable *cancellable; - GSimpleAsyncResult *result; -} GSSubprocessWatchData; - -static gboolean -gs_subprocess_on_child_exited (GPid pid, - gint status_code, - gpointer user_data) -{ - GSSubprocessWatchData *data = user_data; - GError *error = NULL; - - if (g_cancellable_set_error_if_cancelled (data->cancellable, &error)) - { - g_simple_async_result_take_error (data->result, error); - } - else - { - data->self->reaped_child = TRUE; - - g_simple_async_result_set_op_res_gssize (data->result, status_code); - } - - g_simple_async_result_complete (data->result); - - g_object_unref (data->result); - g_object_unref (data->self); - g_free (data); - - return FALSE; -} - -/** - * gs_subprocess_wait: - * @self: a #GSSubprocess - * @cancellable: a #GCancellable - * @callback: Invoked when process exits, or @cancellable is cancelled - * @user_data: Data for @callback - * - * Start an asynchronous wait for the subprocess @self to exit. - * - * Since: 2.36 - */ -void -gs_subprocess_wait (GSSubprocess *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSource *source; - GSSubprocessWatchData *data; - - data = g_new0 (GSSubprocessWatchData, 1); - - data->self = g_object_ref (self); - data->result = g_simple_async_result_new ((GObject*)self, callback, user_data, - gs_subprocess_wait); - - source = g_child_watch_source_new (self->pid); - - g_source_set_callback (source, (GSourceFunc)gs_subprocess_on_child_exited, - data, NULL); - if (cancellable) - { - GSource *cancellable_source; - - data->cancellable = g_object_ref (cancellable); - - cancellable_source = g_cancellable_source_new (cancellable); - g_source_add_child_source (source, cancellable_source); - g_source_unref (cancellable_source); - } - - g_source_attach (source, g_main_context_get_thread_default ()); - g_source_unref (source); -} - -/** - * gs_subprocess_wait_finish: - * @self: a #GSSubprocess - * @result: a #GAsyncResult - * @out_exit_status: (out): Exit status of the process encoded in platform-specific way - * @error: a #GError - * - * The exit status of the process will be stored in @out_exit_status. - * See the documentation of g_spawn_check_exit_status() for more - * details. - * - * Note that @error is not set if the process exits abnormally; you - * must use g_spawn_check_exit_status() for that. - * - * Since: 2.36 - */ -gboolean -gs_subprocess_wait_finish (GSSubprocess *self, - GAsyncResult *result, - int *out_exit_status, - GError **error) -{ - GSimpleAsyncResult *simple; - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *out_exit_status = g_simple_async_result_get_op_res_gssize (simple); - - return TRUE; -} - -typedef struct { - GMainLoop *loop; - gint *exit_status_ptr; - gboolean caught_error; - GError **error; -} GSSubprocessSyncWaitData; - -static void -gs_subprocess_on_sync_wait_complete (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GSSubprocessSyncWaitData *data = user_data; - - if (!gs_subprocess_wait_finish ((GSSubprocess*)object, result, - data->exit_status_ptr, data->error)) - data->caught_error = TRUE; - - g_main_loop_quit (data->loop); -} - -/** - * gs_subprocess_wait_sync: - * @self: a #GSSubprocess - * @out_exit_status: (out): Platform-specific exit code - * @cancellable: a #GCancellable - * @error: a #GError - * - * Synchronously wait for the subprocess to terminate, returning the - * status code in @out_exit_status. See the documentation of - * g_spawn_check_exit_status() for how to interpret it. Note that if - * @error is set, then @out_exit_status will be left uninitialized. - * - * Returns: %TRUE on success, %FALSE if @cancellable was cancelled - * - * Since: 2.36 - */ -gboolean -gs_subprocess_wait_sync (GSSubprocess *self, - int *out_exit_status, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - gboolean pushed_thread_default = FALSE; - GMainContext *context = NULL; - GSSubprocessSyncWaitData data; - - memset (&data, 0, sizeof (data)); - - g_return_val_if_fail (GS_IS_SUBPROCESS (self), FALSE); - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - context = g_main_context_new (); - g_main_context_push_thread_default (context); - pushed_thread_default = TRUE; - - data.exit_status_ptr = out_exit_status; - data.loop = g_main_loop_new (context, TRUE); - data.error = error; - - gs_subprocess_wait (self, cancellable, - gs_subprocess_on_sync_wait_complete, &data); - - g_main_loop_run (data.loop); - - if (data.caught_error) - goto out; - - ret = TRUE; - out: - if (pushed_thread_default) - g_main_context_pop_thread_default (context); - if (context) - g_main_context_unref (context); - if (data.loop) - g_main_loop_unref (data.loop); - - return ret; -} - -/** - * gs_subprocess_wait_sync_check: - * @self: a #GSSubprocess - * @cancellable: a #GCancellable - * @error: a #GError - * - * Combines gs_subprocess_wait_sync() with g_spawn_check_exit_status(). - * - * Returns: %TRUE on success, %FALSE if process exited abnormally, or @cancellable was cancelled - * - * Since: 2.36 - */ -gboolean -gs_subprocess_wait_sync_check (GSSubprocess *self, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - int exit_status; - - if (!gs_subprocess_wait_sync (self, &exit_status, cancellable, error)) - goto out; - - if (!g_spawn_check_exit_status (exit_status, error)) - goto out; - - ret = TRUE; - out: - return ret; -} - -/** - * gs_subprocess_request_exit: - * @self: a #GSSubprocess - * - * This API uses an operating-system specific mechanism to request - * that the subprocess gracefully exit. This API is not available on - * all operating systems; for those not supported, it will do nothing - * and return %FALSE. Portable code should handle this situation - * gracefully. For example, if you are communicating via input or - * output pipe with the child, many programs will automatically exit - * when one of their standard input or output are closed. - * - * On Unix, this API sends %SIGTERM. - * - * A %TRUE return value does <emphasis>not</emphasis> mean the - * subprocess has exited, merely that an exit request was initiated. - * You can use gs_subprocess_add_watch() to monitor the status of the - * process after calling this function. - * - * This function returns %TRUE if the process has already exited. - * - * Returns: %TRUE if the operation is supported, %FALSE otherwise. - * - * Since: 2.36 - */ -gboolean -gs_subprocess_request_exit (GSSubprocess *self) -{ - g_return_val_if_fail (GS_IS_SUBPROCESS (self), FALSE); - -#ifdef G_OS_UNIX - (void) kill (self->pid, SIGTERM); - return TRUE; -#else - return FALSE; -#endif -} - -/** - * gs_subprocess_force_exit: - * @self: a #GSSubprocess - * - * Use an operating-system specific method to attempt an immediate, - * forceful termination of the process. There is no mechanism to - * determine whether or not the request itself was successful; - * however, you can use gs_subprocess_wait() to monitor the status of - * the process after calling this function. - * - * On Unix, this function sends %SIGKILL. - */ -void -gs_subprocess_force_exit (GSSubprocess *self) -{ - g_return_if_fail (GS_IS_SUBPROCESS (self)); - -#if !defined(GLIB_COMPIATION) - { - int ret; - do - ret = kill (self->pid, SIGKILL); - while (ret == -1 && errno == EINTR); - } -#elif defined(G_OS_UNIX) - GLIB_PRIVATE_CALL (g_main_send_signal) (self->pid, SIGKILL); -#else - TerminateProcess (self->pid, 1); -#endif -} - -GSSubprocess * -gs_subprocess_new_simple_argl (GSSubprocessStreamDisposition stdout_disposition, - GSSubprocessStreamDisposition stderr_disposition, - GCancellable *cancellable, - GError **error, - const gchar *first_arg, - ...) -{ - va_list args; - GSSubprocess *result; - GSSubprocessContext *context; - - va_start (args, first_arg); - context = gs_subprocess_context_newa (first_arg, args); - va_end (args); - result = gs_subprocess_new (context, cancellable, error); - g_object_unref (context); - - return result; -} - -/** - * gs_subprocess_new_simple_argv: - * @argv: (array zero-terminated=1) (element-type utf8): Argument array - * @stdout_disposition: Where to redirect stdout - * @stderr_disposition: Where to redirect stdout - * @error: a #GError - * - * Create a new subprocess using the provided argument array and - * stream dispositions. - */ -GSSubprocess * -gs_subprocess_new_simple_argv (gchar **argv, - GSSubprocessStreamDisposition stdout_disposition, - GSSubprocessStreamDisposition stderr_disposition, - GCancellable *cancellable, - GError **error) -{ - GSSubprocessContext *context; - GSSubprocess *result; - - context = gs_subprocess_context_new (argv); - gs_subprocess_context_set_stdout_disposition (context, stdout_disposition); - gs_subprocess_context_set_stderr_disposition (context, stderr_disposition); - - result = gs_subprocess_new (context, cancellable, error); - g_object_unref (context); - - return result; -} - -/** - * gs_subprocess_simple_run_sync: - * @cwd: Current working directory - * @stdin_disposition: What to do with standard input - * @cancellable: a #GCancellable - * @error: a #GError - * @first_arg: First argument - * @...: Remaining arguments, %NULL terminated - * - * Run a process synchronously, throw an error if it fails. - */ -gboolean -gs_subprocess_simple_run_sync (const char *cwd, - GSSubprocessStreamDisposition stdin_disposition, - GCancellable *cancellable, - GError **error, - const char *first_arg, - ...) -{ - gboolean ret = FALSE; - va_list args; - GSSubprocess *proc = NULL; - GSSubprocessContext *context = NULL; - - va_start (args, first_arg); - context = gs_subprocess_context_newa (first_arg, args); - va_end (args); - gs_subprocess_context_set_stdin_disposition (context, stdin_disposition); - gs_subprocess_context_set_cwd (context, cwd); - proc = gs_subprocess_new (context, cancellable, error); - if (!proc) - goto out; - - if (!gs_subprocess_wait_sync_check (proc, cancellable, error)) - goto out; - - ret = TRUE; - out: - g_object_unref (context); - if (proc) - g_object_unref (proc); - return ret; -} - -#endif |