[gnome-builder/wip/chergert/flatpak-breakout] wip: subprocess abstraction
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/flatpak-breakout] wip: subprocess abstraction
- Date: Tue, 6 Sep 2016 22:58:45 +0000 (UTC)
commit 2036c464b758eedefa546fd99df6167dd09b86b4
Author: Christian Hergert <chergert redhat com>
Date: Tue Sep 6 15:58:23 2016 -0700
wip: subprocess abstraction
To support Building applications while running from inside the sandbox, we
need to allow breaking out of the sandbox for such tooling. For example,
we want to run the build tooling for flatpaks from the host system (since
we wont have sufficient privileges inside the sandbox).
This abstracts our use of GSubprocess so that we can have multiple
implementations. Sadly, since GSubprocess is not an interface, we cannot
simply create an alternate implementation.
There are two implementations of the abstraction. IdeSimpleSubprocess is
just a wrapper around GSubprocess (since our API is almost identical).
Additionally, we have IdeHostProcess which is a process that runs within
the host system, via the --allow=devel support in flatpak.
libide/Makefile.am | 11 +-
libide/buildsystem/ide-build-command.c | 23 +-
libide/buildsystem/ide-build-result.c | 9 +-
libide/buildsystem/ide-build-result.h | 2 +-
libide/ide-types.h | 3 +
libide/ide.h | 3 +-
libide/runner/ide-runner.c | 29 +-
libide/runtimes/ide-runtime.h | 2 +-
.../subprocess/ide-breakout-subprocess-private.h | 35 +
libide/subprocess/ide-breakout-subprocess.c | 732 ++++++++++++++++++++
libide/subprocess/ide-breakout-subprocess.h | 32 +
libide/subprocess/ide-simple-subprocess.c | 310 +++++++++
libide/subprocess/ide-simple-subprocess.h | 34 +
.../ide-subprocess-launcher.c | 44 +-
.../ide-subprocess-launcher.h | 33 +-
libide/subprocess/ide-subprocess.c | 316 +++++++++
libide/subprocess/ide-subprocess.h | 141 ++++
plugins/autotools/ide-autotools-build-system.c | 14 +-
plugins/autotools/ide-autotools-build-task.c | 64 +-
plugins/autotools/ide-makecache.c | 4 +-
plugins/flatpak/gbp-flatpak-runtime.c | 38 +-
21 files changed, 1758 insertions(+), 121 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index dddce43..cc1ef10 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -124,6 +124,8 @@ libide_1_0_la_public_headers = \
sourceview/ide-source-style-scheme.h \
sourceview/ide-source-view-mode.h \
sourceview/ide-source-view.h \
+ subprocess/ide-subprocess.h \
+ subprocess/ide-subprocess-launcher.h \
symbols/ide-symbol-node.h \
symbols/ide-symbol-resolver.h \
symbols/ide-symbol-tree.h \
@@ -175,7 +177,6 @@ libide_1_0_la_public_headers = \
workbench/ide-workbench-addin.h \
workbench/ide-workbench-header-bar.h \
workbench/ide-workbench.h \
- workers/ide-subprocess-launcher.h \
workers/ide-worker.h \
$(NULL)
@@ -287,6 +288,8 @@ libide_1_0_la_public_sources = \
sourceview/ide-source-style-scheme.c \
sourceview/ide-source-view-mode.c \
sourceview/ide-source-view.c \
+ subprocess/ide-subprocess.c \
+ subprocess/ide-subprocess-launcher.c \
symbols/ide-symbol-node.c \
symbols/ide-symbol-resolver.c \
symbols/ide-symbol-tree.c \
@@ -326,7 +329,6 @@ libide_1_0_la_public_sources = \
workbench/ide-workbench-header-bar.c \
workbench/ide-workbench-open.c \
workbench/ide-workbench.c \
- workers/ide-subprocess-launcher.c \
workers/ide-worker.c \
$(NULL)
@@ -416,6 +418,11 @@ libide_1_0_la_SOURCES = \
sourceview/ide-text-iter.h \
sourceview/ide-text-util.c \
sourceview/ide-text-util.h \
+ subprocess/ide-breakout-subprocess.c \
+ subprocess/ide-breakout-subprocess.h \
+ subprocess/ide-breakout-subprocess-private.h \
+ subprocess/ide-simple-subprocess.c \
+ subprocess/ide-simple-subprocess.h \
theatrics/ide-box-theatric.c \
theatrics/ide-box-theatric.h \
theming/ide-css-provider.c \
diff --git a/libide/buildsystem/ide-build-command.c b/libide/buildsystem/ide-build-command.c
index 3aa69e3..99f71dd 100644
--- a/libide/buildsystem/ide-build-command.c
+++ b/libide/buildsystem/ide-build-command.c
@@ -24,7 +24,8 @@
#include "buildsystem/ide-build-result.h"
#include "buildsystem/ide-environment.h"
#include "runtimes/ide-runtime.h"
-#include "workers/ide-subprocess-launcher.h"
+#include "subprocess/ide-subprocess.h"
+#include "subprocess/ide-subprocess-launcher.h"
typedef struct
{
@@ -46,17 +47,17 @@ ide_build_command_wait_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
- GSubprocess *subprocess = (GSubprocess *)object;
+ IdeSubprocess *subprocess = (IdeSubprocess *)object;
g_autoptr(GTask) task = user_data;
GError *error = NULL;
IDE_ENTRY;
- g_assert (G_IS_SUBPROCESS (subprocess));
+ g_assert (IDE_IS_SUBPROCESS (subprocess));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
- if (!g_subprocess_wait_finish (subprocess, result, &error))
+ if (!ide_subprocess_wait_finish (subprocess, result, &error))
{
g_task_return_error (task, error);
IDE_EXIT;
@@ -121,7 +122,7 @@ ide_build_command_real_run (IdeBuildCommand *self,
{
IdeBuildCommandPrivate *priv = ide_build_command_get_instance_private (self);
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
- g_autoptr(GSubprocess) subprocess = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
gboolean ret;
IDE_ENTRY;
@@ -147,7 +148,7 @@ ide_build_command_real_run (IdeBuildCommand *self,
ide_build_result_log_subprocess (build_result, subprocess);
- ret = g_subprocess_wait (subprocess, cancellable, error);
+ ret = ide_subprocess_wait (subprocess, cancellable, error);
IDE_RETURN (ret);
}
@@ -163,7 +164,7 @@ ide_build_command_real_run_async (IdeBuildCommand *self,
{
IdeBuildCommandPrivate *priv = ide_build_command_get_instance_private (self);
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
- g_autoptr(GSubprocess) subprocess = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
g_autoptr(GTask) task = NULL;
GError *error = NULL;
@@ -201,10 +202,10 @@ ide_build_command_real_run_async (IdeBuildCommand *self,
ide_build_result_log_subprocess (build_result, subprocess);
- g_subprocess_wait_async (subprocess,
- cancellable,
- ide_build_command_wait_cb,
- g_steal_pointer (&task));
+ ide_subprocess_wait_async (subprocess,
+ cancellable,
+ ide_build_command_wait_cb,
+ g_steal_pointer (&task));
IDE_EXIT;
}
diff --git a/libide/buildsystem/ide-build-result.c b/libide/buildsystem/ide-build-result.c
index 6e7045e..f37139e 100644
--- a/libide/buildsystem/ide-build-result.c
+++ b/libide/buildsystem/ide-build-result.c
@@ -29,6 +29,7 @@
#include "buildsystem/ide-build-result-addin.h"
#include "diagnostics/ide-source-location.h"
#include "files/ide-file.h"
+#include "subprocess/ide-subprocess.h"
#define POINTER_MARK(p) GSIZE_TO_POINTER(GPOINTER_TO_SIZE(p)|1)
#define POINTER_UNMARK(p) GSIZE_TO_POINTER(GPOINTER_TO_SIZE(p)&~(gsize)1)
@@ -351,27 +352,27 @@ ide_build_result_tail_into (IdeBuildResult *self,
void
ide_build_result_log_subprocess (IdeBuildResult *self,
- GSubprocess *subprocess)
+ IdeSubprocess *subprocess)
{
IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
GInputStream *stdout_stream;
GInputStream *stderr_stream;
g_return_if_fail (IDE_IS_BUILD_RESULT (self));
- g_return_if_fail (G_IS_SUBPROCESS (subprocess));
+ g_return_if_fail (IDE_IS_SUBPROCESS (subprocess));
/* ensure lazily created streams are available */
(void)ide_build_result_get_stderr_stream (self);
(void)ide_build_result_get_stdout_stream (self);
- stderr_stream = g_subprocess_get_stderr_pipe (subprocess);
+ stderr_stream = ide_subprocess_get_stderr_pipe (subprocess);
if (stderr_stream)
ide_build_result_tail_into (self,
IDE_BUILD_RESULT_LOG_STDERR,
stderr_stream,
priv->stderr_writer);
- stdout_stream = g_subprocess_get_stdout_pipe (subprocess);
+ stdout_stream = ide_subprocess_get_stdout_pipe (subprocess);
if (stdout_stream)
ide_build_result_tail_into (self,
IDE_BUILD_RESULT_LOG_STDOUT,
diff --git a/libide/buildsystem/ide-build-result.h b/libide/buildsystem/ide-build-result.h
index 26b802f..6df1e7e 100644
--- a/libide/buildsystem/ide-build-result.h
+++ b/libide/buildsystem/ide-build-result.h
@@ -60,7 +60,7 @@ struct _IdeBuildResultClass
GInputStream *ide_build_result_get_stdout_stream (IdeBuildResult *result);
GInputStream *ide_build_result_get_stderr_stream (IdeBuildResult *result);
void ide_build_result_log_subprocess (IdeBuildResult *result,
- GSubprocess *subprocess);
+ IdeSubprocess *subprocess);
GTimeSpan ide_build_result_get_running_time (IdeBuildResult *self);
gboolean ide_build_result_get_running (IdeBuildResult *self);
void ide_build_result_set_running (IdeBuildResult *self,
diff --git a/libide/ide-types.h b/libide/ide-types.h
index c5afe50..b4aee57 100644
--- a/libide/ide-types.h
+++ b/libide/ide-types.h
@@ -122,6 +122,9 @@ typedef struct _IdeSourceSnippets IdeSourceSnippets;
typedef struct _IdeSourceSnippetsManager IdeSourceSnippetsManager;
+typedef struct _IdeSubprocess IdeSubprocess;
+typedef struct _IdeSubprocessLauncher IdeSubprocessLauncher;
+
typedef struct _IdeSymbol IdeSymbol;
typedef struct _IdeSymbolResolver IdeSymbolResolver;
diff --git a/libide/ide.h b/libide/ide.h
index 20258c0..1b93c8e 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -115,6 +115,8 @@ G_BEGIN_DECLS
#include "sourceview/ide-source-map.h"
#include "sourceview/ide-source-style-scheme.h"
#include "sourceview/ide-source-view.h"
+#include "subprocess/ide-subprocess.h"
+#include "subprocess/ide-subprocess-launcher.h"
#include "symbols/ide-symbol-resolver.h"
#include "symbols/ide-symbol.h"
#include "symbols/ide-tags-builder.h"
@@ -148,7 +150,6 @@ G_BEGIN_DECLS
#include "workbench/ide-workbench-addin.h"
#include "workbench/ide-workbench-header-bar.h"
#include "workbench/ide-workbench.h"
-#include "workers/ide-subprocess-launcher.h"
#undef IDE_INSIDE
diff --git a/libide/runner/ide-runner.c b/libide/runner/ide-runner.c
index 18bf152..f1eeef4 100644
--- a/libide/runner/ide-runner.c
+++ b/libide/runner/ide-runner.c
@@ -27,7 +27,8 @@
#include "runner/ide-runner.h"
#include "runner/ide-runner-addin.h"
-#include "workers/ide-subprocess-launcher.h"
+#include "subprocess/ide-subprocess.h"
+#include "subprocess/ide-subprocess-launcher.h"
typedef struct
{
@@ -96,14 +97,16 @@ ide_runner_run_wait_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
- GSubprocess *subprocess = (GSubprocess *)object;
+ IdeSubprocess *subprocess = (IdeSubprocess *)object;
g_autoptr(GTask) task = user_data;
GError *error = NULL;
IdeRunner *self;
IDE_ENTRY;
- g_assert (G_IS_SUBPROCESS (subprocess));
+ g_assert (IDE_IS_SUBPROCESS (subprocess));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
@@ -111,17 +114,17 @@ ide_runner_run_wait_cb (GObject *object,
g_signal_emit (self, signals [EXITED], 0);
- if (!g_subprocess_wait_finish (subprocess, result, &error))
+ if (!ide_subprocess_wait_finish (subprocess, result, &error))
{
g_task_return_error (task, error);
IDE_EXIT;
}
- if (g_subprocess_get_if_exited (subprocess))
+ if (ide_subprocess_get_if_exited (subprocess))
{
gint exit_code;
- exit_code = g_subprocess_get_exit_status (subprocess);
+ exit_code = ide_subprocess_get_exit_status (subprocess);
if (exit_code == EXIT_SUCCESS)
{
@@ -148,7 +151,7 @@ ide_runner_real_run_async (IdeRunner *self,
IdeRunnerPrivate *priv = ide_runner_get_instance_private (self);
g_autoptr(GTask) task = NULL;
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
- g_autoptr(GSubprocess) subprocess = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
const gchar *identifier;
GError *error = NULL;
@@ -169,7 +172,7 @@ ide_runner_real_run_async (IdeRunner *self,
subprocess = ide_subprocess_launcher_spawn_sync (launcher, cancellable, &error);
- g_assert (subprocess == NULL || G_IS_SUBPROCESS (subprocess));
+ g_assert (subprocess == NULL || IDE_IS_SUBPROCESS (subprocess));
if (subprocess == NULL)
{
@@ -177,14 +180,14 @@ ide_runner_real_run_async (IdeRunner *self,
IDE_GOTO (failure);
}
- identifier = g_subprocess_get_identifier (subprocess);
+ identifier = ide_subprocess_get_identifier (subprocess);
g_signal_emit (self, signals [SPAWNED], 0, identifier);
- g_subprocess_wait_async (subprocess,
- cancellable,
- ide_runner_run_wait_cb,
- g_steal_pointer (&task));
+ ide_subprocess_wait_async (subprocess,
+ cancellable,
+ ide_runner_run_wait_cb,
+ g_steal_pointer (&task));
failure:
IDE_EXIT;
diff --git a/libide/runtimes/ide-runtime.h b/libide/runtimes/ide-runtime.h
index cccdc3a..28f3931 100644
--- a/libide/runtimes/ide-runtime.h
+++ b/libide/runtimes/ide-runtime.h
@@ -25,7 +25,7 @@
#include "buildsystem/ide-build-target.h"
#include "runner/ide-runner.h"
-#include "workers/ide-subprocess-launcher.h"
+#include "subprocess/ide-subprocess-launcher.h"
G_BEGIN_DECLS
diff --git a/libide/subprocess/ide-breakout-subprocess-private.h
b/libide/subprocess/ide-breakout-subprocess-private.h
new file mode 100644
index 0000000..ff4e761
--- /dev/null
+++ b/libide/subprocess/ide-breakout-subprocess-private.h
@@ -0,0 +1,35 @@
+/* ide-breakout-subprocess-private.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BREAKOUT_SUBPROCESS_PRIVATE_H
+#define IDE_BREAKOUT_SUBPROCESS_PRIVATE_H
+
+#include "subprocess/ide-breakout-subprocess.h"
+
+G_BEGIN_DECLS
+
+IdeSubprocess *_ide_breakout_subprocess_new (const gchar *cwd,
+ const gchar * const *argv,
+ const gchar * const *env,
+ GSubprocessFlags flags,
+ GCancellable *cancellable,
+ GError **error) G_GNUC_INTERNAL;
+
+G_END_DECLS
+
+#endif /* IDE_BREAKOUT_SUBPROCESS_PRIVATE_H */
diff --git a/libide/subprocess/ide-breakout-subprocess.c b/libide/subprocess/ide-breakout-subprocess.c
new file mode 100644
index 0000000..a75dd78
--- /dev/null
+++ b/libide/subprocess/ide-breakout-subprocess.c
@@ -0,0 +1,732 @@
+/* ide-breakout-subprocess.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-breakout-subprocess"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "ide-breakout-subprocess.h"
+
+#ifndef FLATPAK_HOST_COMMAND_FLAGS_CLEAR_ENV
+# define FLATPAK_HOST_COMMAND_FLAGS_CLEAR_ENV (1 << 0)
+#endif
+
+struct _IdeBreakoutSubprocess
+{
+ GObject parent_instance;
+
+ GDBusProxy *proxy;
+
+ GPid process;
+ gint status;
+
+ GSubprocessFlags flags;
+
+ gchar **argv;
+ gchar **env;
+ gchar *cwd;
+
+ gchar *identifier;
+
+ GOutputStream *stdin_pipe;
+ GInputStream *stdout_pipe;
+ GInputStream *stderr_pipe;
+
+ GList *waiting;
+
+ guint sigint_id;
+ guint sigterm_id;
+};
+
+enum {
+ PROP_0,
+ PROP_ARGV,
+ PROP_CWD,
+ PROP_ENV,
+ PROP_FLAGS,
+ N_PROPS
+};
+
+static const gchar *
+ide_breakout_subprocess_get_identifier (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ return self->identifier;
+}
+
+static GInputStream *
+ide_breakout_subprocess_get_stdout_pipe (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ return self->stdout_pipe;
+}
+
+static GInputStream *
+ide_breakout_subprocess_get_stderr_pipe (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ return self->stderr_pipe;
+}
+
+static GOutputStream *
+ide_breakout_subprocess_get_stdin_pipe (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ return self->stdin_pipe;
+}
+
+static gboolean
+ide_breakout_subprocess_wait (IdeSubprocess *subprocess,
+ GCancellable *cancellable,
+ GError **error)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ /* TODO */ g_assert_not_reached ();
+}
+
+static void
+ide_breakout_subprocess_wait_async (IdeSubprocess *subprocess,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_breakout_subprocess_wait_async);
+
+ if (ide_subprocess_get_if_exited (subprocess))
+ {
+ g_task_return_boolean (task, TRUE);
+ return;
+ }
+
+ self->waiting = g_list_append (self->waiting, g_steal_pointer (&task));
+}
+
+static gboolean
+ide_breakout_subprocess_wait_finish (IdeSubprocess *subprocess,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (subprocess));
+ g_assert (G_IS_TASK (result));
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static gboolean
+ide_breakout_subprocess_get_successful (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ return WIFEXITED (self->status) && WEXITSTATUS (self->status) == 0;
+}
+
+static gboolean
+ide_breakout_subprocess_get_if_exited (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ return self->process == 0;
+}
+
+static gint
+ide_breakout_subprocess_get_exit_status (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+ g_assert (self->process == 0);
+
+ if (!WIFEXITED (self->status))
+ return 1;
+
+ return WEXITSTATUS (self->status);
+}
+
+static gboolean
+ide_breakout_subprocess_get_if_signaled (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+ g_assert (self->process == 0);
+
+ return WIFSIGNALED (self->status);
+}
+
+static gint
+ide_breakout_subprocess_get_term_sig (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ return WTERMSIG (self->status);
+}
+
+static gint
+ide_breakout_subprocess_get_status (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+ g_assert (self->process == 0);
+
+ return self->status;
+}
+
+static void
+ide_breakout_subprocess_send_signal (IdeSubprocess *subprocess,
+ gint signal_num)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ /* TODO: */ g_assert_not_reached ();
+}
+
+static void
+ide_breakout_subprocess_force_exit (IdeSubprocess *subprocess)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+
+ ide_breakout_subprocess_send_signal (subprocess, SIGKILL);
+}
+
+static void
+ide_breakout_subprocess_communicate (IdeSubprocess *subprocess,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ /* TODO */ g_assert_not_reached ();
+}
+
+static void
+ide_breakout_subprocess_communicate_async (IdeSubprocess *subprocess,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ /* TODO */ g_assert_not_reached ();
+}
+
+static gboolean
+ide_breakout_subprocess_communicate_finish (IdeSubprocess *subprocess,
+ GAsyncResult *result,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)subprocess;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ /* TODO */ g_assert_not_reached ();
+}
+
+static void
+subprocess_iface_init (IdeSubprocessInterface *iface)
+{
+ iface->get_identifier = ide_breakout_subprocess_get_identifier;
+ iface->get_stdout_pipe = ide_breakout_subprocess_get_stdout_pipe;
+ iface->get_stderr_pipe = ide_breakout_subprocess_get_stderr_pipe;
+ iface->get_stdin_pipe = ide_breakout_subprocess_get_stdin_pipe;
+ iface->wait = ide_breakout_subprocess_wait;
+ iface->wait_async = ide_breakout_subprocess_wait_async;
+ iface->wait_finish = ide_breakout_subprocess_wait_finish;
+ iface->get_successful = ide_breakout_subprocess_get_successful;
+ iface->get_if_exited = ide_breakout_subprocess_get_if_exited;
+ iface->get_exit_status = ide_breakout_subprocess_get_exit_status;
+ iface->get_if_signaled = ide_breakout_subprocess_get_if_signaled;
+ iface->get_term_sig = ide_breakout_subprocess_get_term_sig;
+ iface->get_status = ide_breakout_subprocess_get_status;
+ iface->send_signal = ide_breakout_subprocess_send_signal;
+ iface->force_exit = ide_breakout_subprocess_force_exit;
+ iface->communicate = ide_breakout_subprocess_communicate;
+ iface->communicate_async = ide_breakout_subprocess_communicate_async;
+ iface->communicate_finish = ide_breakout_subprocess_communicate_finish;
+}
+
+static void
+maybe_create_input_stream (GInputStream **ret,
+ gint *fdptr)
+{
+ g_assert (ret != NULL);
+ g_assert (fdptr != NULL);
+
+ g_clear_object (ret);
+
+ if (*fdptr != -1)
+ {
+ *ret = g_unix_input_stream_new (*fdptr, TRUE);
+ *fdptr = -1;
+ }
+}
+
+static void
+maybe_create_output_stream (GOutputStream **ret,
+ gint *fdptr)
+{
+ g_assert (ret != NULL);
+ g_assert (fdptr != NULL);
+
+ g_clear_object (ret);
+
+ if (*fdptr != -1)
+ {
+ *ret = g_unix_output_stream_new (*fdptr, TRUE);
+ *fdptr = -1;
+ }
+}
+
+static inline void
+set_error_from_errno (GError **error)
+{
+ g_set_error (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (errno),
+ "%s",
+ g_strerror (errno));
+}
+
+static gboolean
+ide_breakout_subprocess_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)initable;
+ g_autoptr(GVariantBuilder) fd_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{uh}"));
+ g_autoptr(GVariantBuilder) env_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{ss}"));
+ g_autoptr(GUnixFDList) fd_list = g_unix_fd_list_new ();
+ g_autoptr(GVariant) reply = NULL;
+ GDBusConnection *bus;
+ guint32 client_pid = 0;
+ guint subscription;
+ gint stdout_pair[2] = { -1, -1 };
+ gint stderr_pair[2] = { -1, -1 };
+ gint stdin_pair[2] = { -1, -1 };
+ gint stdin_handle;
+ gint stdout_handle;
+ gint stderr_handle;
+ gboolean ret = FALSE;
+
+ g_assert (IDE_IS_BREAKOUT_SUBPROCESS (self));
+ g_assert (self->proxy == NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+
+ bus = g_bus_get (G_BUS_TYPE_SESSION, cancellable, error);
+ if (bus == NULL)
+ return FALSE;
+
+
+ /*
+ * Handle STDIN for the process.
+ *
+ * Make sure we handle inherit STDIN, a new pipe (so that the application can
+ * get the stdin stream), or simply redirect to /dev/null.
+ */
+ if (self->flags & G_SUBPROCESS_FLAGS_STDIN_INHERIT)
+ {
+ stdin_pair[0] = STDIN_FILENO;
+ }
+ else if (self->flags & G_SUBPROCESS_FLAGS_STDIN_PIPE)
+ {
+ if (pipe2 (stdin_pair, O_CLOEXEC) != 0)
+ {
+ set_error_from_errno (error);
+ goto cleanup_fds;
+ }
+ }
+ else
+ {
+ stderr_pair[0] = open ("/dev/null", O_RDWR, 0);
+
+ if (stderr_pair[0] == -1)
+ goto cleanup_fds;
+ }
+
+ g_assert (stdin_pair[0] != -1);
+
+ stdin_pair = g_unix_fd_list_append (fd_list, stdin_pair[0], error);
+ if (stdin_pair == -1)
+ goto cleanup_fds;
+
+
+ /*
+ * Setup STDOUT for the process.
+ *
+ * Make sure we redirect STDOUT to our stdout, unless a pipe was requested
+ * for the application to read. However, if silence was requested, redirect
+ * to /dev/null.
+ */
+ if (self->flags & G_SUBPROCESS_FLAGS_STDOUT_SILENCE)
+ {
+ stdout_pair[1] = open ("/dev/null", O_RDWR, 0);
+
+ if (stdout_pair[1] == -1)
+ goto cleanup_fds;
+ }
+ else if (self->flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE)
+ {
+ if (pipe2 (stdout_pair, O_CLOEXEC) != 0)
+ {
+ set_error_from_errno (error);
+ goto cleanup_fds;
+ }
+ }
+ else
+ {
+ stdout_pair[1] = STDOUT_FILENO;
+ }
+
+ g_assert (stdout_pair[1] != -1);
+
+ stdout_handle = g_unix_fd_list_append (fd_list, stdout_pair[1], error);
+ if (stdout_handle == -1)
+ goto cleanup_fds;
+
+
+ /*
+ * Handle STDERR for the process.
+ *
+ * If silence is requested, we simply redirect to /dev/null. If the
+ * application requested to read from the subprocesses stderr, then we need
+ * to create a pipe. Otherwose, merge stderr into our own stderr.
+ */
+ if (self->flags & G_SUBPROCESS_FLAGS_STDERR_SILENCE)
+ {
+ stderr_pair[1] = open ("/dev/null", O_RDWR, 0);
+
+ if (stderr_pair[1] == -1)
+ goto cleanup_fds;
+ }
+ else if (self->flags & G_SUBPROCESS_FLAGS_STDERR_PIPE)
+ {
+ if (pipe2 (stderr_pair, O_CLOEXEC) != 0)
+ {
+ set_error_from_errno (error);
+ goto cleanup_fds;
+ }
+ }
+ else
+ {
+ stderr_pair[1] = STDERR_FILENO;
+ }
+
+ g_assert (stderr_pair[1] != -1);
+
+ stderr_handle = g_unix_fd_list_append (fd_list, stderr_pair[1], error);
+ if (stderr_handle == -1)
+ goto cleanup_fds;
+
+
+ /*
+ * Build our FDs for the message.
+ */
+ g_variant_builder_add (fd_builder, "{uh}", 0, stdin_handle);
+ g_variant_builder_add (fd_builder, "{uh}", 1, stdout_handle);
+ g_variant_builder_add (fd_builder, "{uh}", 2, stderr_handle);
+
+
+ /*
+ * Build streams for our application to use.
+ */
+ maybe_create_output_stream (&self->stdin_pipe, &stdin_pipe[1]);
+ maybe_create_input_stream (&self->stdout_pipe, &stdout_pipe[0]);
+ maybe_create_input_stream (&self->stderr_pipe, &stderr_pipe[0]);
+
+
+ /*
+ * Build our environment variables message.
+ */
+ if (self->env != NULL)
+ {
+ for (guint i = 0; self->env[i]; i++)
+ {
+ const gchar *pair = self->env[i];
+ const gchar *eq = strchr (pair, '=');
+ const gchar *val = eq ? eq + 1 : "";
+ g_autofree gchar *key = eq ? g_strndup (pair, eq - pair) : g_strdup (pair);
+
+ g_variant_builder_add (env_builder, "{ss}", key, val);
+ }
+ }
+
+
+ /*
+ * Register signal handlers for SIGTERM/SIGINT so that we can terminate
+ * the host process with us (which won't be guaranteed since its outside
+ * our cgroup, nor can we use a process group leader).
+ */
+ self->sigterm_id = g_unix_signal_add_full (SIGTERM, sigterm_handler, g_object_ref (self), g_object_unref);
+ self->sigint_id = g_unix_signal_add_full (SIGINT, sigint_handler, g_object_ref (self), g_object_unref);
+
+
+ /*
+ * Connect to the HostCommandExited signal so that we can make progress
+ * on all tasks waiting on ide_subprocess_wait() and it's async variants.
+ * We need to do this before spawning the process to avoid the race.
+ */
+ subscription = g_dbus_connection_signal_subscribe (connection,
+ NULL,
+ "org.freedesktop.flatpak.Development",
+ "HostCommandExited",
+ "/org/freedesktop/flatpak/Development",
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ host_command_exited_cb,
+ g_object_ref (self),
+ g_object_unref);
+
+
+ /*
+ * Now call the HostCommand service to execute the process within the host
+ * system. We need to ensure our fd_list is sent across for redirecting
+ * various standard streams.
+ */
+ reply = g_dbus_connection_call_with_unix_fd_list_sync (proxy,
+ "HostCommand",
+ g_variant_new ("(^ay^aay@a{uh}@a{ss}u)",
+ self->cwd ?: g_get_home_dir (),
+ self->argv,
+ g_variant_builder_end
(g_steal_pointer (&fd_builder)),
+ g_variant_builder_end
(g_steal_pointer (&env_builder)),
+
FLATPAK_HOST_COMMAND_FLAGS_CLEAR_ENV),
+ G_DBUS_CALL_FLAGS_NONE,
+ 1000,
+ cancellable,
+ error);
+ if (reply == NULL)
+ return FALSE;
+
+ if (!g_variant_is_of_type (reply, G_VARIANT_TYPE ("u")))
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Received invalid reply from org.freedesktop.flatpak.Development service");
+ return FALSE;
+ }
+
+ g_variant_get (reply, "(u)", &client_pid);
+
+ self->process = (GPid)client_pid;
+
+ ret = TRUE;
+
+cleanup_fds:
+
+ /* Close lingering stdout fds */
+ if (stdout_pair[0] != -1)
+ close (stdout_pair[0]);
+ if (stdout_pair[1] != -1)
+ close (stdout_pair[1]);
+
+ /* Close lingering stderr fds */
+ if (stderr_pair[0] != -1)
+ close (stderr_pair[0]);
+ if (stderr_pair[1] != -1)
+ close (stderr_pair[1]);
+
+ /* Close lingering stdin fds */
+ if (stdin_pair[0] != -1)
+ close (stdin_pair[0]);
+ if (stdin_pair[1] != -1)
+ close (stdin_pair[1]);
+
+ return ret;
+}
+
+static void
+initiable_iface_init (GInitableIface *iface)
+{
+ iface->init = ide_breakout_subprocess_initable_init;
+}
+
+G_DEFINE_TYPE_EXTENDED (IdeBreakoutSubprocess, ide_breakout_subprocess, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initiable_iface_init)
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_SUBPROCESS, subprocess_iface_init))
+
+static void
+ide_breakout_subprocess_finalize (GObject *object)
+{
+ IdeBreakoutSubprocess *self = (IdeBreakoutSubprocess *)object;
+
+ g_assert (self->waiting == NULL);
+ g_assert (self->sigint_id == 0);
+ g_assert (self->sigterm_id == 0);
+
+ g_clear_pointer (&self->identifier, g_free);
+ g_clear_pointer (&self->cwd, g_free);
+ g_clear_pointer (&self->argv, g_strfreev);
+ g_clear_pointer (&self->env, g_strfreev);
+
+ g_clear_object (&self->proxy);
+ g_clear_object (&self->stdin_pipe);
+ g_clear_object (&self->stdout_pipe);
+ g_clear_object (&self->stderr_pipe);
+
+ G_OBJECT_CLASS (ide_breakout_subprocess_parent_class)->finalize (object);
+}
+
+static void
+ide_breakout_subprocess_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeBreakoutSubprocess *self = IDE_BREAKOUT_SUBPROCESS (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_breakout_subprocess_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeBreakoutSubprocess *self = IDE_BREAKOUT_SUBPROCESS (object);
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_breakout_subprocess_class_init (IdeBreakoutSubprocessClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_breakout_subprocess_finalize;
+ object_class->get_property = ide_breakout_subprocess_get_property;
+ object_class->set_property = ide_breakout_subprocess_set_property;
+
+ properties [PROP_CWD] =
+ g_param_spec_string ("cwd",
+ "Current Working Directory",
+ "The working directory for spawning the process",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ARGV] =
+ g_param_spec_boxed ("argv",
+ "Argv",
+ "The arguments for the process, including argv0",
+ G_TYPE_STRV,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ENV] =
+ g_param_spec_boxed ("env",
+ "Environment",
+ "The environment variables for the process",
+ G_TYPE_STRV,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_FLAGS] =
+ g_param_spec_flags ("flags",
+ "Flags",
+ "The subprocess flags to use when spawning",
+ G_TYPE_SUBPROCESS_FLAGS,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_breakout_subprocess_init (IdeBreakoutSubprocess *self)
+{
+}
+
+IdeSubprocess *
+_ide_breakout_subprocess_new (const gchar *cwd,
+ const gchar * const *argv,
+ const gchar * const *env,
+ GSubprocessFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(IdeBreakoutSubprocess) ret = NULL;
+
+ g_return_val_if_fail (argv != NULL, NULL);
+ g_return_val_if_fail (argv[0] != NULL, NULL);
+
+ ret = g_object_new (IDE_TYPE_BREAKOUT_SUBPROCESS,
+ "cwd", cwd,
+ "argv", argv,
+ "env", env,
+ "flags", flags,
+ NULL);
+
+ if (!g_initable_init (G_INITABLE (ret), cancellable, error))
+ return NULL;
+
+ return g_steam_pointer (&ret);
+}
diff --git a/libide/subprocess/ide-breakout-subprocess.h b/libide/subprocess/ide-breakout-subprocess.h
new file mode 100644
index 0000000..3272957
--- /dev/null
+++ b/libide/subprocess/ide-breakout-subprocess.h
@@ -0,0 +1,32 @@
+/* ide-breakout-subprocess.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BREAKOUT_SUBPROCESS_H
+#define IDE_BREAKOUT_SUBPROCESS_H
+
+#include "subprocess/ide-subprocess.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BREAKOUT_SUBPROCESS (ide_breakout_subprocess_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeBreakoutSubprocess, ide_breakout_subprocess, IDE, BREAKOUT_SUBPROCESS, GObject)
+
+G_END_DECLS
+
+#endif /* IDE_BREAKOUT_SUBPROCESS_H */
diff --git a/libide/subprocess/ide-simple-subprocess.c b/libide/subprocess/ide-simple-subprocess.c
new file mode 100644
index 0000000..98bc99d
--- /dev/null
+++ b/libide/subprocess/ide-simple-subprocess.c
@@ -0,0 +1,310 @@
+/* ide-simple-subprocess.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-simple-subprocess"
+
+#include "ide-simple-subprocess.h"
+
+struct _IdeSimpleSubprocess
+{
+ GObject parent_instance;
+ GSubprocess *subprocess;
+};
+
+static void subprocess_iface_init (IdeSubprocessInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeSimpleSubprocess, ide_simple_subprocess, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_SUBPROCESS, subprocess_iface_init))
+
+static void
+ide_simple_subprocess_finalize (GObject *object)
+{
+ IdeSimpleSubprocess *self = (IdeSimpleSubprocess *)object;
+
+ g_clear_object (&self->subprocess);
+
+ G_OBJECT_CLASS (ide_simple_subprocess_parent_class)->finalize (object);
+}
+
+static void
+ide_simple_subprocess_class_init (IdeSimpleSubprocessClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_simple_subprocess_finalize;
+}
+
+static void
+ide_simple_subprocess_init (IdeSimpleSubprocess *self)
+{
+}
+
+#define WRAP_INTERFACE_METHOD(name, ...) \
+ g_subprocess_##name(IDE_SIMPLE_SUBPROCESS(subprocess)->subprocess, ## __VA_ARGS__)
+
+static const gchar *
+ide_simple_subprocess_get_identifier (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_identifier);
+}
+
+static GInputStream *
+ide_simple_subprocess_get_stdout_pipe (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_stdout_pipe);
+}
+
+static GInputStream *
+ide_simple_subprocess_get_stderr_pipe (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_stderr_pipe);
+}
+
+static GOutputStream *
+ide_simple_subprocess_get_stdin_pipe (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_stdin_pipe);
+}
+
+static gboolean
+ide_simple_subprocess_wait (IdeSubprocess *subprocess,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return WRAP_INTERFACE_METHOD (wait, cancellable, error);
+}
+
+static void
+ide_simple_subprocess_wait_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSubprocess *subprocess = (GSubprocess *)object;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ if (!g_subprocess_wait_finish (subprocess, result, &error))
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_simple_subprocess_wait_async (IdeSubprocess *subprocess,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeSimpleSubprocess *self = (IdeSimpleSubprocess *)subprocess;
+ GTask *task = g_task_new (self, cancellable, callback, user_data);
+ g_subprocess_wait_async (self->subprocess, cancellable, ide_simple_subprocess_wait_cb, task);
+}
+
+static gboolean
+ide_simple_subprocess_wait_finish (IdeSubprocess *subprocess,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static gboolean
+ide_simple_subprocess_get_successful (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_successful);
+}
+
+static gboolean
+ide_simple_subprocess_get_if_exited (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_if_exited);
+}
+
+static gint
+ide_simple_subprocess_get_exit_status (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_exit_status);
+}
+
+static gboolean
+ide_simple_subprocess_get_if_signaled (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_if_signaled);
+}
+
+static gint
+ide_simple_subprocess_get_term_sig (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_term_sig);
+}
+
+static gint
+ide_simple_subprocess_get_status (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (get_status);
+}
+
+static void
+ide_simple_subprocess_send_signal (IdeSubprocess *subprocess,
+ gint signal_num)
+{
+ return WRAP_INTERFACE_METHOD (send_signal, signal_num);
+}
+
+static void
+ide_simple_subprocess_force_exit (IdeSubprocess *subprocess)
+{
+ return WRAP_INTERFACE_METHOD (force_exit);
+}
+
+static gboolean
+ide_simple_subprocess_communicate (IdeSubprocess *subprocess,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error)
+{
+ return WRAP_INTERFACE_METHOD (communicate, stdin_buf, cancellable, stdout_buf, stderr_buf, error);
+}
+
+static gboolean
+ide_simple_subprocess_communicate_utf8 (IdeSubprocess *subprocess,
+ const gchar *stdin_buf,
+ GCancellable *cancellable,
+ gchar **stdout_buf,
+ gchar **stderr_buf,
+ GError **error)
+{
+ return WRAP_INTERFACE_METHOD (communicate_utf8, stdin_buf, cancellable, stdout_buf, stderr_buf, error);
+}
+
+static void
+free_object_pair (gpointer data)
+{
+ gpointer *pair = data;
+
+ g_clear_object (&pair[0]);
+ g_clear_object (&pair[1]);
+ g_free (pair);
+}
+
+static void
+ide_simple_subprocess_communicate_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSubprocess *subprocess = (GSubprocess *)object;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GBytes) stdout_buf = NULL;
+ g_autoptr(GBytes) stderr_buf = NULL;
+ gpointer *data;
+
+ if (!g_subprocess_communicate_finish (subprocess, result, &stdout_buf, &stderr_buf, &error))
+ g_task_return_error (task, g_steal_pointer (&error));
+
+ data = g_new0 (gpointer, 2);
+ data[0] = g_steal_pointer (&stdout_buf);
+ data[1] = g_steal_pointer (&stderr_buf);
+
+ g_task_return_pointer (task, data, free_object_pair);
+}
+
+static void
+ide_simple_subprocess_communicate_async (IdeSubprocess *subprocess,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeSimpleSubprocess *self = (IdeSimpleSubprocess *)subprocess;
+ GTask *task = g_task_new (self, cancellable, callback, user_data);
+ g_subprocess_communicate_async (self->subprocess, stdin_buf, cancellable,
ide_simple_subprocess_communicate_cb, task);
+}
+
+static gboolean
+ide_simple_subprocess_communicate_finish (IdeSubprocess *subprocess,
+ GAsyncResult *result,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error)
+{
+ gpointer *pair;
+
+ pair = g_task_propagate_pointer (G_TASK (result), error);
+
+ if (pair != NULL)
+ {
+ if (stdout_buf != NULL)
+ *stdout_buf = g_steal_pointer (&pair[0]);
+
+ if (stderr_buf != NULL)
+ *stderr_buf = g_steal_pointer (&pair[1]);
+
+ free_object_pair (pair);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+subprocess_iface_init (IdeSubprocessInterface *iface)
+{
+ iface->get_identifier = ide_simple_subprocess_get_identifier;
+ iface->get_stdout_pipe = ide_simple_subprocess_get_stdout_pipe;
+ iface->get_stderr_pipe = ide_simple_subprocess_get_stderr_pipe;
+ iface->get_stdin_pipe = ide_simple_subprocess_get_stdin_pipe;
+ iface->wait = ide_simple_subprocess_wait;
+ iface->wait_async = ide_simple_subprocess_wait_async;
+ iface->wait_finish = ide_simple_subprocess_wait_finish;
+ iface->get_successful = ide_simple_subprocess_get_successful;
+ iface->get_if_exited = ide_simple_subprocess_get_if_exited;
+ iface->get_exit_status = ide_simple_subprocess_get_exit_status;
+ iface->get_if_signaled = ide_simple_subprocess_get_if_signaled;
+ iface->get_term_sig = ide_simple_subprocess_get_term_sig;
+ iface->get_status = ide_simple_subprocess_get_status;
+ iface->send_signal = ide_simple_subprocess_send_signal;
+ iface->force_exit = ide_simple_subprocess_force_exit;
+ iface->communicate = ide_simple_subprocess_communicate;
+ iface->communicate_utf8 = ide_simple_subprocess_communicate_utf8;
+ iface->communicate_async = ide_simple_subprocess_communicate_async;
+ iface->communicate_finish = ide_simple_subprocess_communicate_finish;
+}
+
+/**
+ * ide_simple_subprocess_new:
+ *
+ * Creates a new #IdeSimpleSubprocess wrapping the #GSubprocess.
+ *
+ * Returns: (transfer full): A new #IdeSubprocess
+ */
+IdeSubprocess *
+ide_simple_subprocess_new (GSubprocess *subprocess)
+{
+ IdeSimpleSubprocess *ret;
+
+ g_return_val_if_fail (G_IS_SUBPROCESS (subprocess), NULL);
+
+ ret = g_object_new (IDE_TYPE_SIMPLE_SUBPROCESS, NULL);
+ ret->subprocess = g_object_ref (subprocess);
+
+ return IDE_SUBPROCESS (ret);
+}
diff --git a/libide/subprocess/ide-simple-subprocess.h b/libide/subprocess/ide-simple-subprocess.h
new file mode 100644
index 0000000..dff3e3a
--- /dev/null
+++ b/libide/subprocess/ide-simple-subprocess.h
@@ -0,0 +1,34 @@
+/* ide-simple-subprocess.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SIMPLE_SUBPROCESS_H
+#define IDE_SIMPLE_SUBPROCESS_H
+
+#include "ide-subprocess.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SIMPLE_SUBPROCESS (ide_simple_subprocess_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeSimpleSubprocess, ide_simple_subprocess, IDE, SIMPLE_SUBPROCESS, GObject)
+
+IdeSubprocess *ide_simple_subprocess_new (GSubprocess *subprocess);
+
+G_END_DECLS
+
+#endif /* IDE_SIMPLE_SUBPROCESS_H */
diff --git a/libide/workers/ide-subprocess-launcher.c b/libide/subprocess/ide-subprocess-launcher.c
similarity index 93%
rename from libide/workers/ide-subprocess-launcher.c
rename to libide/subprocess/ide-subprocess-launcher.c
index 44e6563..6c3f4e7 100644
--- a/libide/workers/ide-subprocess-launcher.c
+++ b/libide/subprocess/ide-subprocess-launcher.c
@@ -28,7 +28,8 @@
#include "buildsystem/ide-environment-variable.h"
#include "buildsystem/ide-environment.h"
-#include "workers/ide-subprocess-launcher.h"
+#include "subprocess/ide-simple-subprocess.h"
+#include "subprocess/ide-subprocess-launcher.h"
typedef struct
{
@@ -104,6 +105,14 @@ ide_subprocess_launcher_new (GSubprocessFlags flags)
}
static void
+ide_subprocess_launcher_spawn_worker_flatpak (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+}
+
+static void
ide_subprocess_launcher_spawn_worker (GTask *task,
gpointer source_object,
gpointer task_data,
@@ -112,8 +121,9 @@ ide_subprocess_launcher_spawn_worker (GTask *task,
IdeSubprocessLauncher *self = source_object;
IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
g_autoptr(GSubprocessLauncher) launcher = NULL;
- GSubprocess *ret;
- GError *error = NULL;
+ g_autoptr(GSubprocess) real = NULL;
+ g_autoptr(IdeSubprocess) wrapped = NULL;
+ g_autoptr(GError) error = NULL;
g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
@@ -130,13 +140,13 @@ ide_subprocess_launcher_spawn_worker (GTask *task,
g_subprocess_launcher_set_cwd (launcher, priv->cwd);
if (priv->environ->len > 1)
g_subprocess_launcher_set_environ (launcher, (gchar **)priv->environ->pdata);
- ret = g_subprocess_launcher_spawnv (launcher,
- (const gchar * const *)priv->argv->pdata,
- &error);
+ real = g_subprocess_launcher_spawnv (launcher,
+ (const gchar * const *)priv->argv->pdata,
+ &error);
- if (ret == NULL)
+ if (real == NULL)
{
- g_task_return_error (task, error);
+ g_task_return_error (task, g_steal_pointer (&error));
return;
}
@@ -145,15 +155,17 @@ ide_subprocess_launcher_spawn_worker (GTask *task,
g_signal_connect_data (cancellable,
"cancelled",
G_CALLBACK (ide_subprocess_launcher_kill_process_group),
- g_object_ref (ret),
+ g_object_ref (real),
(GClosureNotify)g_object_unref,
0);
}
- g_task_return_pointer (task, ret, g_object_unref);
+ wrapped = ide_simple_subprocess_new (real);
+
+ g_task_return_pointer (task, g_steal_pointer (&wrapped), g_object_unref);
}
-static GSubprocess *
+static IdeSubprocess *
ide_subprocess_launcher_real_spawn_sync (IdeSubprocessLauncher *self,
GCancellable *cancellable,
GError **error)
@@ -189,7 +201,7 @@ ide_subprocess_launcher_real_spawn_async (IdeSubprocessLauncher *self,
g_task_run_in_thread (task, ide_subprocess_launcher_spawn_worker);
}
-static GSubprocess *
+static IdeSubprocess *
ide_subprocess_launcher_real_spawn_finish (IdeSubprocessLauncher *self,
GAsyncResult *result,
GError **error)
@@ -447,9 +459,9 @@ ide_subprocess_launcher_spawn_async (IdeSubprocessLauncher *self,
*
* Complete a request to asynchronously spawn a process.
*
- * Returns: (transfer full): A #GSubprocess or %NULL upon error.
+ * Returns: (transfer full): A #IdeSubprocess or %NULL upon error.
*/
-GSubprocess *
+IdeSubprocess *
ide_subprocess_launcher_spawn_finish (IdeSubprocessLauncher *self,
GAsyncResult *result,
GError **error)
@@ -465,9 +477,9 @@ ide_subprocess_launcher_spawn_finish (IdeSubprocessLauncher *self,
*
* Synchronously spawn a process using the internal state.
*
- * Returns: (transfer full): A #GSubprocess or %NULL upon error.
+ * Returns: (transfer full): A #IdeSubprocess or %NULL upon error.
*/
-GSubprocess *
+IdeSubprocess *
ide_subprocess_launcher_spawn_sync (IdeSubprocessLauncher *self,
GCancellable *cancellable,
GError **error)
diff --git a/libide/workers/ide-subprocess-launcher.h b/libide/subprocess/ide-subprocess-launcher.h
similarity index 80%
rename from libide/workers/ide-subprocess-launcher.h
rename to libide/subprocess/ide-subprocess-launcher.h
index b72813a..4577afa 100644
--- a/libide/workers/ide-subprocess-launcher.h
+++ b/libide/subprocess/ide-subprocess-launcher.h
@@ -33,16 +33,25 @@ struct _IdeSubprocessLauncherClass
{
GObjectClass parent_class;
- GSubprocess *(*spawn_sync) (IdeSubprocessLauncher *self,
- GCancellable *cancellable,
- GError **error);
- void (*spawn_async) (IdeSubprocessLauncher *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
- GSubprocess *(*spawn_finish) (IdeSubprocessLauncher *self,
- GAsyncResult *result,
- GError **error);
+ IdeSubprocess *(*spawn_sync) (IdeSubprocessLauncher *self,
+ GCancellable *cancellable,
+ GError **error);
+ void (*spawn_async) (IdeSubprocessLauncher *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ IdeSubprocess *(*spawn_finish) (IdeSubprocessLauncher *self,
+ GAsyncResult *result,
+ GError **error);
+
+ gpointer _reserved1;
+ gpointer _reserved2;
+ gpointer _reserved3;
+ gpointer _reserved4;
+ gpointer _reserved5;
+ gpointer _reserved6;
+ gpointer _reserved7;
+ gpointer _reserved8;
};
IdeSubprocessLauncher *ide_subprocess_launcher_new (GSubprocessFlags flags);
@@ -66,14 +75,14 @@ void ide_subprocess_launcher_push_args (IdeSubproces
void ide_subprocess_launcher_push_argv (IdeSubprocessLauncher *self,
const gchar *argv);
gchar *ide_subprocess_launcher_pop_argv (IdeSubprocessLauncher *self)
G_GNUC_WARN_UNUSED_RESULT;
-GSubprocess *ide_subprocess_launcher_spawn_sync (IdeSubprocessLauncher *self,
+IdeSubprocess *ide_subprocess_launcher_spawn_sync (IdeSubprocessLauncher *self,
GCancellable *cancellable,
GError **error);
void ide_subprocess_launcher_spawn_async (IdeSubprocessLauncher *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-GSubprocess *ide_subprocess_launcher_spawn_finish (IdeSubprocessLauncher *self,
+IdeSubprocess *ide_subprocess_launcher_spawn_finish (IdeSubprocessLauncher *self,
GAsyncResult *result,
GError **error);
diff --git a/libide/subprocess/ide-subprocess.c b/libide/subprocess/ide-subprocess.c
new file mode 100644
index 0000000..57ce699
--- /dev/null
+++ b/libide/subprocess/ide-subprocess.c
@@ -0,0 +1,316 @@
+/* ide-subprocess.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-subprocess"
+
+#include "ide-subprocess.h"
+
+G_DEFINE_INTERFACE (IdeSubprocess, ide_subprocess, G_TYPE_OBJECT)
+
+static void
+ide_subprocess_default_init (IdeSubprocessInterface *iface)
+{
+}
+
+#define WRAP_INTERFACE_METHOD(self, name, default_return, ...) \
+ ((IDE_SUBPROCESS_GET_IFACE(self)->name != NULL) ? \
+ IDE_SUBPROCESS_GET_IFACE(self)->name (self, ##__VA_ARGS__) : \
+ default_return)
+
+const gchar *
+ide_subprocess_get_identifier (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), NULL);
+
+ return WRAP_INTERFACE_METHOD (self, get_identifier, NULL);
+}
+
+/**
+ * ide_subprocess_get_stdout_pipe:
+ *
+ * Returns: (transfer none): A #GInputStream or %NULL.
+ */
+GInputStream *
+ide_subprocess_get_stdout_pipe (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), NULL);
+
+ return WRAP_INTERFACE_METHOD (self, get_stdout_pipe, NULL);
+}
+
+/**
+ * ide_subprocess_get_stderr_pipe:
+ *
+ * Returns: (transfer none): A #GInputStream or %NULL.
+ */
+GInputStream *
+ide_subprocess_get_stderr_pipe (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), NULL);
+
+ return WRAP_INTERFACE_METHOD (self, get_stderr_pipe, NULL);
+}
+
+/**
+ * ide_subprocess_get_stdin_pipe:
+ *
+ * Returns: (transfer none): A #GOutputStream or %NULL.
+ */
+GOutputStream *
+ide_subprocess_get_stdin_pipe (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), NULL);
+
+ return WRAP_INTERFACE_METHOD (self, get_stdin_pipe, NULL);
+}
+
+gboolean
+ide_subprocess_wait (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+ return WRAP_INTERFACE_METHOD (self, wait, FALSE, cancellable, error);
+}
+
+gboolean
+ide_subprocess_wait_check (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+ return ide_subprocess_wait (self, cancellable, error) &&
+ ide_subprocess_check_exit_status (self, error);
+}
+
+void
+ide_subprocess_wait_async (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_SUBPROCESS (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ WRAP_INTERFACE_METHOD (self, wait_async, NULL, cancellable, callback, user_data);
+}
+
+gboolean
+ide_subprocess_wait_finish (IdeSubprocess *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+ return WRAP_INTERFACE_METHOD (self, wait_finish, FALSE, result, error);
+}
+
+static void
+ide_subprocess_wait_check_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSubprocess *self = (IdeSubprocess *)object;
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (IDE_IS_SUBPROCESS (self));
+ g_assert (G_IS_TASK (task));
+
+ if (!ide_subprocess_wait_finish (self, result, &error))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ if (!ide_subprocess_check_exit_status (self, &error))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+void
+ide_subprocess_wait_check_async (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (IDE_IS_SUBPROCESS (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_subprocess_wait_check_async);
+
+ ide_subprocess_wait_async (self,
+ cancellable,
+ ide_subprocess_wait_check_cb,
+ g_steal_pointer (&task));
+}
+
+gboolean
+ide_subprocess_wait_check_finish (IdeSubprocess *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (g_task_is_valid (G_TASK (result), self), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), FALSE);
+}
+
+gboolean
+ide_subprocess_get_successful (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+ return WRAP_INTERFACE_METHOD (self, get_successful, FALSE);
+}
+
+gboolean
+ide_subprocess_get_if_exited (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+ return WRAP_INTERFACE_METHOD (self, get_if_exited, FALSE);
+}
+
+gint
+ide_subprocess_get_exit_status (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), 0);
+
+ return WRAP_INTERFACE_METHOD (self, get_exit_status, 0);
+}
+
+gboolean
+ide_subprocess_get_if_signaled (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+ return WRAP_INTERFACE_METHOD (self, get_if_signaled, FALSE);
+}
+
+gint
+ide_subprocess_get_term_sig (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), 0);
+
+ return WRAP_INTERFACE_METHOD (self, get_term_sig, 0);
+}
+
+gint
+ide_subprocess_get_status (IdeSubprocess *self)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), 0);
+
+ return WRAP_INTERFACE_METHOD (self, get_status, 0);
+}
+
+void
+ide_subprocess_send_signal (IdeSubprocess *self,
+ gint signal_num)
+{
+ g_return_if_fail (IDE_IS_SUBPROCESS (self));
+
+ WRAP_INTERFACE_METHOD (self, send_signal, NULL, signal_num);
+}
+
+void
+ide_subprocess_force_exit (IdeSubprocess *self)
+{
+ g_return_if_fail (IDE_IS_SUBPROCESS (self));
+
+ WRAP_INTERFACE_METHOD (self, force_exit, NULL);
+}
+
+gboolean
+ide_subprocess_communicate (IdeSubprocess *self,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+ return WRAP_INTERFACE_METHOD (self, communicate, FALSE, stdin_buf, cancellable, stdout_buf, stderr_buf,
error);
+}
+
+gboolean
+ide_subprocess_communicate_utf8 (IdeSubprocess *self,
+ const gchar *stdin_buf,
+ GCancellable *cancellable,
+ gchar **stdout_buf,
+ gchar **stderr_buf,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+ return WRAP_INTERFACE_METHOD (self, communicate_utf8, FALSE, stdin_buf, cancellable, stdout_buf,
stderr_buf, error);
+}
+
+void
+ide_subprocess_communicate_async (IdeSubprocess *self,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_SUBPROCESS (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ WRAP_INTERFACE_METHOD (self, communicate_async, NULL, stdin_buf, cancellable, callback, user_data);
+}
+
+gboolean
+ide_subprocess_communicate_finish (IdeSubprocess *self,
+ GAsyncResult *result,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return WRAP_INTERFACE_METHOD (self, communicate_finish, FALSE, result, stdout_buf, stderr_buf, error);
+}
+
+gboolean
+ide_subprocess_check_exit_status (IdeSubprocess *self,
+ GError **error)
+{
+ gint exit_status;
+
+ g_return_val_if_fail (IDE_IS_SUBPROCESS (self), FALSE);
+
+ exit_status = ide_subprocess_get_exit_status (self);
+
+ return g_spawn_check_exit_status (exit_status, error);
+}
diff --git a/libide/subprocess/ide-subprocess.h b/libide/subprocess/ide-subprocess.h
new file mode 100644
index 0000000..557bc98
--- /dev/null
+++ b/libide/subprocess/ide-subprocess.h
@@ -0,0 +1,141 @@
+/* ide-subprocess.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SUBPROCESS_H
+#define IDE_SUBPROCESS_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SUBPROCESS (ide_subprocess_get_type())
+
+G_DECLARE_INTERFACE (IdeSubprocess, ide_subprocess, IDE, SUBPROCESS, GObject)
+
+struct _IdeSubprocessInterface
+{
+ GTypeInterface parent_interface;
+
+ const gchar *(*get_identifier) (IdeSubprocess *self);
+ GInputStream *(*get_stdout_pipe) (IdeSubprocess *self);
+ GInputStream *(*get_stderr_pipe) (IdeSubprocess *self);
+ GOutputStream *(*get_stdin_pipe) (IdeSubprocess *self);
+ gboolean (*wait) (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GError **error);
+ void (*wait_async) (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*wait_finish) (IdeSubprocess *self,
+ GAsyncResult *result,
+ GError **error);
+ gboolean (*get_successful) (IdeSubprocess *self);
+ gboolean (*get_if_exited) (IdeSubprocess *self);
+ gint (*get_exit_status) (IdeSubprocess *self);
+ gboolean (*get_if_signaled) (IdeSubprocess *self);
+ gint (*get_term_sig) (IdeSubprocess *self);
+ gint (*get_status) (IdeSubprocess *self);
+ void (*send_signal) (IdeSubprocess *self,
+ gint signal_num);
+ void (*force_exit) (IdeSubprocess *self);
+ gboolean (*communicate) (IdeSubprocess *self,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error);
+ gboolean (*communicate_utf8) (IdeSubprocess *self,
+ const gchar *stdin_buf,
+ GCancellable *cancellable,
+ gchar **stdout_buf,
+ gchar **stderr_buf,
+ GError **error);
+ void (*communicate_async) (IdeSubprocess *self,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*communicate_finish) (IdeSubprocess *self,
+ GAsyncResult *result,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error);
+};
+
+const gchar *ide_subprocess_get_identifier (IdeSubprocess *self);
+GInputStream *ide_subprocess_get_stdout_pipe (IdeSubprocess *self);
+GInputStream *ide_subprocess_get_stderr_pipe (IdeSubprocess *self);
+GOutputStream *ide_subprocess_get_stdin_pipe (IdeSubprocess *self);
+gboolean ide_subprocess_wait (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GError **error);
+gboolean ide_subprocess_wait_check (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GError **error);
+void ide_subprocess_wait_async (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_subprocess_wait_finish (IdeSubprocess *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_subprocess_wait_check_async (IdeSubprocess *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_subprocess_wait_check_finish (IdeSubprocess *self,
+ GAsyncResult *result,
+ GError **error);
+gboolean ide_subprocess_check_exit_status (IdeSubprocess *self,
+ GError **error);
+gboolean ide_subprocess_get_successful (IdeSubprocess *self);
+gboolean ide_subprocess_get_if_exited (IdeSubprocess *self);
+gint ide_subprocess_get_exit_status (IdeSubprocess *self);
+gboolean ide_subprocess_get_if_signaled (IdeSubprocess *self);
+gint ide_subprocess_get_term_sig (IdeSubprocess *self);
+gint ide_subprocess_get_status (IdeSubprocess *self);
+void ide_subprocess_send_signal (IdeSubprocess *self,
+ gint signal_num);
+void ide_subprocess_force_exit (IdeSubprocess *self);
+gboolean ide_subprocess_communicate (IdeSubprocess *self,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error);
+gboolean ide_subprocess_communicate_utf8 (IdeSubprocess *self,
+ const gchar *stdin_buf,
+ GCancellable *cancellable,
+ gchar **stdout_buf,
+ gchar **stderr_buf,
+ GError **error);
+void ide_subprocess_communicate_async (IdeSubprocess *self,
+ GBytes *stdin_buf,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_subprocess_communicate_finish (IdeSubprocess *self,
+ GAsyncResult *result,
+ GBytes **stdout_buf,
+ GBytes **stderr_buf,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* IDE_SUBPROCESS_H */
diff --git a/plugins/autotools/ide-autotools-build-system.c b/plugins/autotools/ide-autotools-build-system.c
index 4f64866..7debea4 100644
--- a/plugins/autotools/ide-autotools-build-system.c
+++ b/plugins/autotools/ide-autotools-build-system.c
@@ -932,11 +932,11 @@ simple_make_command_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
- GSubprocess *subprocess = (GSubprocess *)object;
+ IdeSubprocess *subprocess = (IdeSubprocess *)object;
g_autoptr(GTask) task = user_data;
GError *error = NULL;
- if (!g_subprocess_wait_check_finish (subprocess, result, &error))
+ if (!ide_subprocess_wait_check_finish (subprocess, result, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
@@ -949,7 +949,7 @@ simple_make_command (GFile *directory,
IdeConfiguration *configuration)
{
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
- g_autoptr(GSubprocess) subprocess = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
GCancellable *cancellable;
IdeRuntime *runtime;
GError *error = NULL;
@@ -1000,10 +1000,10 @@ simple_make_command (GFile *directory,
return;
}
- g_subprocess_wait_check_async (subprocess,
- g_task_get_cancellable (task),
- simple_make_command_cb,
- g_object_ref (task));
+ ide_subprocess_wait_check_async (subprocess,
+ cancellable,
+ simple_make_command_cb,
+ g_steal_pointer (&task));
}
static void
diff --git a/plugins/autotools/ide-autotools-build-task.c b/plugins/autotools/ide-autotools-build-task.c
index 8f43973..d3094be 100644
--- a/plugins/autotools/ide-autotools-build-task.c
+++ b/plugins/autotools/ide-autotools-build-task.c
@@ -75,30 +75,30 @@ enum {
LAST_PROP
};
-static GSubprocess *log_and_spawn (IdeAutotoolsBuildTask *self,
- IdeSubprocessLauncher *launcher,
- GCancellable *cancellable,
- GError **error,
- const gchar *argv0,
- ...) G_GNUC_NULL_TERMINATED;
-static gboolean step_mkdirs (GTask *task,
- IdeAutotoolsBuildTask *self,
- WorkerState *state,
- GCancellable *cancellable);
-static gboolean step_autogen (GTask *task,
- IdeAutotoolsBuildTask *self,
- WorkerState *state,
- GCancellable *cancellable);
-static gboolean step_configure (GTask *task,
- IdeAutotoolsBuildTask *self,
- WorkerState *state,
- GCancellable *cancellable);
-static gboolean step_make_all (GTask *task,
- IdeAutotoolsBuildTask *self,
- WorkerState *state,
- GCancellable *cancellable);
-static void apply_environment (IdeAutotoolsBuildTask *self,
- IdeSubprocessLauncher *launcher);
+static IdeSubprocess *log_and_spawn (IdeAutotoolsBuildTask *self,
+ IdeSubprocessLauncher *launcher,
+ GCancellable *cancellable,
+ GError **error,
+ const gchar *argv0,
+ ...) G_GNUC_NULL_TERMINATED;
+static gboolean step_mkdirs (GTask *task,
+ IdeAutotoolsBuildTask *self,
+ WorkerState *state,
+ GCancellable *cancellable);
+static gboolean step_autogen (GTask *task,
+ IdeAutotoolsBuildTask *self,
+ WorkerState *state,
+ GCancellable *cancellable);
+static gboolean step_configure (GTask *task,
+ IdeAutotoolsBuildTask *self,
+ WorkerState *state,
+ GCancellable *cancellable);
+static gboolean step_make_all (GTask *task,
+ IdeAutotoolsBuildTask *self,
+ WorkerState *state,
+ GCancellable *cancellable);
+static void apply_environment (IdeAutotoolsBuildTask *self,
+ IdeSubprocessLauncher *launcher);
static GParamSpec *properties [LAST_PROP];
static WorkStep workSteps [] = {
@@ -735,7 +735,7 @@ log_in_main (gpointer data)
return G_SOURCE_REMOVE;
}
-static GSubprocess *
+static IdeSubprocess *
log_and_spawn (IdeAutotoolsBuildTask *self,
IdeSubprocessLauncher *launcher,
GCancellable *cancellable,
@@ -743,7 +743,7 @@ log_and_spawn (IdeAutotoolsBuildTask *self,
const gchar *argv0,
...)
{
- GSubprocess *ret;
+ IdeSubprocess *ret;
struct {
IdeBuildResult *result;
gchar *message;
@@ -830,7 +830,7 @@ step_autogen (GTask *task,
g_autofree gchar *autogen_sh_path = NULL;
g_autofree gchar *configure_path = NULL;
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
- g_autoptr(GSubprocess) process = NULL;
+ g_autoptr(IdeSubprocess) process = NULL;
GError *error = NULL;
g_assert (G_IS_TASK (task));
@@ -889,7 +889,7 @@ step_autogen (GTask *task,
ide_build_result_log_subprocess (IDE_BUILD_RESULT (self), process);
- if (!g_subprocess_wait_check (process, cancellable, &error))
+ if (!ide_subprocess_wait_check (process, cancellable, &error))
{
g_task_return_error (task, error);
return FALSE;
@@ -915,7 +915,7 @@ step_configure (GTask *task,
GCancellable *cancellable)
{
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
- g_autoptr(GSubprocess) process = NULL;
+ g_autoptr(IdeSubprocess) process = NULL;
g_autofree gchar *makefile_path = NULL;
g_autofree gchar *config_log = NULL;
GError *error = NULL;
@@ -959,7 +959,7 @@ step_configure (GTask *task,
ide_build_result_log_subprocess (IDE_BUILD_RESULT (self), process);
- if (!g_subprocess_wait_check (process, cancellable, &error))
+ if (!ide_subprocess_wait_check (process, cancellable, &error))
{
g_task_return_error (task, error);
return FALSE;
@@ -981,7 +981,7 @@ step_make_all (GTask *task,
GCancellable *cancellable)
{
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
- g_autoptr(GSubprocess) process = NULL;
+ g_autoptr(IdeSubprocess) process = NULL;
const gchar * const *targets;
const gchar *make = NULL;
gchar *default_targets[] = { "all", NULL };
@@ -1047,7 +1047,7 @@ step_make_all (GTask *task,
ide_build_result_log_subprocess (IDE_BUILD_RESULT (self), process);
- if (!g_subprocess_wait_check (process, cancellable, &error))
+ if (!ide_subprocess_wait_check (process, cancellable, &error))
{
g_task_return_error (task, error);
return FALSE;
diff --git a/plugins/autotools/ide-makecache.c b/plugins/autotools/ide-makecache.c
index 14bd650..c4ecb82 100644
--- a/plugins/autotools/ide-makecache.c
+++ b/plugins/autotools/ide-makecache.c
@@ -1943,7 +1943,7 @@ ide_makecache_get_build_targets_worker (GTask *task,
for (guint j = 0; j < makedirs->len; j++)
{
- g_autoptr(GSubprocess) subprocess = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
g_autoptr(GHashTable) amdirs = NULL;
g_autofree gchar *path = NULL;
GFile *makedir;
@@ -1970,7 +1970,7 @@ ide_makecache_get_build_targets_worker (GTask *task,
* Write our helper target that will include the Makefile and then print
* debug variables we care about.
*/
- if (!g_subprocess_communicate_utf8 (subprocess, PRINT_VARS, NULL, &stdout_buf, NULL, &error))
+ if (!ide_subprocess_communicate_utf8 (subprocess, PRINT_VARS, NULL, &stdout_buf, NULL, &error))
{
g_task_return_error (task, error);
IDE_GOTO (failure);
diff --git a/plugins/flatpak/gbp-flatpak-runtime.c b/plugins/flatpak/gbp-flatpak-runtime.c
index e8e35bc..12fa6cb 100644
--- a/plugins/flatpak/gbp-flatpak-runtime.c
+++ b/plugins/flatpak/gbp-flatpak-runtime.c
@@ -61,11 +61,11 @@ get_build_directory (GbpFlatpakRuntime *self)
static gboolean
gbp_flatpak_runtime_contains_program_in_path (IdeRuntime *runtime,
- const gchar *program,
- GCancellable *cancellable)
+ const gchar *program,
+ GCancellable *cancellable)
{
g_autoptr(IdeSubprocessLauncher) launcher = NULL;
- g_autoptr(GSubprocess) subprocess = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
g_assert (IDE_IS_RUNTIME (runtime));
g_assert (program != NULL);
@@ -78,14 +78,14 @@ gbp_flatpak_runtime_contains_program_in_path (IdeRuntime *runtime,
subprocess = ide_subprocess_launcher_spawn_sync (launcher, cancellable, NULL);
- return (subprocess != NULL) && g_subprocess_wait_check (subprocess, cancellable, NULL);
+ return (subprocess != NULL) && ide_subprocess_wait_check (subprocess, cancellable, NULL);
}
static void
gbp_flatpak_runtime_prebuild_worker (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
{
GbpFlatpakRuntime *self = source_object;
g_autofree gchar *build_path = NULL;
@@ -153,9 +153,9 @@ gbp_flatpak_runtime_prebuild_worker (GTask *task,
static void
gbp_flatpak_runtime_prebuild_async (IdeRuntime *runtime,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
GbpFlatpakRuntime *self = (GbpFlatpakRuntime *)runtime;
g_autoptr(GTask) task = NULL;
@@ -169,8 +169,8 @@ gbp_flatpak_runtime_prebuild_async (IdeRuntime *runtime,
static gboolean
gbp_flatpak_runtime_prebuild_finish (IdeRuntime *runtime,
- GAsyncResult *result,
- GError **error)
+ GAsyncResult *result,
+ GError **error)
{
GbpFlatpakRuntime *self = (GbpFlatpakRuntime *)runtime;
@@ -205,7 +205,7 @@ gbp_flatpak_runtime_create_launcher (IdeRuntime *runtime,
static void
gbp_flatpak_runtime_prepare_configuration (IdeRuntime *runtime,
- IdeConfiguration *configuration)
+ IdeConfiguration *configuration)
{
g_assert (IDE_IS_RUNTIME (runtime));
g_assert (IDE_IS_CONFIGURATION (configuration));
@@ -215,9 +215,9 @@ gbp_flatpak_runtime_prepare_configuration (IdeRuntime *runtime,
static void
gbp_flatpak_runtime_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
GbpFlatpakRuntime *self = GBP_FLATPAK_RUNTIME(object);
@@ -242,9 +242,9 @@ gbp_flatpak_runtime_get_property (GObject *object,
static void
gbp_flatpak_runtime_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
GbpFlatpakRuntime *self = GBP_FLATPAK_RUNTIME(object);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]