X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgsubprocess.c;h=999d777e4f8d07c7b2b63cf405e1a364d1b349f7;hb=51fac05d73f8363de821eb0d6940dedca13a8c0f;hp=2e517f9b735887b6d5deb9044db3e8ce355f6c12;hpb=9318d5a4292544f2f7f8f9bc2d805974b3b52c7e;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gsubprocess.c b/gio/gsubprocess.c index 2e517f9..999d777 100644 --- a/gio/gsubprocess.c +++ b/gio/gsubprocess.c @@ -1,7 +1,7 @@ /* GIO - GLib Input, Output and Streaming Library * - * Copyright © 2012 Red Hat, Inc. - * Copyright © 2012-2013 Canonical Limited + * Copyright © 2012, 2013 Red Hat, Inc. + * Copyright © 2012, 2013 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 @@ -18,6 +18,7 @@ * SECTION:gsubprocess * @title: GSubprocess * @short_description: Child processes + * @include: gio/gio.h * @see_also: #GSubprocessLauncher * * #GSubprocess allows the creation of and interaction with child @@ -35,16 +36,15 @@ * 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 subprocess.py + * some other languages such as the `subprocess.py` * 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. * * A powerful g_subprocess_communicate() API is provided similar to the - * communicate() method of - * subprocess.py. This enables very easy interaction - * with a subprocess that has been opened with pipes. + * `communicate()` method of `subprocess.py`. This enables very easy + * interaction with a subprocess that has been opened with pipes. * * #GSubprocess defaults to tight control over the file descriptors open * in the child process, avoiding dangling-fd issues that are caused by @@ -71,12 +71,12 @@ * change of working directory, child setup functions, etc). * * A typical use of #GSubprocess will involve calling - * g_subprocess_new(), followed by g_subprocess_wait() or - * g_subprocess_wait_sync(). After the process exits, the status can be + * g_subprocess_new(), followed by g_subprocess_wait_async() or + * g_subprocess_wait(). After the process exits, the status can be * checked using functions such as g_subprocess_get_if_exited() (which * are similar to the familiar WIFEXITED-style POSIX macros). * - * Since: 2.36 + * Since: 2.40 **/ #include "config.h" @@ -99,8 +99,8 @@ #include #endif #ifdef G_OS_WIN32 -#define _WIN32_WINNT 0x0500 #include +#include #include "giowin32-priv.h" #endif @@ -108,6 +108,12 @@ #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#else +#define HAVE_O_CLOEXEC 1 +#endif + #define COMMUNICATE_READ_SIZE 4096 /* A GSubprocess can have two possible states: running and not. @@ -173,6 +179,7 @@ enum N_PROPS }; +#ifdef G_OS_UNIX typedef struct { gint fds[3]; @@ -199,6 +206,34 @@ unset_cloexec (int fd) } } +static int +dupfd_cloexec (int parent_fd) +{ + int fd; +#ifdef F_DUPFD_CLOEXEC + do + fd = fcntl (parent_fd, F_DUPFD_CLOEXEC, 3); + while (fd == -1 && errno == EINTR); +#else + /* OS X Snow Lion and earlier don't have F_DUPFD_CLOEXEC: + * https://bugzilla.gnome.org/show_bug.cgi?id=710962 + */ + int result, flags; + do + fd = fcntl (parent_fd, F_DUPFD, 3); + while (fd == -1 && errno == EINTR); + flags = fcntl (fd, F_GETFD, 0); + if (flags != -1) + { + flags |= FD_CLOEXEC; + do + result = fcntl (fd, F_SETFD, flags); + while (result == -1 && errno == EINTR); + } +#endif + return fd; +} + /** * Based on code derived from * gnome-terminal:src/terminal-screen.c:terminal_screen_child_setup(), @@ -250,9 +285,7 @@ child_setup (gpointer user_data) gint parent_fd = g_array_index (child_data->needdup_fd_assignments, int, i); gint new_parent_fd; - do - new_parent_fd = fcntl (parent_fd, F_DUPFD_CLOEXEC, 3); - while (parent_fd == -1 && errno == EINTR); + new_parent_fd = dupfd_cloexec (parent_fd); g_array_index (child_data->needdup_fd_assignments, int, i) = new_parent_fd; } @@ -278,6 +311,7 @@ child_setup (gpointer user_data) if (child_data->child_setup_func) child_data->child_setup_func (child_data->child_setup_data); } +#endif static GInputStream * platform_input_stream_from_spawn_fd (gint fd) @@ -328,6 +362,10 @@ unix_open_file (const char *filename, g_free (display_name); /* fall through... */ } +#ifndef HAVE_O_CLOEXEC + else + fcntl (my_fd, F_SETFD, FD_CLOEXEC); +#endif return my_fd; } @@ -377,6 +415,7 @@ g_subprocess_exited (GPid pid, while (tasks) { g_task_return_boolean (tasks->data, TRUE); + g_object_unref (tasks->data); tasks = g_slist_delete_link (tasks, tasks); } @@ -391,7 +430,9 @@ initable_init (GInitable *initable, GError **error) { GSubprocess *self = G_SUBPROCESS (initable); +#ifdef G_OS_UNIX ChildData child_data = { { -1, -1, -1 }, 0 }; +#endif gint *pipe_ptrs[3] = { NULL, NULL, NULL }; gint pipe_fds[3] = { -1, -1, -1 }; gint close_fds[3] = { -1, -1, -1 }; @@ -453,10 +494,10 @@ initable_init (GInitable *initable, spawn_flags |= G_SPAWN_STDERR_TO_DEV_NULL; else if (self->flags & G_SUBPROCESS_FLAGS_STDERR_PIPE) pipe_ptrs[2] = &pipe_fds[2]; +#ifdef G_OS_UNIX 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->launcher) { if (self->launcher->stderr_fd != -1) @@ -493,13 +534,20 @@ initable_init (GInitable *initable, spawn_flags |= G_SPAWN_DO_NOT_REAP_CHILD; spawn_flags |= G_SPAWN_CLOEXEC_PIPES; +#ifdef G_OS_UNIX child_data.child_setup_func = self->launcher ? self->launcher->child_setup_func : NULL; child_data.child_setup_data = self->launcher ? self->launcher->child_setup_user_data : NULL; +#endif + success = g_spawn_async_with_pipes (self->launcher ? self->launcher->cwd : NULL, self->argv, self->launcher ? self->launcher->envp : NULL, spawn_flags, +#ifdef G_OS_UNIX child_setup, &child_data, +#else + NULL, NULL, +#endif &self->pid, pipe_ptrs[0], pipe_ptrs[1], pipe_ptrs[2], error); @@ -515,7 +563,7 @@ initable_init (GInitable *initable, identifier = (guint64) self->pid; #endif - s = snprintf (self->identifier, sizeof self->identifier, "%"G_GUINT64_FORMAT, identifier); + s = g_snprintf (self->identifier, sizeof self->identifier, "%"G_GUINT64_FORMAT, identifier); g_assert (0 < s && s < sizeof self->identifier); } @@ -532,7 +580,9 @@ initable_init (GInitable *initable, g_source_unref (source); } +#ifdef G_OS_UNIX out: +#endif /* we don't need this past init... */ self->launcher = NULL; @@ -558,7 +608,9 @@ g_subprocess_finalize (GObject *object) g_clear_object (&self->stdin_pipe); g_clear_object (&self->stdout_pipe); g_clear_object (&self->stderr_pipe); - g_free (self->argv); + g_strfreev (self->argv); + + g_mutex_clear (&self->pending_waits_lock); G_OBJECT_CLASS (g_subprocess_parent_class)->finalize (object); } @@ -566,6 +618,7 @@ g_subprocess_finalize (GObject *object) static void g_subprocess_init (GSubprocess *self) { + g_mutex_init (&self->pending_waits_lock); } static void @@ -594,15 +647,23 @@ g_subprocess_class_init (GSubprocessClass *class) /** * g_subprocess_new: (skip) + * @flags: flags that define the behaviour of the subprocess + * @error: (allow-none): return location for an error, or %NULL + * @argv0: first commandline argument to pass to the subprocess + * @...: more commandline arguments, followed by %NULL * - * Create a new process with the given flags and varargs argument list. + * Create a new process with the given flags and varargs argument + * list. By default, matching the g_spawn_async() defaults, the + * child's stdin will be set to the system null device, and + * stdout/stderr will be inherited from the parent. You can use + * @flags to control this behavior. * * The argument list must be terminated with %NULL. * * Returns: A newly created #GSubprocess, or %NULL on error (and @error * will be set) * - * Since: 2.36 + * Since: 2.40 */ GSubprocess * g_subprocess_new (GSubprocessFlags flags, @@ -625,6 +686,7 @@ g_subprocess_new (GSubprocessFlags flags, while ((arg = va_arg (ap, const gchar *))) g_ptr_array_add (args, (gchar *) arg); g_ptr_array_add (args, NULL); + va_end (ap); result = g_subprocess_newv ((const gchar * const *) args->pdata, flags, error); @@ -635,6 +697,9 @@ g_subprocess_new (GSubprocessFlags flags, /** * g_subprocess_newv: + * @argv: (array zero-terminated=1) (element-type utf8): commandline arguments for the subprocess + * @flags: flags that define the behaviour of the subprocess + * @error: (allow-none): return location for an error, or %NULL * * Create a new process with the given flags and argument list. * @@ -643,7 +708,7 @@ g_subprocess_new (GSubprocessFlags flags, * Returns: A newly created #GSubprocess, or %NULL on error (and @error * will be set) * - * Since: 2.36 + * Since: 2.40 * Rename to: g_subprocess_new */ GSubprocess * @@ -659,42 +724,91 @@ g_subprocess_newv (const gchar * const *argv, NULL); } +/** + * g_subprocess_get_identifier: + * @subprocess: a #GSubprocess + * + * On UNIX, returns the process ID as a decimal string. + * On Windows, returns the result of GetProcessId() also as a string. + */ const gchar * -g_subprocess_get_identifier (GSubprocess *self) +g_subprocess_get_identifier (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), NULL); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), NULL); - if (self->pid) - return self->identifier; + if (subprocess->pid) + return subprocess->identifier; else return NULL; } +/** + * g_subprocess_get_stdin_pipe: + * @subprocess: a #GSubprocess + * + * Gets the #GOutputStream that you can write to in order to give data + * to the stdin of @subprocess. + * + * The process must have been created with + * %G_SUBPROCESS_FLAGS_STDIN_PIPE. + * + * Returns: (transfer none): the stdout pipe + * + * Since: 2.40 + **/ GOutputStream * -g_subprocess_get_stdin_pipe (GSubprocess *self) +g_subprocess_get_stdin_pipe (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), NULL); - g_return_val_if_fail (self->stdin_pipe, NULL); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), NULL); + g_return_val_if_fail (subprocess->stdin_pipe, NULL); - return self->stdin_pipe; + return subprocess->stdin_pipe; } +/** + * g_subprocess_get_stdout_pipe: + * @subprocess: a #GSubprocess + * + * Gets the #GInputStream from which to read the stdout output of + * @subprocess. + * + * The process must have been created with + * %G_SUBPROCESS_FLAGS_STDOUT_PIPE. + * + * Returns: (transfer none): the stdout pipe + * + * Since: 2.40 + **/ GInputStream * -g_subprocess_get_stdout_pipe (GSubprocess *self) +g_subprocess_get_stdout_pipe (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), NULL); - g_return_val_if_fail (self->stdout_pipe, NULL); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), NULL); + g_return_val_if_fail (subprocess->stdout_pipe, NULL); - return self->stdout_pipe; + return subprocess->stdout_pipe; } +/** + * g_subprocess_get_stderr_pipe: + * @subprocess: a #GSubprocess + * + * Gets the #GInputStream from which to read the stderr output of + * @subprocess. + * + * The process must have been created with + * %G_SUBPROCESS_FLAGS_STDERR_PIPE. + * + * Returns: (transfer none): the stderr pipe + * + * Since: 2.40 + **/ GInputStream * -g_subprocess_get_stderr_pipe (GSubprocess *self) +g_subprocess_get_stderr_pipe (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), NULL); - g_return_val_if_fail (self->stderr_pipe, NULL); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), NULL); + g_return_val_if_fail (subprocess->stderr_pipe, NULL); - return self->stderr_pipe; + return subprocess->stderr_pipe; } static void @@ -714,18 +828,31 @@ g_subprocess_wait_cancelled (GCancellable *cancellable, g_object_unref (task); } +/** + * g_subprocess_wait_async: + * @subprocess: a #GSubprocess + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback to call when the operation is complete + * @user_data: user_data for @callback + * + * Wait for the subprocess to terminate. + * + * This is the asynchronous version of g_subprocess_wait(). + * + * Since: 2.40 + */ void -g_subprocess_wait_async (GSubprocess *self, +g_subprocess_wait_async (GSubprocess *subprocess, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; - task = g_task_new (self, cancellable, callback, user_data); + task = g_task_new (subprocess, cancellable, callback, user_data); - g_mutex_lock (&self->pending_waits_lock); - if (self->pid) + g_mutex_lock (&subprocess->pending_waits_lock); + if (subprocess->pid) { /* Only bother with cancellable if we're putting it in the list. * If not, it's going to dispatch immediately anyway and we will @@ -734,10 +861,10 @@ g_subprocess_wait_async (GSubprocess *self, if (cancellable) g_signal_connect_object (cancellable, "cancelled", G_CALLBACK (g_subprocess_wait_cancelled), task, 0); - self->pending_waits = g_slist_prepend (self->pending_waits, task); + subprocess->pending_waits = g_slist_prepend (subprocess->pending_waits, task); task = NULL; } - g_mutex_unlock (&self->pending_waits_lock); + g_mutex_unlock (&subprocess->pending_waits_lock); /* If we still have task then it's because did_exit is already TRUE */ if (task != NULL) @@ -747,8 +874,21 @@ g_subprocess_wait_async (GSubprocess *self, } } +/** + * g_subprocess_wait_finish: + * @subprocess: a #GSubprocess + * @result: the #GAsyncResult passed to your #GAsyncReadyCallback + * @error: a pointer to a %NULL #GError, or %NULL + * + * Collects the result of a previous call to + * g_subprocess_wait_async(). + * + * Returns: %TRUE if successful, or %FALSE with @error set + * + * Since: 2.40 + */ gboolean -g_subprocess_wait_finish (GSubprocess *self, +g_subprocess_wait_finish (GSubprocess *subprocess, GAsyncResult *result, GError **error) { @@ -788,28 +928,35 @@ g_subprocess_sync_complete (GAsyncResult **result) /** * g_subprocess_wait: - * @self: a #GSubprocess + * @subprocess: a #GSubprocess * @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. + * Synchronously wait for the subprocess to terminate. + * + * After the process terminates you can query its exit status with + * functions such as g_subprocess_get_if_exited() and + * g_subprocess_get_exit_status(). + * + * This function does not fail in the case of the subprocess having + * abnormal termination. See g_subprocess_wait_check() for that. + * + * Cancelling @cancellable doesn't kill the subprocess. Call + * g_subprocess_force_exit() if it is desirable. * * Returns: %TRUE on success, %FALSE if @cancellable was cancelled * - * Since: 2.36 + * Since: 2.40 */ gboolean -g_subprocess_wait (GSubprocess *self, +g_subprocess_wait (GSubprocess *subprocess, GCancellable *cancellable, GError **error) { GAsyncResult *result = NULL; gboolean success; - g_return_val_if_fail (G_IS_SUBPROCESS (self), FALSE); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE); /* Synchronous waits are actually the 'more difficult' case because we * need to deal with the possibility of cancellation. That more or @@ -825,56 +972,83 @@ g_subprocess_wait (GSubprocess *self, /* We can shortcut in the case that the process already quit (but only * after we checked the cancellable). */ - if (self->pid == 0) + if (subprocess->pid == 0) return TRUE; /* Otherwise, we need to do this the long way... */ g_subprocess_sync_setup (); - g_subprocess_wait_async (self, cancellable, g_subprocess_sync_done, &result); + g_subprocess_wait_async (subprocess, cancellable, g_subprocess_sync_done, &result); g_subprocess_sync_complete (&result); - success = g_subprocess_wait_finish (self, result, error); + success = g_subprocess_wait_finish (subprocess, result, error); g_object_unref (result); return success; } /** - * g_subprocess_wait_sync_check: - * @self: a #GSubprocess + * g_subprocess_wait_check: + * @subprocess: a #GSubprocess * @cancellable: a #GCancellable * @error: a #GError * - * Combines g_subprocess_wait_sync() with g_spawn_check_exit_status(). + * Combines g_subprocess_wait() with g_spawn_check_exit_status(). * - * Returns: %TRUE on success, %FALSE if process exited abnormally, or @cancellable was cancelled + * Returns: %TRUE on success, %FALSE if process exited abnormally, or + * @cancellable was cancelled * - * Since: 2.36 + * Since: 2.40 */ gboolean -g_subprocess_wait_check (GSubprocess *self, +g_subprocess_wait_check (GSubprocess *subprocess, GCancellable *cancellable, GError **error) { - return g_subprocess_wait (self, cancellable, error) && - g_spawn_check_exit_status (self->status, error); + return g_subprocess_wait (subprocess, cancellable, error) && + g_spawn_check_exit_status (subprocess->status, error); } +/** + * g_subprocess_wait_check_async: + * @subprocess: a #GSubprocess + * @cancellable: a #GCancellable, or %NULL + * @callback: a #GAsyncReadyCallback to call when the operation is complete + * @user_data: user_data for @callback + * + * Combines g_subprocess_wait_async() with g_spawn_check_exit_status(). + * + * This is the asynchronous version of g_subprocess_wait_check(). + * + * Since: 2.40 + */ void -g_subprocess_wait_check_async (GSubprocess *self, +g_subprocess_wait_check_async (GSubprocess *subprocess, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { - g_subprocess_wait_async (self, cancellable, callback, user_data); + g_subprocess_wait_async (subprocess, cancellable, callback, user_data); } +/** + * g_subprocess_wait_check_finish: + * @subprocess: a #GSubprocess + * @result: the #GAsyncResult passed to your #GAsyncReadyCallback + * @error: a pointer to a %NULL #GError, or %NULL + * + * Collects the result of a previous call to + * g_subprocess_wait_check_async(). + * + * Returns: %TRUE if successful, or %FALSE with @error set + * + * Since: 2.40 + */ gboolean -g_subprocess_wait_check_finish (GSubprocess *self, +g_subprocess_wait_check_finish (GSubprocess *subprocess, GAsyncResult *result, GError **error) { - return g_subprocess_wait_finish (self, result, error) && - g_spawn_check_exit_status (self->status, error); + return g_subprocess_wait_finish (subprocess, result, error) && + g_spawn_check_exit_status (subprocess->status, error); } #ifdef G_OS_UNIX @@ -903,12 +1077,12 @@ g_subprocess_actually_send_signal (gpointer user_data) } static void -g_subprocess_dispatch_signal (GSubprocess *self, +g_subprocess_dispatch_signal (GSubprocess *subprocess, gint signalnum) { - SignalRecord signal_record = { g_object_ref (self), signalnum }; + SignalRecord signal_record = { g_object_ref (subprocess), signalnum }; - g_return_if_fail (G_IS_SUBPROCESS (self)); + g_return_if_fail (G_IS_SUBPROCESS (subprocess)); /* This MUST be a lower priority than the priority that the child * watch source uses in initable_init(). @@ -930,7 +1104,7 @@ g_subprocess_dispatch_signal (GSubprocess *self, /** * g_subprocess_send_signal: - * @self: a #GSubprocess + * @subprocess: a #GSubprocess * @signal_num: the signal number to send * * Sends the UNIX signal @signal_num to the subprocess, if it is still @@ -941,21 +1115,21 @@ g_subprocess_dispatch_signal (GSubprocess *self, * * This API is not available on Windows. * - * Since: 2.36 + * Since: 2.40 **/ void -g_subprocess_send_signal (GSubprocess *self, +g_subprocess_send_signal (GSubprocess *subprocess, gint signal_num) { - g_return_if_fail (G_IS_SUBPROCESS (self)); + g_return_if_fail (G_IS_SUBPROCESS (subprocess)); - g_subprocess_dispatch_signal (self, signal_num); + g_subprocess_dispatch_signal (subprocess, signal_num); } #endif /** * g_subprocess_force_exit: - * @self: a #GSubprocess + * @subprocess: a #GSubprocess * * Use an operating-system specific method to attempt an immediate, * forceful termination of the process. There is no mechanism to @@ -965,23 +1139,23 @@ g_subprocess_send_signal (GSubprocess *self, * * On Unix, this function sends %SIGKILL. * - * Since: 2.36 + * Since: 2.40 **/ void -g_subprocess_force_exit (GSubprocess *self) +g_subprocess_force_exit (GSubprocess *subprocess) { - g_return_if_fail (G_IS_SUBPROCESS (self)); + g_return_if_fail (G_IS_SUBPROCESS (subprocess)); #ifdef G_OS_UNIX - g_subprocess_dispatch_signal (self, SIGKILL); + g_subprocess_dispatch_signal (subprocess, SIGKILL); #else - TerminateProcess (self->pid, 1); + TerminateProcess (subprocess->pid, 1); #endif } /** * g_subprocess_get_status: - * @self: a #GSubprocess + * @subprocess: a #GSubprocess * * Gets the raw status code of the process, as from waitpid(). * @@ -997,20 +1171,20 @@ g_subprocess_force_exit (GSubprocess *self) * * Returns: the (meaningless) waitpid() exit status from the kernel * - * Since: 2.36 + * Since: 2.40 **/ gint -g_subprocess_get_status (GSubprocess *self) +g_subprocess_get_status (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), FALSE); - g_return_val_if_fail (self->pid == 0, FALSE); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE); + g_return_val_if_fail (subprocess->pid == 0, FALSE); - return self->status; + return subprocess->status; } /** * g_subprocess_get_successful: - * @self: a #GSubprocess + * @subprocess: a #GSubprocess * * Checks if the process was "successful". A process is considered * successful if it exited cleanly with an exit status of 0, either by @@ -1021,20 +1195,24 @@ g_subprocess_get_status (GSubprocess *self) * * Returns: %TRUE if the process exited cleanly with a exit status of 0 * - * Since: 2.36 + * Since: 2.40 **/ gboolean -g_subprocess_get_successful (GSubprocess *self) +g_subprocess_get_successful (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), FALSE); - g_return_val_if_fail (self->pid == 0, FALSE); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE); + g_return_val_if_fail (subprocess->pid == 0, FALSE); - return WIFEXITED (self->status) && WEXITSTATUS (self->status) == 0; +#ifdef G_OS_UNIX + return WIFEXITED (subprocess->status) && WEXITSTATUS (subprocess->status) == 0; +#else + return subprocess->status == 0; +#endif } /** * g_subprocess_get_if_exited: - * @self: a #GSubprocess + * @subprocess: a #GSubprocess * * Check if the given subprocess exited normally (ie: by way of exit() * or return from main()). @@ -1046,20 +1224,24 @@ g_subprocess_get_successful (GSubprocess *self) * * Returns: %TRUE if the case of a normal exit * - * Since: 2.36 + * Since: 2.40 **/ gboolean -g_subprocess_get_if_exited (GSubprocess *self) +g_subprocess_get_if_exited (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), FALSE); - g_return_val_if_fail (self->pid == 0, FALSE); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE); + g_return_val_if_fail (subprocess->pid == 0, FALSE); - return WIFEXITED (self->status); +#ifdef G_OS_UNIX + return WIFEXITED (subprocess->status); +#else + return TRUE; +#endif } /** * g_subprocess_get_exit_status: - * @self: a #GSubprocess + * @subprocess: a #GSubprocess * * Check the exit status of the subprocess, given that it exited * normally. This is the value passed to the exit() system call or the @@ -1072,21 +1254,26 @@ g_subprocess_get_if_exited (GSubprocess *self) * * Returns: the exit status * - * Since: 2.36 + * Since: 2.40 **/ gint -g_subprocess_get_exit_status (GSubprocess *self) +g_subprocess_get_exit_status (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), 1); - g_return_val_if_fail (self->pid == 0, 1); - g_return_val_if_fail (WIFEXITED (self->status), 1); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), 1); + g_return_val_if_fail (subprocess->pid == 0, 1); + +#ifdef G_OS_UNIX + g_return_val_if_fail (WIFEXITED (subprocess->status), 1); - return WEXITSTATUS (self->status); + return WEXITSTATUS (subprocess->status); +#else + return subprocess->status; +#endif } /** * g_subprocess_get_if_signaled: - * @self: a #GSubprocess + * @subprocess: a #GSubprocess * * Check if the given subprocess terminated in response to a signal. * @@ -1097,20 +1284,24 @@ g_subprocess_get_exit_status (GSubprocess *self) * * Returns: %TRUE if the case of termination due to a signal * - * Since: 2.36 + * Since: 2.40 **/ gboolean -g_subprocess_get_if_signaled (GSubprocess *self) +g_subprocess_get_if_signaled (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), FALSE); - g_return_val_if_fail (self->pid == 0, FALSE); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE); + g_return_val_if_fail (subprocess->pid == 0, FALSE); - return WIFSIGNALED (self->status); +#ifdef G_OS_UNIX + return WIFSIGNALED (subprocess->status); +#else + return FALSE; +#endif } /** * g_subprocess_get_term_sig: - * @self: a #GSubprocess + * @subprocess: a #GSubprocess * * Get the signal number that caused the subprocess to terminate, given * that it terminated due to a signal. @@ -1122,16 +1313,23 @@ g_subprocess_get_if_signaled (GSubprocess *self) * * Returns: the signal causing termination * - * Since: 2.36 + * Since: 2.40 **/ gint -g_subprocess_get_term_sig (GSubprocess *self) +g_subprocess_get_term_sig (GSubprocess *subprocess) { - g_return_val_if_fail (G_IS_SUBPROCESS (self), 0); - g_return_val_if_fail (self->pid == 0, 0); - g_return_val_if_fail (WIFSIGNALED (self->status), 0); + g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), 0); + g_return_val_if_fail (subprocess->pid == 0, 0); + +#ifdef G_OS_UNIX + g_return_val_if_fail (WIFSIGNALED (subprocess->status), 0); - return WTERMSIG (self->status); + return WTERMSIG (subprocess->status); +#else + g_critical ("g_subprocess_get_term_sig() called on Windows, where " + "g_subprocess_get_if_signaled() always returns FALSE..."); + return 0; +#endif } /*< private >*/ @@ -1226,7 +1424,7 @@ g_subprocess_communicate_made_progress (GObject *source_object, source == state->stdout_buf || source == state->stderr_buf) { - if (!g_output_stream_splice_finish ((GOutputStream*)source, result, &error)) + if (g_output_stream_splice_finish ((GOutputStream*) source, result, &error) == -1) goto out; if (source == state->stdout_buf || @@ -1294,13 +1492,17 @@ g_subprocess_communicate_state_free (gpointer data) { CommunicateState *state = data; + g_clear_object (&state->cancellable); g_clear_object (&state->stdin_buf); g_clear_object (&state->stdout_buf); g_clear_object (&state->stderr_buf); - if (!g_source_is_destroyed (state->cancellable_source)) - g_source_destroy (state->cancellable_source); - g_source_unref (state->cancellable_source); + if (state->cancellable_source) + { + if (!g_source_is_destroyed (state->cancellable_source)) + g_source_destroy (state->cancellable_source); + g_source_unref (state->cancellable_source); + } g_slice_free (CommunicateState, state); } @@ -1366,13 +1568,14 @@ g_subprocess_communicate_internal (GSubprocess *subprocess, g_subprocess_communicate_made_progress, g_object_ref (task)); state->outstanding_ops++; + g_object_unref (task); return state; } /** * g_subprocess_communicate: - * @self: a #GSubprocess - * @stdin_buf: data to send to the stdin of the subprocess, or %NULL + * @subprocess: a #GSubprocess + * @stdin_buf: (allow-none): data to send to the stdin of the subprocess, or %NULL * @cancellable: a #GCancellable * @stdout_buf: (out): data read from the subprocess stdout * @stderr_buf: (out): data read from the subprocess stderr @@ -1381,7 +1584,7 @@ g_subprocess_communicate_internal (GSubprocess *subprocess, * Communicate with the subprocess until it terminates, and all input * and output has been completed. * - * If @stdin is given, the subprocess must have been created with + * If @stdin_buf is given, the subprocess must have been created with * %G_SUBPROCESS_FLAGS_STDIN_PIPE. The given data is fed to the * stdin of the subprocess and the pipe is closed (ie: EOF). * @@ -1453,8 +1656,8 @@ g_subprocess_communicate (GSubprocess *subprocess, /** * g_subprocess_communicate_async: * @subprocess: Self - * @stdin_buf: Input data - * @cancellable: Cancellable + * @stdin_buf: (allow-none): Input data, or %NULL + * @cancellable: (allow-none): Cancellable * @callback: Callback * @user_data: User data * @@ -1518,8 +1721,8 @@ g_subprocess_communicate_finish (GSubprocess *subprocess, /** * g_subprocess_communicate_utf8: - * @self: a #GSubprocess - * @stdin_buf: data to send to the stdin of the subprocess, or %NULL + * @subprocess: a #GSubprocess + * @stdin_buf: (allow-none): data to send to the stdin of the subprocess, or %NULL * @cancellable: a #GCancellable * @stdout_buf: (out): data read from the subprocess stdout * @stderr_buf: (out): data read from the subprocess stderr @@ -1529,23 +1732,26 @@ g_subprocess_communicate_finish (GSubprocess *subprocess, * process as UTF-8, and returns it as a regular NUL terminated string. */ gboolean -g_subprocess_communicate_utf8 (GSubprocess *subprocess, - const char *stdin_buf, - GCancellable *cancellable, - char **stdout_buf, - char **stderr_buf, - GError **error) +g_subprocess_communicate_utf8 (GSubprocess *subprocess, + const char *stdin_buf, + GCancellable *cancellable, + char **stdout_buf, + char **stderr_buf, + GError **error) { GAsyncResult *result = NULL; gboolean success; GBytes *stdin_bytes; + size_t stdin_buf_len = 0; g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), FALSE); g_return_val_if_fail (stdin_buf == NULL || (subprocess->flags & G_SUBPROCESS_FLAGS_STDIN_PIPE), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - stdin_bytes = g_bytes_new (stdin_buf, strlen (stdin_buf)); + if (stdin_buf != NULL) + stdin_buf_len = strlen (stdin_buf); + stdin_bytes = g_bytes_new (stdin_buf, stdin_buf_len); g_subprocess_sync_setup (); g_subprocess_communicate_internal (subprocess, TRUE, stdin_bytes, cancellable, @@ -1561,29 +1767,34 @@ g_subprocess_communicate_utf8 (GSubprocess *subprocess, /** * g_subprocess_communicate_utf8_async: * @subprocess: Self - * @stdin_buf: Input data + * @stdin_buf: (allow-none): Input data, or %NULL * @cancellable: Cancellable * @callback: Callback * @user_data: User data * - * Asynchronous version of g_subprocess_communicate_utf(). Complete + * Asynchronous version of g_subprocess_communicate_utf8(). Complete * invocation with g_subprocess_communicate_utf8_finish(). */ void -g_subprocess_communicate_utf8_async (GSubprocess *subprocess, - const char *stdin_buf, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_subprocess_communicate_utf8_async (GSubprocess *subprocess, + const char *stdin_buf, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GBytes *stdin_bytes; + size_t stdin_buf_len = 0; g_return_if_fail (G_IS_SUBPROCESS (subprocess)); g_return_if_fail (stdin_buf == NULL || (subprocess->flags & G_SUBPROCESS_FLAGS_STDIN_PIPE)); g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); - stdin_bytes = g_bytes_new (stdin_buf, strlen (stdin_buf)); + if (stdin_buf != NULL) + stdin_buf_len = strlen (stdin_buf); + stdin_bytes = g_bytes_new (stdin_buf, stdin_buf_len); + g_subprocess_communicate_internal (subprocess, TRUE, stdin_bytes, cancellable, callback, user_data); + g_bytes_unref (stdin_bytes); } @@ -1627,11 +1838,11 @@ communicate_result_validate_utf8 (const char *stream_name, * Complete an invocation of g_subprocess_communicate_utf8_async(). */ gboolean -g_subprocess_communicate_utf8_finish (GSubprocess *subprocess, - GAsyncResult *result, - char **stdout_buf, - char **stderr_buf, - GError **error) +g_subprocess_communicate_utf8_finish (GSubprocess *subprocess, + GAsyncResult *result, + char **stdout_buf, + char **stderr_buf, + GError **error) { gboolean ret = FALSE; CommunicateState *state;