[gnome-builder] flatpak: implement install RPC using transactions
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] flatpak: implement install RPC using transactions
- Date: Mon, 3 May 2021 20:44:28 +0000 (UTC)
commit 7396a3d8f212c480d10f3134a83d9c2fd611daf5
Author: Christian Hergert <chergert redhat com>
Date: Mon May 3 13:44:23 2021 -0700
flatpak: implement install RPC using transactions
.../flatpak/daemon/ipc-flatpak-service-impl.c | 234 ++++++++++++++++++++-
src/plugins/flatpak/daemon/meson.build | 10 +
.../daemon/org.gnome.Builder.Flatpak.Service.xml | 7 +-
.../daemon/org.gnome.Builder.Flatpak.Transfer.xml | 7 +-
src/plugins/flatpak/daemon/test-install.c | 133 ++++++++++++
5 files changed, 375 insertions(+), 16 deletions(-)
---
diff --git a/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c
b/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c
index 122d779a7..5c0d08499 100644
--- a/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c
+++ b/src/plugins/flatpak/daemon/ipc-flatpak-service-impl.c
@@ -23,8 +23,10 @@
#include "config.h"
#include <flatpak/flatpak.h>
+#include <glib/gi18n.h>
#include "ipc-flatpak-service-impl.h"
+#include "ipc-flatpak-transfer.h"
#include "ipc-flatpak-util.h"
typedef struct
@@ -65,6 +67,7 @@ struct _IpcFlatpakServiceImpl
IpcFlatpakServiceSkeleton parent;
GHashTable *installs;
GPtrArray *runtimes;
+ GPtrArray *installs_ordered;
};
static void ipc_flatpak_service_impl_install_changed_cb (IpcFlatpakServiceImpl *self,
@@ -196,6 +199,13 @@ add_runtime (IpcFlatpakServiceImpl *self,
ipc_flatpak_service_emit_runtime_added (IPC_FLATPAK_SERVICE (self), variant);
}
+static FlatpakInstallation *
+ipc_flatpak_service_impl_ref_user_installation (IpcFlatpakServiceImpl *self)
+{
+ Install *install = g_ptr_array_index (self->installs_ordered, 0);
+ return g_object_ref (install->installation);
+}
+
static void
runtime_free (Runtime *runtime)
{
@@ -353,6 +363,7 @@ add_installation (IpcFlatpakServiceImpl *self,
install_reload (self, install);
+ g_ptr_array_add (self->installs_ordered, install);
g_hash_table_insert (self->installs,
g_steal_pointer (&file),
g_steal_pointer (&install));
@@ -500,8 +511,6 @@ ipc_flatpak_service_impl_runtime_is_known (IpcFlatpakService *service,
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));
@@ -544,10 +553,11 @@ ipc_flatpak_service_impl_runtime_is_known (IpcFlatpakService *service,
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));
+ for (guint i = 0; i < self->installs_ordered->len; i++)
+ {
+ const Install *install = g_ptr_array_index (self->installs_ordered, i);
+ g_ptr_array_add (state->installs, g_object_ref (install->installation));
+ }
g_task_run_in_thread (task, is_known_worker);
@@ -581,22 +591,226 @@ ipc_flatpak_service_impl_install_changed_cb (IpcFlatpakServiceImpl *self,
}
}
+typedef struct
+{
+ FlatpakInstallation *installation;
+ GDBusMethodInvocation *invocation;
+ IpcFlatpakTransfer *transfer;
+ char *remote;
+ char *ref;
+ char *parent_window;
+} InstallState;
+
+static void
+install_state_free (InstallState *state)
+{
+ g_clear_object (&state->installation);
+ g_clear_object (&state->invocation);
+ g_clear_object (&state->transfer);
+ g_clear_pointer (&state->remote, g_free);
+ g_clear_pointer (&state->ref, g_free);
+ g_clear_pointer (&state->parent_window, g_free);
+ g_slice_free (InstallState, state);
+}
+
+static void
+on_progress_changed (FlatpakTransactionProgress *progress,
+ IpcFlatpakTransfer *transfer)
+{
+ g_autofree char *message = NULL;
+ int val;
+
+ g_assert (FLATPAK_IS_TRANSACTION_PROGRESS (progress));
+ g_assert (IPC_IS_FLATPAK_TRANSFER (transfer));
+
+ val = flatpak_transaction_progress_get_progress (progress);
+ message = flatpak_transaction_progress_get_status (progress);
+
+ ipc_flatpak_transfer_set_message (transfer, message);
+ ipc_flatpak_transfer_set_fraction (transfer, (double)val / 100.0);
+}
+
+static void
+on_new_operation_cb (FlatpakTransaction *transaction,
+ FlatpakTransactionOperation *operation,
+ FlatpakTransactionProgress *progress,
+ IpcFlatpakTransfer *transfer)
+{
+ g_assert (FLATPAK_IS_TRANSACTION (transaction));
+ g_assert (FLATPAK_IS_TRANSACTION_OPERATION (operation));
+ g_assert (FLATPAK_IS_TRANSACTION_PROGRESS (progress));
+ g_assert (IPC_IS_FLATPAK_TRANSFER (transfer));
+
+ g_signal_connect_object (progress,
+ "changed",
+ G_CALLBACK (on_progress_changed),
+ transfer,
+ 0);
+ on_progress_changed (progress, transfer);
+}
+
+static gboolean
+connect_signals (FlatpakTransaction *transaction,
+ IpcFlatpakTransfer *transfer,
+ GError **error)
+{
+ g_signal_connect_object (transaction,
+ "new-operation",
+ G_CALLBACK (on_new_operation_cb),
+ transfer,
+ 0);
+ return TRUE;
+}
+
+static void
+install_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ InstallState *state = task_data;
+ g_autoptr(FlatpakTransaction) transaction = NULL;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (source_object));
+ g_assert (state != NULL);
+ g_assert (state->remote != NULL);
+ g_assert (G_IS_DBUS_METHOD_INVOCATION (state->invocation));
+ g_assert (IPC_IS_FLATPAK_TRANSFER (state->transfer));
+ g_assert (FLATPAK_IS_INSTALLATION (state->installation));
+
+ ipc_flatpak_transfer_set_fraction (state->transfer, 0.0);
+ ipc_flatpak_transfer_set_message (state->transfer, "");
+
+ if (!(transaction = flatpak_transaction_new_for_installation (state->installation, NULL, &error)) ||
+ !flatpak_transaction_add_install (transaction, state->remote, state->ref, NULL, &error) ||
+ !connect_signals (transaction, state->transfer, &error) ||
+ !flatpak_transaction_run (transaction, cancellable, &error))
+ {
+ ipc_flatpak_transfer_set_fraction (state->transfer, 1.0);
+ ipc_flatpak_transfer_set_message (state->transfer, _("Installation failed"));
+ complete_wrapped_error (g_steal_pointer (&state->invocation), g_steal_pointer (&error));
+ }
+ else
+ {
+ ipc_flatpak_transfer_set_fraction (state->transfer, 1.0);
+ ipc_flatpak_transfer_set_message (state->transfer, _("Installation complete"));
+ ipc_flatpak_service_complete_install (source_object, g_steal_pointer (&state->invocation));
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+refs_equal (FlatpakRef *a,
+ FlatpakRef *b)
+{
+ return (str_equal0 (flatpak_ref_get_name (a),
+ flatpak_ref_get_name (b)) &&
+ str_equal0 (flatpak_ref_get_arch (a),
+ flatpak_ref_get_arch (b)) &&
+ str_equal0 (flatpak_ref_get_branch (a),
+ flatpak_ref_get_branch (b)));
+}
+
+static char *
+find_remote_for_ref (IpcFlatpakServiceImpl *self,
+ FlatpakRef *ref)
+{
+ g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
+ g_assert (FLATPAK_IS_REF (ref));
+
+ /* Someday we might want to prompt the user for which remote to install from,
+ * but for now we'll just take the first.
+ */
+
+ for (guint i = 0; i < self->installs_ordered->len; i++)
+ {
+ Install *install = g_ptr_array_index (self->installs_ordered, i);
+ g_autoptr(GPtrArray) remotes = flatpak_installation_list_remotes (install->installation, NULL, NULL);
+
+ if (remotes == NULL)
+ continue;
+
+ for (guint j = 0; j < remotes->len; j++)
+ {
+ FlatpakRemote *remote = g_ptr_array_index (remotes, j);
+ const char *name = flatpak_remote_get_name (remote);
+ g_autoptr(GPtrArray) refs = flatpak_installation_list_remote_refs_sync_full
(install->installation, name,
+
FLATPAK_QUERY_FLAGS_ONLY_CACHED,
+ NULL, NULL);
+
+ if (refs == NULL)
+ continue;
+
+ for (guint k = 0; k < refs->len; k++)
+ {
+ FlatpakRef *remote_ref = g_ptr_array_index (refs, k);
+
+ if (refs_equal (ref, remote_ref))
+ return g_strdup (name);
+ }
+ }
+ }
+
+ return NULL;
+}
+
static gboolean
ipc_flatpak_service_impl_install (IpcFlatpakService *service,
GDBusMethodInvocation *invocation,
- const gchar *full_ref_name)
+ const gchar *full_ref_name,
+ const gchar *transfer_path,
+ const gchar *parent_window)
{
+ IpcFlatpakServiceImpl *self = (IpcFlatpakServiceImpl *)service;
+ g_autoptr(IpcFlatpakTransfer) transfer = NULL;
g_autoptr(FlatpakRef) ref = NULL;
g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = NULL;
+ g_autofree char *remote = NULL;
+ GDBusConnection *connection;
+ InstallState *state;
- g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (service));
+ g_assert (IPC_IS_FLATPAK_SERVICE_IMPL (self));
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
g_assert (full_ref_name != NULL);
+ g_assert (transfer_path != NULL);
+ g_assert (parent_window != NULL);
if (!(ref = flatpak_ref_parse (full_ref_name, &error)))
return complete_wrapped_error (invocation, error);
- ipc_flatpak_service_complete_install (service, invocation, "");
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ remote = find_remote_for_ref (self, ref);
+ transfer = ipc_flatpak_transfer_proxy_new_sync (connection,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ transfer_path,
+ NULL, NULL);
+
+ if (remote == NULL)
+ {
+ g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "No configured remote contains ref");
+ return TRUE;
+ }
+
+ state = g_slice_new0 (InstallState);
+ state->installation = ipc_flatpak_service_impl_ref_user_installation (self);
+ state->invocation = g_steal_pointer (&invocation);
+ state->ref = g_strdup (full_ref_name);
+ state->remote = g_steal_pointer (&remote);
+ state->parent_window = parent_window[0] ? g_strdup (parent_window) : NULL;
+ state->transfer = g_object_ref (transfer);
+
+ task = g_task_new (self, NULL, NULL, NULL);
+ g_task_set_source_tag (task, ipc_flatpak_service_impl_install);
+ g_task_set_task_data (task, state, (GDestroyNotify)install_state_free);
+ g_task_run_in_thread (task, install_worker);
return TRUE;
}
@@ -930,6 +1144,7 @@ ipc_flatpak_service_impl_finalize (GObject *object)
{
IpcFlatpakServiceImpl *self = (IpcFlatpakServiceImpl *)object;
+ g_clear_pointer (&self->installs_ordered, g_ptr_array_unref);
g_clear_pointer (&self->installs, g_hash_table_unref);
g_clear_pointer (&self->runtimes, g_ptr_array_unref);
@@ -948,6 +1163,7 @@ ipc_flatpak_service_impl_class_init (IpcFlatpakServiceImplClass *klass)
static void
ipc_flatpak_service_impl_init (IpcFlatpakServiceImpl *self)
{
+ self->installs_ordered = g_ptr_array_new ();
self->installs = g_hash_table_new_full (g_file_hash,
(GEqualFunc) g_file_equal,
g_object_unref,
diff --git a/src/plugins/flatpak/daemon/meson.build b/src/plugins/flatpak/daemon/meson.build
index 49e7217e5..14c95654e 100644
--- a/src/plugins/flatpak/daemon/meson.build
+++ b/src/plugins/flatpak/daemon/meson.build
@@ -36,6 +36,12 @@ test_flatpak_sources = [
ipc_flatpak_transfer_src,
]
+test_install_sources = [
+ 'test-install.c',
+ ipc_flatpak_service_src,
+ ipc_flatpak_transfer_src,
+]
+
plugins_sources += [
ipc_flatpak_service_src,
ipc_flatpak_transfer_src,
@@ -44,3 +50,7 @@ plugins_sources += [
test_flatpak = executable('test-flatpak', 'test-flatpak.c', test_flatpak_sources,
dependencies: [ libgiounix_dep ],
)
+
+test_install = executable('test-install', 'test-install.c', test_install_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
index 6ef120fdf..829852f42 100644
--- a/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Service.xml
+++ b/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Service.xml
@@ -64,12 +64,15 @@
<!--
Install:
@full_ref_name: the full name of the target to install such as "runtime/org.gnome.Sdk/x86_64/master"
+ @transfer: the path of an org.gnome.Builder.Flatpak.Transfer object on the caller side
- Installs a ref from a configured remote.
+ Begins the installation of a ref from a configured remote. The operation will not
+ complete until the installation has completed.
-->
<method name="Install">
<arg name="full_ref_name" direction="in" type="s"/>
- <arg name="transfer" direction="out" type="o"/>
+ <arg name="transfer" direction="in" type="o"/>
+ <arg name="parent_window" direction="in" type="s"/>
</method>
<!--
ResolveExtension:
diff --git a/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Transfer.xml
b/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Transfer.xml
index 5a321f3a1..4f5762c87 100644
--- a/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Transfer.xml
+++ b/src/plugins/flatpak/daemon/org.gnome.Builder.Flatpak.Transfer.xml
@@ -21,10 +21,7 @@
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"/>
+ <property name="Fraction" type="d" access="readwrite"/>
+ <property name="Message" type="s" access="readwrite"/>
</interface>
</node>
diff --git a/src/plugins/flatpak/daemon/test-install.c b/src/plugins/flatpak/daemon/test-install.c
new file mode 100644
index 000000000..e8cb52447
--- /dev/null
+++ b/src/plugins/flatpak/daemon/test-install.c
@@ -0,0 +1,133 @@
+/* test-install.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"
+#include "ipc-flatpak-transfer.h"
+
+static void
+install_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IpcFlatpakService *service = (IpcFlatpakService *)object;
+ g_autoptr(GMainLoop) main_loop = user_data;
+ g_autoptr(GVariant) runtimes = NULL;
+ g_autoptr(GError) error = NULL;
+ gboolean ret;
+
+ ret = ipc_flatpak_service_call_install_finish (service, result, &error);
+ g_assert_no_error (error);
+ g_assert_true (ret);
+ g_message ("Installed.");
+
+ g_main_loop_quit (main_loop);
+}
+
+static void
+print_info (IpcFlatpakTransfer *transfer,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ g_print ("%s: %lf\n",
+ ipc_flatpak_transfer_get_message (transfer) ?: "",
+ ipc_flatpak_transfer_get_fraction (transfer));
+}
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ static const char *transfer_path = "/org/gnome/Builder/Flatpak/Transfer/0";
+ 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;
+ g_autoptr(IpcFlatpakTransfer) transfer = NULL;
+ GMainLoop *main_loop;
+ gboolean ret;
+
+ if (argc != 2)
+ {
+ g_printerr ("usage: %s REF\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ 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));
+
+ transfer = ipc_flatpak_transfer_skeleton_new ();
+ g_signal_connect (transfer, "notify::message", G_CALLBACK (print_info), NULL);
+ g_signal_connect (transfer, "notify::fraction", G_CALLBACK (print_info), NULL);
+ ret = g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (transfer), connection, transfer_path,
&error);
+ g_assert_no_error (error);
+ g_assert_true (ret);
+
+ g_message ("Installing %s\n", argv[1]);
+ ipc_flatpak_service_call_install (service,
+ argv[1],
+ transfer_path,
+ "",
+ NULL,
+ install_cb,
+ g_main_loop_ref (main_loop));
+
+ g_main_loop_run (main_loop);
+
+ return EXIT_SUCCESS;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]