[gnome-builder] flatpak: install gnome-builder-flatpak daemon



commit b98f2d323bfa1d88edf9418594320452e7550768
Author: Christian Hergert <chergert redhat com>
Date:   Fri Apr 30 14:14:02 2021 -0700

    flatpak: install gnome-builder-flatpak daemon
    
    This daemon will allow us to eventually offload more of Flatpak operations
    to an external daemon, simplifying what runs in process and reduce
    surface area for things to break.

 src/plugins/flatpak/daemon/gnome-builder-flatpak.c | 134 +++++
 .../flatpak/daemon/ipc-flatpak-service-impl.c      | 543 +++++++++++++++++++++
 .../flatpak/daemon/ipc-flatpak-service-impl.h      |  33 ++
 src/plugins/flatpak/daemon/ipc-flatpak-util.h      |  42 ++
 src/plugins/flatpak/daemon/meson.build             |  41 ++
 .../daemon/org.gnome.Builder.Flatpak.Service.xml   |  71 +++
 .../daemon/org.gnome.Builder.Flatpak.Transfer.xml  |  30 ++
 src/plugins/flatpak/daemon/test-flatpak.c          | 175 +++++++
 src/plugins/flatpak/meson.build                    |  12 +-
 9 files changed, 1078 insertions(+), 3 deletions(-)
---
diff --git a/src/plugins/flatpak/daemon/gnome-builder-flatpak.c 
b/src/plugins/flatpak/daemon/gnome-builder-flatpak.c
new file mode 100644
index 000000000..9321adf18
--- /dev/null
+++ b/src/plugins/flatpak/daemon/gnome-builder-flatpak.c
@@ -0,0 +1,134 @@
+/* gnome-builder-flatpak.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib-unix.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+#include <flatpak/flatpak.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/procctl.h>
+#endif
+
+#include "ipc-flatpak-service.h"
+#include "ipc-flatpak-service-impl.h"
+
+static GDBusConnection *
+create_connection (GIOStream  *stream,
+                   GMainLoop  *main_loop,
+                   GError    **error)
+{
+  GDBusConnection *ret;
+
+  g_assert (G_IS_IO_STREAM (stream));
+  g_assert (main_loop != NULL);
+  g_assert (error != NULL);
+
+  if ((ret = g_dbus_connection_new_sync (stream, NULL,
+                                          G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
+                                          NULL, NULL, error)))
+    {
+      g_dbus_connection_set_exit_on_close (ret, FALSE);
+      g_signal_connect_swapped (ret, "closed", G_CALLBACK (g_main_loop_quit), main_loop);
+    }
+
+  return ret;
+}
+
+static void
+log_func (const gchar    *log_domain,
+          GLogLevelFlags  flags,
+          const gchar    *message,
+          gpointer        user_data)
+{
+  g_printerr ("gnome-builder-flatpak: %s\n", message);
+}
+
+gint
+main (gint argc,
+      gchar *argv[])
+{
+  g_autoptr(GDBusConnection) connection = NULL;
+  g_autoptr(IpcFlatpakService) service = NULL;
+  g_autoptr(GOutputStream) stdout_stream = NULL;
+  g_autoptr(GInputStream) stdin_stream = NULL;
+  g_autoptr(GIOStream) stream = NULL;
+  g_autoptr(GMainLoop) main_loop = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_set_prgname ("gnome-builder-flatpak");
+  g_set_application_name ("gnome-builder-flatpak");
+
+#ifdef __linux__
+  prctl (PR_SET_PDEATHSIG, SIGTERM);
+#elif defined(__FreeBSD__)
+  procctl (P_PID, 0, PROC_PDEATHSIG_CTL, &(int){ SIGTERM });
+#else
+# warning "Please submit a patch to support parent-death signal on your OS"
+#endif
+
+  signal (SIGPIPE, SIG_IGN);
+
+  g_log_set_handler (NULL, G_LOG_LEVEL_MASK, log_func, NULL);
+
+  if (!g_unix_set_fd_nonblocking (STDIN_FILENO, TRUE, &error) ||
+      !g_unix_set_fd_nonblocking (STDOUT_FILENO, TRUE, &error))
+    goto error;
+
+  main_loop = g_main_loop_new (NULL, FALSE);
+  stdin_stream = g_unix_input_stream_new (STDIN_FILENO, FALSE);
+  stdout_stream = g_unix_output_stream_new (STDOUT_FILENO, FALSE);
+  stream = g_simple_io_stream_new (stdin_stream, stdout_stream);
+
+  if (!(connection = create_connection (stream, main_loop, &error)))
+    goto error;
+
+  g_dbus_connection_set_exit_on_close (connection, FALSE);
+  g_signal_connect_swapped (connection, "closed", G_CALLBACK (g_main_loop_quit), main_loop);
+
+  service = ipc_flatpak_service_impl_new ();
+
+  if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service),
+                                         connection,
+                                         "/org/gnome/Builder/Flatpak",
+                                         &error))
+    goto error;
+
+  g_dbus_connection_start_message_processing (connection);
+  g_main_loop_run (main_loop);
+
+  g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (service));
+  g_dbus_connection_close_sync (connection, NULL, NULL);
+
+  return EXIT_SUCCESS;
+
+error:
+  if (error != NULL)
+    g_printerr ("%s\n", error->message);
+
+  return EXIT_FAILURE;
+}
diff --git a/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c 
b/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c
new file mode 100644
index 000000000..e1921ed66
--- /dev/null
+++ b/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c
@@ -0,0 +1,543 @@
+/* ipc-flatpak-service-impl.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ipc-flatpak-service-impl"
+
+#include "config.h"
+
+#include <flatpak/flatpak.h>
+
+#include "ipc-flatpak-service-impl.h"
+#include "ipc-flatpak-util.h"
+
+typedef struct
+{
+  FlatpakInstallation *installation;
+  gchar *name;
+  gchar *arch;
+  gchar *branch;
+  gchar *sdk_name;
+  gchar *sdk_branch;
+  gboolean sdk_extension : 1;
+} Runtime;
+
+typedef struct
+{
+  FlatpakInstallation *installation;
+  GFileMonitor *monitor;
+} Install;
+
+typedef struct
+{
+  GDBusMethodInvocation *invocation;
+  GPtrArray             *installs;
+  FlatpakRef            *target;
+} IsKnown;
+
+struct _IpcFlatpakServiceImpl
+{
+  IpcFlatpakServiceSkeleton parent;
+  GHashTable *installs;
+  GPtrArray *runtimes;
+};
+
+static void      ipc_flatpak_service_impl_install_changed_cb (IpcFlatpakServiceImpl  *self,
+                                                              GFile                  *file,
+                                                              GFile                  *other_file,
+                                                              GFileMonitorEvent       event,
+                                                              GFileMonitor           *monitor);
+static gboolean  ipc_flatpak_service_impl_add_installation   (IpcFlatpakService      *service,
+                                                              GDBusMethodInvocation  *invocation,
+                                                              const gchar            *path,
+                                                              gboolean                is_user);
+static void      add_runtime                                 (IpcFlatpakServiceImpl  *service,
+                                                              Runtime                *runtime);
+static Install  *install_new                                 (IpcFlatpakServiceImpl  *self,
+                                                              FlatpakInstallation    *installation,
+                                                              GError                **error);
+static void      install_free                                (Install                *install);
+static void      install_reload                              (IpcFlatpakServiceImpl  *self,
+                                                              Install                *install);
+static GVariant *runtime_to_variant                          (const Runtime          *runtime);
+static void      runtime_free                                (Runtime                *runtime);
+static gboolean  runtime_equal                               (const Runtime          *a,
+                                                              const Runtime          *b);
+static void      is_known_free                               (IsKnown                *state);
+
+static void
+is_known_free (IsKnown *state)
+{
+  g_clear_pointer (&state->installs, g_ptr_array_unref);
+  g_clear_object (&state->invocation);
+  g_clear_object (&state->target);
+  g_slice_free (IsKnown, state);
+}
+
+static void
+add_runtime (IpcFlatpakServiceImpl *self,
+             Runtime               *runtime)
+{
+  g_autoptr(GVariant) variant = NULL;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
+  g_assert (runtime != NULL);
+
+  for (guint i = 0; i < self->runtimes->len; i++)
+    {
+      const Runtime *other = g_ptr_array_index (self->runtimes, i);
+
+      /* Ignore if we know about it already */
+      if (runtime_equal (other, runtime))
+        return;
+    }
+
+  variant = runtime_to_variant (runtime);
+  g_ptr_array_add (self->runtimes, g_steal_pointer (&runtime));
+
+  ipc_flatpak_service_emit_runtime_added (IPC_FLATPAK_SERVICE (self), variant);
+}
+
+static void
+runtime_free (Runtime *runtime)
+{
+  g_clear_pointer (&runtime->name, g_free);
+  g_clear_pointer (&runtime->arch, g_free);
+  g_clear_pointer (&runtime->branch, g_free);
+  g_clear_pointer (&runtime->sdk_name, g_free);
+  g_clear_pointer (&runtime->sdk_branch, g_free);
+  g_slice_free (Runtime, runtime);
+}
+
+static gboolean
+runtime_equal (const Runtime *a,
+               const Runtime *b)
+{
+  return g_str_equal (a->name, b->name) &&
+         g_str_equal (a->arch, b->arch) &&
+         g_str_equal (a->branch, b->branch);
+}
+
+static GVariant *
+runtime_to_variant (const Runtime *runtime)
+{
+  return g_variant_take_ref (g_variant_new ("(sssssb)",
+                                            runtime->name,
+                                            runtime->arch,
+                                            runtime->branch,
+                                            runtime->sdk_name,
+                                            runtime->sdk_branch,
+                                            runtime->sdk_extension));
+}
+
+static Install *
+install_new (IpcFlatpakServiceImpl  *self,
+             FlatpakInstallation    *installation,
+             GError                **error)
+{
+  g_autoptr(GFileMonitor) monitor = NULL;
+  Install *ret;
+
+  if (!(monitor = flatpak_installation_create_monitor (installation, NULL, error)))
+    return NULL;
+
+  ret = g_slice_new0 (Install);
+  ret->installation = g_object_ref (installation);
+  ret->monitor = g_steal_pointer (&monitor);
+
+  g_signal_connect_object (ret->monitor,
+                           "changed",
+                           G_CALLBACK (ipc_flatpak_service_impl_install_changed_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  return g_steal_pointer (&ret);
+}
+
+static void
+install_free (Install *install)
+{
+  g_clear_object (&install->installation);
+  g_clear_object (&install->monitor);
+  g_slice_free (Install, install);
+}
+
+static void
+install_reload (IpcFlatpakServiceImpl *self,
+                Install               *install)
+{
+  g_autoptr(GPtrArray) refs = NULL;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
+  g_assert (install != NULL);
+
+  if (!(refs = flatpak_installation_list_installed_refs_by_kind (install->installation,
+                                                                 FLATPAK_REF_KIND_RUNTIME,
+                                                                 NULL, NULL)))
+    return;
+
+  for (guint i = 0; i < refs->len; i++)
+    {
+      FlatpakInstalledRef *ref = g_ptr_array_index (refs, i);
+      g_autoptr(FlatpakRef) sdk_ref = NULL;
+      g_autoptr(GBytes) bytes = NULL;
+      g_autoptr(GKeyFile) keyfile = g_key_file_new ();
+      g_autofree gchar *sdk_full_ref = NULL;
+      g_autofree gchar *name = NULL;
+      g_autofree gchar *runtime = NULL;
+      g_autofree gchar *sdk = NULL;
+      g_autofree gchar *exten_of = NULL;
+      Runtime *state;
+
+      if (!(bytes = flatpak_installed_ref_load_metadata (ref, NULL, NULL)) ||
+          !g_key_file_load_from_bytes (keyfile, bytes, G_KEY_FILE_NONE, NULL))
+        continue;
+
+      if (!(name = g_key_file_get_string (keyfile, "Runtime", "name", NULL)) ||
+          !(runtime = g_key_file_get_string (keyfile, "Runtime", "runtime", NULL)) ||
+          !(sdk = g_key_file_get_string (keyfile, "Runtime", "sdk", NULL)))
+        continue;
+
+      if (g_key_file_has_group (keyfile, "ExtensionOf"))
+        {
+          /* Skip if this item is an extension, but not an SDK extension */
+          if (!g_key_file_has_key (keyfile, "ExtensionOf", "ref", NULL) ||
+              strstr (name, ".Extension.") == NULL)
+            continue;
+
+          exten_of = g_key_file_get_string (keyfile, "ExtensionOf", "ref", NULL);
+        }
+
+      if (!g_str_has_prefix (sdk, "runtime/"))
+        sdk_full_ref = g_strdup_printf ("runtime/%s", sdk);
+      else
+        sdk_full_ref = g_strdup (sdk);
+
+      /* Make sure we can parse the SDK reference */
+      if (!(sdk_ref = flatpak_ref_parse (sdk_full_ref, NULL)))
+        continue;
+
+      state = g_slice_new0 (Runtime);
+      state->installation = g_object_ref (install->installation);
+      state->name = g_strdup (flatpak_ref_get_name (FLATPAK_REF (ref)));
+      state->arch = g_strdup (flatpak_ref_get_arch (FLATPAK_REF (ref)));
+      state->branch = g_strdup (flatpak_ref_get_branch (FLATPAK_REF (ref)));
+      state->sdk_name = g_strdup (flatpak_ref_get_name (sdk_ref));
+      state->sdk_branch = g_strdup (flatpak_ref_get_branch (sdk_ref));
+      state->sdk_extension = exten_of != NULL;
+
+      add_runtime (self, g_steal_pointer (&state));
+    }
+}
+
+static gboolean
+ipc_flatpak_service_impl_add_installation (IpcFlatpakService     *service,
+                                           GDBusMethodInvocation *invocation,
+                                           const gchar           *path,
+                                           gboolean               is_user)
+{
+  IpcFlatpakServiceImpl *self = (IpcFlatpakServiceImpl *)service;
+  g_autoptr(FlatpakInstallation) installation = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) file = NULL;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
+  g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
+  g_assert (path != NULL);
+
+  file = g_file_new_for_path (path);
+
+  if (!g_hash_table_contains (self->installs, file))
+    {
+      Install *install;
+
+      if (!(installation = flatpak_installation_new_for_path (file, is_user, NULL, &error)) ||
+          !(install = install_new (self, installation, &error)))
+        return complete_wrapped_error (invocation, error);
+
+      install_reload (self, install);
+
+      g_hash_table_insert (self->installs,
+                           g_steal_pointer (&file),
+                           g_steal_pointer (&install));
+    }
+
+  ipc_flatpak_service_complete_add_installation (service, invocation);
+
+  return TRUE;
+}
+
+static gboolean
+ipc_flatpak_service_impl_list_runtimes (IpcFlatpakService     *service,
+                                        GDBusMethodInvocation *invocation)
+{
+  IpcFlatpakServiceImpl *self = (IpcFlatpakServiceImpl *)service;
+  g_autoptr(GError) error = NULL;
+  GVariantBuilder builder;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
+  g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssssb)"));
+
+  for (guint i = 0; i < self->runtimes->len; i++)
+    {
+      const Runtime *runtime = g_ptr_array_index (self->runtimes, i);
+      g_autoptr(GVariant) item = runtime_to_variant (runtime);
+      g_variant_builder_add_value (&builder, item);
+    }
+
+  ipc_flatpak_service_complete_list_runtimes (service,
+                                              invocation,
+                                              g_variant_builder_end (&builder));
+
+  return TRUE;
+}
+
+static void
+is_known_worker (GTask        *task,
+                 gpointer      source_object,
+                 gpointer      task_data,
+                 GCancellable *cancellable)
+{
+  g_autoptr(GPtrArray) remotes = NULL;
+  g_autoptr(GError) error = NULL;
+  IsKnown *state = task_data;
+  const gchar *ref_name;
+  const gchar *ref_arch;
+  const gchar *ref_branch;
+  gint64 download_size = 0;
+  gboolean found = FALSE;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (source_object));
+  g_assert (state != NULL);
+  g_assert (state->installs != NULL);
+  g_assert (G_IS_DBUS_METHOD_INVOCATION (state->invocation));
+  g_assert (FLATPAK_IS_REF (state->target));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  ref_name = flatpak_ref_get_name (state->target);
+  ref_arch = flatpak_ref_get_arch (state->target);
+  ref_branch = flatpak_ref_get_branch (state->target);
+
+  for (guint z = 0; z < state->installs->len; z++)
+    {
+      FlatpakInstallation *install = g_ptr_array_index (state->installs, z);
+
+      if (!(remotes = flatpak_installation_list_remotes (install, NULL, &error)))
+        goto finish;
+
+      for (guint i = 0; i < remotes->len; i++)
+        {
+          FlatpakRemote *remote = g_ptr_array_index (remotes, i);
+          const gchar *remote_name = flatpak_remote_get_name (remote);
+          g_autoptr(GPtrArray) refs = NULL;
+
+          if (!(refs = flatpak_installation_list_remote_refs_sync (install, remote_name, NULL, NULL)))
+            continue;
+
+          for (guint j = 0; j < refs->len; j++)
+            {
+              FlatpakRemoteRef *remote_ref = g_ptr_array_index (refs, j);
+
+              if (g_str_equal (ref_name, flatpak_ref_get_name (FLATPAK_REF (remote_ref))) &&
+                  g_str_equal (ref_arch, flatpak_ref_get_arch (FLATPAK_REF (remote_ref))) &&
+                  g_str_equal (ref_branch, flatpak_ref_get_branch (FLATPAK_REF (remote_ref))))
+                {
+                  found = TRUE;
+                  download_size = flatpak_remote_ref_get_download_size (remote_ref);
+                  goto finish;
+                }
+            }
+        }
+    }
+
+finish:
+  ipc_flatpak_service_complete_runtime_is_known (g_task_get_source_object (task),
+                                                 g_steal_pointer (&state->invocation),
+                                                 found,
+                                                 download_size);
+
+  if (error != NULL)
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+ipc_flatpak_service_impl_runtime_is_known (IpcFlatpakService     *service,
+                                           GDBusMethodInvocation *invocation,
+                                           const gchar           *name)
+{
+  IpcFlatpakServiceImpl *self = (IpcFlatpakServiceImpl *)service;
+  g_autofree gchar *full_name = NULL;
+  g_autoptr(FlatpakRef) ref = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = NULL;
+  const gchar *ref_name;
+  const gchar *ref_arch;
+  const gchar *ref_branch;
+  GHashTableIter iter;
+  Install *install;
+  IsKnown *state;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
+  g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
+  g_assert (name != NULL);
+
+  /* Homogenize names into runtime/name/arch/branch */
+  if (g_str_has_prefix (name, "runtime/"))
+    name += strlen ("runtime/");
+  full_name = g_strdup_printf ("runtime/%s", name);
+
+  /* Parse the ref, so we can try to locate it */
+  if (!(ref = flatpak_ref_parse (full_name, &error)))
+    return complete_wrapped_error (invocation, error);
+
+  ref_name = flatpak_ref_get_name (ref);
+  ref_arch = flatpak_ref_get_arch (ref);
+  ref_branch = flatpak_ref_get_branch (ref);
+
+  /* First check if we know about the runtime from those installed */
+  for (guint i = 0; i < self->runtimes->len; i++)
+    {
+      const Runtime *runtime = g_ptr_array_index (self->runtimes, i);
+
+      if (g_str_equal (ref_name, runtime->name) &&
+          g_str_equal (ref_arch, runtime->arch) &&
+          g_str_equal (ref_branch, runtime->branch))
+        {
+          ipc_flatpak_service_complete_runtime_is_known (service, invocation, TRUE, 0);
+          return TRUE;
+        }
+    }
+
+  state = g_slice_new0 (IsKnown);
+  state->installs = g_ptr_array_new_with_free_func (g_object_unref);
+  state->target = g_object_ref (ref);
+  state->invocation = g_steal_pointer (&invocation);
+
+  task = g_task_new (self, NULL, NULL, NULL);
+  g_task_set_source_tag (task, ipc_flatpak_service_impl_runtime_is_known);
+  g_task_set_task_data (task, state, (GDestroyNotify) is_known_free);
+
+  /* Now check remote refs */
+  g_hash_table_iter_init (&iter, self->installs);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&install))
+    g_ptr_array_add (state->installs, g_object_ref (install->installation));
+
+  g_task_run_in_thread (task, is_known_worker);
+
+  return TRUE;
+}
+
+static void
+ipc_flatpak_service_impl_install_changed_cb (IpcFlatpakServiceImpl *self,
+                                             GFile                 *file,
+                                             GFile                 *other_file,
+                                             GFileMonitorEvent      event,
+                                             GFileMonitor          *monitor)
+{
+  GHashTableIter iter;
+  Install *install;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (!other_file || G_IS_FILE (other_file));
+  g_assert (G_IS_FILE_MONITOR (monitor));
+
+  g_hash_table_iter_init (&iter, self->installs);
+
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&install))
+    {
+      if (install->monitor == monitor)
+        {
+          install_reload (self, install);
+          break;
+        }
+    }
+}
+
+static gboolean
+ipc_flatpak_service_impl_install (IpcFlatpakService     *service,
+                                  GDBusMethodInvocation *invocation,
+                                  const gchar           *full_ref_name)
+{
+  g_autoptr(FlatpakRef) ref = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (service));
+  g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
+  g_assert (full_ref_name != NULL);
+
+  if (!(ref = flatpak_ref_parse (full_ref_name, &error)))
+    return complete_wrapped_error (invocation, error);
+
+  ipc_flatpak_service_complete_install (service, invocation, "");
+
+  return TRUE;
+}
+
+static void
+service_iface_init (IpcFlatpakServiceIface *iface)
+{
+  iface->handle_add_installation = ipc_flatpak_service_impl_add_installation;
+  iface->handle_list_runtimes = ipc_flatpak_service_impl_list_runtimes;
+  iface->handle_runtime_is_known = ipc_flatpak_service_impl_runtime_is_known;
+  iface->handle_install = ipc_flatpak_service_impl_install;
+}
+
+G_DEFINE_TYPE_WITH_CODE (IpcFlatpakServiceImpl, ipc_flatpak_service_impl, IPC_TYPE_FLATPAK_SERVICE_SKELETON,
+                         G_IMPLEMENT_INTERFACE (IPC_TYPE_FLATPAK_SERVICE, service_iface_init))
+
+static void
+ipc_flatpak_service_impl_finalize (GObject *object)
+{
+  IpcFlatpakServiceImpl *self = (IpcFlatpakServiceImpl *)object;
+
+  g_clear_pointer (&self->installs, g_hash_table_unref);
+  g_clear_pointer (&self->runtimes, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (ipc_flatpak_service_impl_parent_class)->finalize (object);
+}
+
+static void
+ipc_flatpak_service_impl_class_init (IpcFlatpakServiceImplClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ipc_flatpak_service_impl_finalize;
+}
+
+static void
+ipc_flatpak_service_impl_init (IpcFlatpakServiceImpl *self)
+{
+  self->installs = g_hash_table_new_full (g_file_hash,
+                                               (GEqualFunc) g_file_equal,
+                                               g_object_unref,
+                                               (GDestroyNotify) install_free);
+  self->runtimes = g_ptr_array_new_with_free_func ((GDestroyNotify) runtime_free);
+}
+
+IpcFlatpakService *
+ipc_flatpak_service_impl_new (void)
+{
+  return g_object_new (IPC_TYPE_FLATPAK_SERVICE_IMPL, NULL);
+}
diff --git a/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.h 
b/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.h
new file mode 100644
index 000000000..0ab95b9d8
--- /dev/null
+++ b/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.h
@@ -0,0 +1,33 @@
+/* ipc-flatpak-service-impl.h
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ipc-flatpak-service.h"
+
+G_BEGIN_DECLS
+
+#define IPC_TYPE_FLATPAK_SERVICE_IMPL (ipc_flatpak_service_impl_get_type())
+
+G_DECLARE_FINAL_TYPE (IpcFlatpakServiceImpl, ipc_flatpak_service_impl, IPC, FLATPAK_SERVICE_IMPL, 
IpcFlatpakServiceSkeleton)
+
+IpcFlatpakService *ipc_flatpak_service_impl_new (void);
+
+G_END_DECLS
diff --git a/src/plugins/flatpak/daemon/ipc-flatpak-util.h b/src/plugins/flatpak/daemon/ipc-flatpak-util.h
new file mode 100644
index 000000000..d09099f75
--- /dev/null
+++ b/src/plugins/flatpak/daemon/ipc-flatpak-util.h
@@ -0,0 +1,42 @@
+/* ipc-flatpak-util.h
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+static inline gboolean
+complete_wrapped_error (GDBusMethodInvocation *invocation,
+                        const GError          *error)
+{
+  g_autoptr(GError) wrapped = NULL;
+
+  wrapped = g_error_new (G_IO_ERROR,
+                         G_IO_ERROR_FAILED,
+                         "The operation failed. The original error was \"%s\"",
+                         error->message);
+  g_dbus_method_invocation_return_gerror (invocation, wrapped);
+
+  return TRUE;
+}
+
+G_END_DECLS
diff --git a/src/plugins/flatpak/daemon/meson.build b/src/plugins/flatpak/daemon/meson.build
new file mode 100644
index 000000000..506db5eea
--- /dev/null
+++ b/src/plugins/flatpak/daemon/meson.build
@@ -0,0 +1,41 @@
+gnome_builder_flatpak_deps = [
+  libgiounix_dep,
+  libflatpak_dep,
+  libostree_dep,
+  libsoup_dep,
+]
+
+ipc_flatpak_service_src = gnome.gdbus_codegen('ipc-flatpak-service',
+           sources: 'org.gnome.Builder.Flatpak.Service.xml',
+  interface_prefix: 'org.gnome.Builder.',
+         namespace: 'Ipc',
+)
+
+ipc_flatpak_transfer_src = gnome.gdbus_codegen('ipc-flatpak-transfer',
+           sources: 'org.gnome.Builder.Flatpak.Transfer.xml',
+  interface_prefix: 'org.gnome.Builder.',
+         namespace: 'Ipc',
+)
+
+gnome_builder_flatpak_sources = [
+  'gnome-builder-flatpak.c',
+  'ipc-flatpak-service-impl.c',
+  ipc_flatpak_service_src,
+  ipc_flatpak_transfer_src,
+]
+
+gnome_builder_flatpak = executable('gnome-builder-flatpak', gnome_builder_flatpak_sources,
+           install: true,
+       install_dir: get_option('libexecdir'),
+      dependencies: gnome_builder_flatpak_deps,
+)
+
+test_flatpak_sources = [
+  'test-flatpak.c',
+  ipc_flatpak_service_src,
+  ipc_flatpak_transfer_src,
+]
+
+test_flatpak = executable('test-flatpak', 'test-flatpak.c', test_flatpak_sources,
+  dependencies: [ libgiounix_dep ],
+)
diff --git a/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Service.xml 
b/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Service.xml
new file mode 100644
index 000000000..c680e296b
--- /dev/null
+++ b/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Service.xml
@@ -0,0 +1,71 @@
+<!DOCTYPE node PUBLIC
+        "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"; >
+<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd";>
+  <!--
+    Copyright 2019 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/>.
+ 
+    SPDX-License-Identifier: GPL-3.0-or-later
+  -->
+  <interface name="org.gnome.Builder.Flatpak.Service">
+    <signal name="RuntimeAdded">
+      <!--
+        Param is (name, arch, branch, sdk_name, sdk_branch, is_sdk_extension)
+      -->
+      <arg name="runtime" direction="in" type="(sssssb)"/>
+    </signal>
+    <!--
+      AddInstallation:
+      @path: the path of the installation
+      @is_user: if the installation is a user installation
+
+      Adds an installation to the list of monitored installations.
+    -->
+    <method name="AddInstallation">
+      <arg name="path" direction="in" type="ay"/>
+      <arg name="is_user" direction="in" type="b"/>
+    </method>
+    <!--
+      ListRuntimes:
+
+      Lists the available runtimes for use as development.
+    -->
+    <method name="ListRuntimes">
+      <arg name="runtimes" direction="out" type="a(sssssb)"/>
+    </method>
+    <!--
+      RuntimeIsKnown:
+      @name: a runtime name to lookup in form name/arch/branch
+
+      Checks to see if any installation knows about the runtime.
+    -->
+    <method name="RuntimeIsKnown">
+      <arg name="name" direction="in" type="s"/>
+      <arg name="is_known" direction="out" type="b"/>
+      <arg name="download_size" direction="out" type="x"/>
+    </method>
+    <!--
+      Install:
+      @full_ref_name: the full name of the target to install such as "runtime/org.gnome.Sdk/x86_64/master"
+
+      Installs a ref from a configured remote.
+    -->
+    <method name="Install">
+      <arg name="full_ref_name" direction="in" type="s"/>
+      <arg name="transfer" direction="out" type="o"/>
+    </method>
+  </interface>
+</node>
diff --git a/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Transfer.xml 
b/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Transfer.xml
new file mode 100644
index 000000000..5a321f3a1
--- /dev/null
+++ b/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Transfer.xml
@@ -0,0 +1,30 @@
+<!DOCTYPE node PUBLIC
+        "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"; >
+<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd";>
+  <!--
+    Copyright 2019 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/>.
+ 
+    SPDX-License-Identifier: GPL-3.0-or-later
+  -->
+  <interface name="org.gnome.Builder.Flatpak.Transfer">
+    <property name="Fraction" type="d" access="read"/>
+    <property name="Message" type="s" access="read"/>
+    <signal name="Done"/>
+    <method name="Begin"/>
+    <method name="Cancel"/>
+  </interface>
+</node>
diff --git a/src/plugins/flatpak/daemon/test-flatpak.c b/src/plugins/flatpak/daemon/test-flatpak.c
new file mode 100644
index 000000000..47dea9123
--- /dev/null
+++ b/src/plugins/flatpak/daemon/test-flatpak.c
@@ -0,0 +1,175 @@
+/* test-flatpak.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib-unix.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "ipc-flatpak-service.h"
+
+static void
+on_runtime_added_cb (IpcFlatpakService *service,
+                     GVariant          *info)
+{
+  const gchar *name;
+  const gchar *arch;
+  const gchar *branch;
+  const gchar *sdk_name;
+  const gchar *sdk_branch;
+  gboolean sdk_extension;
+
+  g_assert (IPC_IS_FLATPAK_SERVICE (service));
+  g_assert (info != NULL);
+  g_assert (g_variant_is_of_type (info, G_VARIANT_TYPE ("(sssssb)")));
+
+  g_variant_get (info, "(&s&s&s&s&sb)",
+                 &name, &arch, &branch, &sdk_name, &sdk_branch, &sdk_extension);
+
+  if (!sdk_extension)
+    g_message ("Runtime Added: %s/%s/%s with SDK %s//%s",
+               name, arch, branch, sdk_name, sdk_branch);
+  else
+    g_message ("SDK Extension Added: %s/%s/%s with SDK %s//%s",
+               name, arch, branch, sdk_name, sdk_branch);
+}
+
+static void
+add_install_cb (GObject      *object,
+                GAsyncResult *result,
+                gpointer      user_data)
+{
+  IpcFlatpakService *service = (IpcFlatpakService *)object;
+  g_autofree gchar *sizestr = NULL;
+  g_autoptr(GMainLoop) main_loop = user_data;
+  g_autoptr(GVariant) runtimes = NULL;
+  g_autoptr(GError) error = NULL;
+  GVariantIter iter;
+  gboolean is_known = TRUE;
+  gboolean ret;
+  gint64 download_size = 0;
+
+  ret = ipc_flatpak_service_call_add_installation_finish (service, result, &error);
+  g_assert_no_error (error);
+  g_assert_true (ret);
+  g_message ("Installation added");
+
+  g_message ("Listing runtimes");
+  ret = ipc_flatpak_service_call_list_runtimes_sync (service, &runtimes, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_true (ret);
+
+  if (g_variant_iter_init (&iter, runtimes))
+    {
+      const gchar *name;
+      const gchar *arch;
+      const gchar *branch;
+      const gchar *sdk_name;
+      const gchar *sdk_branch;
+      gboolean sdk_extension;
+
+      while (g_variant_iter_next (&iter, "(&s&s&s&s&sb)", &name, &arch, &branch, &sdk_name, &sdk_branch, 
&sdk_extension))
+        g_message ("  %s/%s/%s with SDK %s//%s (Extension: %d)",
+                   name, arch, branch, sdk_name, sdk_branch, sdk_extension);
+    }
+
+  g_message ("Checking for a missing runtime");
+  ret = ipc_flatpak_service_call_runtime_is_known_sync (service, "me.hergert.FooBar/x86_64/master", 
&is_known, &download_size, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_true (ret);
+  g_assert_false (is_known);
+  g_message ("  Not found");
+
+  g_message ("Checking if org.gnome.Sdk/x86_64/40 is known");
+  ret = ipc_flatpak_service_call_runtime_is_known_sync (service, "org.gnome.Sdk/x86_64/40", &is_known, 
&download_size, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_true (ret);
+  g_assert_true (is_known);
+  sizestr = g_format_size (download_size);
+  g_message ("  Found, Download Size: <=%s", sizestr);
+
+  g_main_loop_quit (main_loop);
+}
+
+gint
+main (gint argc,
+      gchar *argv[])
+{
+  g_autofree gchar *home_install = g_build_filename (g_get_home_dir (), ".local", "share", "flatpak", NULL);
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GIOStream) stream = NULL;
+  g_autoptr(GInputStream) stdout_stream = NULL;
+  g_autoptr(GOutputStream) stdin_stream = NULL;
+  g_autoptr(GDBusConnection) connection = NULL;
+  g_autoptr(GSubprocess) subprocess = NULL;
+  g_autoptr(GSubprocessLauncher) launcher = NULL;
+  g_autoptr(IpcFlatpakService) service = NULL;
+  GMainLoop *main_loop;
+
+  launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE);
+  subprocess = g_subprocess_launcher_spawn (launcher, &error,
+#if 0
+                                            "valgrind", "--quiet",
+#endif
+                                            "./gnome-builder-flatpak", NULL);
+
+  if (subprocess == NULL)
+    g_error ("%s", error->message);
+
+  main_loop = g_main_loop_new (NULL, FALSE);
+  stdin_stream = g_subprocess_get_stdin_pipe (subprocess);
+  stdout_stream = g_subprocess_get_stdout_pipe (subprocess);
+  stream = g_simple_io_stream_new (stdout_stream, stdin_stream);
+  connection = g_dbus_connection_new_sync (stream,
+                                           NULL,
+                                           G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
+                                           NULL,
+                                           NULL,
+                                           &error);
+  g_assert_no_error (error);
+  g_assert_true (G_IS_DBUS_CONNECTION (connection));
+
+  g_dbus_connection_set_exit_on_close (connection, FALSE);
+  g_dbus_connection_start_message_processing (connection);
+
+  g_message ("Creating flatpak service proxy");
+  service = ipc_flatpak_service_proxy_new_sync (connection, 0, NULL, "/org/gnome/Builder/Flatpak", NULL, 
&error);
+  g_assert_no_error (error);
+  g_assert_true (IPC_IS_FLATPAK_SERVICE (service));
+
+  g_signal_connect (service,
+                    "runtime-added",
+                    G_CALLBACK (on_runtime_added_cb),
+                    NULL);
+
+  g_message ("Adding user installation to daemon");
+  ipc_flatpak_service_call_add_installation (service,
+                                             home_install,
+                                             TRUE,
+                                             NULL,
+                                             add_install_cb,
+                                             g_main_loop_ref (main_loop));
+
+  g_main_loop_run (main_loop);
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/plugins/flatpak/meson.build b/src/plugins/flatpak/meson.build
index 1637b04c7..42cca06f4 100644
--- a/src/plugins/flatpak/meson.build
+++ b/src/plugins/flatpak/meson.build
@@ -8,6 +8,12 @@ if not libportal_dep.found()
   error('plugin_flatpak requires access to libportal')
 endif
 
+libflatpak_dep = dependency('flatpak', version: '>= 1.0.0')
+libostree_dep = dependency('ostree-1')
+libsoup_dep = dependency('libsoup-2.4', version: '>= 2.52.0')
+
+subdir('daemon')
+
 plugins_sources += files([
   'flatpak-plugin.c',
   'gbp-flatpak-application-addin.c',
@@ -39,10 +45,10 @@ plugin_flatpak_resources = gnome.compile_resources(
 )
 
 plugins_deps += [
-  dependency('flatpak', version: '>= 0.8.0'),
-  dependency('ostree-1'),
-  dependency('libsoup-2.4', version: '>= 2.52.0'),
+  libflatpak_dep,
+  libostree_dep,
   libportal_dep,
+  libsoup_dep,
 ]
 
 plugins_sources += plugin_flatpak_resources


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]