6770 lines
242 KiB
Diff
6770 lines
242 KiB
Diff
at commit 786bab5f1928920251ee9e93d51953ff9dd9faa4
|
||
Date: Mon Sep 1 18:23:09 2025 +0200
|
||
|
||
diff --git a/meson_options.txt b/meson_options.txt
|
||
index cd49cf529..27fa15e8b 100644
|
||
--- a/meson_options.txt
|
||
+++ b/meson_options.txt
|
||
@@ -3,6 +3,7 @@ option('installed_tests', type : 'boolean', value : false, description : 'enable
|
||
option('man', type : 'boolean', value : true, description : 'enable man pages')
|
||
option('packagekit', type : 'boolean', value : true, description : 'enable PackageKit support')
|
||
option('packagekit_autoremove', type : 'boolean', value : false, description : 'autoremove packages in PackageKit')
|
||
+option('dnf5', type : 'boolean', value : true, description : 'enable dnf5 support')
|
||
option('polkit', type : 'boolean', value : true, description : 'enable PolKit support')
|
||
option('eos_updater', type : 'boolean', value : false, description : 'enable eos-updater support')
|
||
option('fwupd', type : 'boolean', value : true, description : 'enable fwupd support')
|
||
diff --git a/plugins/dnf5/gs-dnf5-progress-helper.c b/plugins/dnf5/gs-dnf5-progress-helper.c
|
||
new file mode 100644
|
||
index 000000000..cd8b861df
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/gs-dnf5-progress-helper.c
|
||
@@ -0,0 +1,552 @@
|
||
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
|
||
+ *
|
||
+ * Copyright (C) 2024 Red Hat <www.redhat.com>
|
||
+ *
|
||
+ * SPDX-License-Identifier: GPL-2.0+
|
||
+ */
|
||
+
|
||
+#include <config.h>
|
||
+
|
||
+#include <gnome-software.h>
|
||
+
|
||
+#include "gs-dnf5-generated.h"
|
||
+
|
||
+#include "gs-dnf5-progress-helper.h"
|
||
+
|
||
+struct _GsDnf5ProgressHelper
|
||
+{
|
||
+ GObject parent;
|
||
+
|
||
+ GsDnf5RpmRpm *rpm_proxy; /* (owned) */
|
||
+ GsDnf5Base *base_proxy; /* (owned) */
|
||
+ GsApp *app; /* (owned) (nullable) */
|
||
+ GsAppList *list; /* (owned) (nullable) */
|
||
+ gchar *session_object_path; /* (owned) */
|
||
+ GHashTable *ongoing_downloads; /* (owned); gchar * (download_id) ~> DownloadInfo * */
|
||
+ GsAppState recover_download_state;
|
||
+ guint64 to_download_total;
|
||
+ guint64 downloaded_total;
|
||
+
|
||
+ gulong download_add_new_id;
|
||
+ gulong download_progress_id;
|
||
+ gulong download_mirror_failure_id;
|
||
+ gulong download_end_id;
|
||
+ gulong transaction_before_begin_id;
|
||
+ gulong transaction_after_complete_id;
|
||
+ gulong transaction_elem_progress_id;
|
||
+ gulong transaction_action_progress_id;
|
||
+ gulong transaction_action_start_id;
|
||
+ gulong transaction_action_stop_id;
|
||
+ gulong transaction_script_error_id;
|
||
+ gulong transaction_script_start_id;
|
||
+ gulong transaction_script_stop_id;
|
||
+ gulong transaction_transaction_progress_id;
|
||
+ gulong transaction_transaction_start_id;
|
||
+ gulong transaction_transaction_stop_id;
|
||
+ gulong transaction_unpack_error_id;
|
||
+ gulong transaction_verify_progress_id;
|
||
+ gulong transaction_verify_start_id;
|
||
+ gulong transaction_verify_stop_id;
|
||
+};
|
||
+
|
||
+G_DEFINE_TYPE (GsDnf5ProgressHelper, gs_dnf5_progress_helper, G_TYPE_OBJECT)
|
||
+
|
||
+static void
|
||
+gs_dnf5_helper_set_progress (GsDnf5ProgressHelper *self,
|
||
+ guint progress)
|
||
+{
|
||
+ if (self->app != NULL)
|
||
+ gs_app_set_progress (self->app, progress);
|
||
+ if (self->list != NULL)
|
||
+ gs_app_list_override_progress (self->list, progress);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_update_download_progress (GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (self->to_download_total > 0)
|
||
+ gs_dnf5_helper_set_progress (self, 100 * self->downloaded_total / self->to_download_total);
|
||
+}
|
||
+
|
||
+typedef struct {
|
||
+ guint64 downloaded;
|
||
+ guint64 total;
|
||
+} DownloadInfo;
|
||
+
|
||
+static void
|
||
+gs_dnf5_download_add_new_cb (GsDnf5Base *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_download_id,
|
||
+ const gchar *arg_description,
|
||
+ gint64 arg_total_to_download,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ DownloadInfo *download_info;
|
||
+
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: download_id:'%s' description:'%s' total_to_download:%" G_GINT64_FORMAT,
|
||
+ G_STRFUNC, arg_download_id, arg_description, arg_total_to_download);
|
||
+ g_return_if_fail (arg_download_id != NULL);
|
||
+
|
||
+ if (arg_total_to_download <= 0)
|
||
+ return;
|
||
+
|
||
+ if (g_hash_table_size (self->ongoing_downloads) == 0) {
|
||
+ if (self->app != NULL) {
|
||
+ self->recover_download_state = gs_app_get_state (self->app);
|
||
+ gs_app_set_state (self->app, GS_APP_STATE_DOWNLOADING);
|
||
+ }
|
||
+ self->to_download_total = 0;
|
||
+ self->downloaded_total = 0;
|
||
+ }
|
||
+
|
||
+ download_info = g_new0 (DownloadInfo, 1);
|
||
+ download_info->total = arg_total_to_download;
|
||
+
|
||
+ g_hash_table_insert (self->ongoing_downloads, g_strdup (arg_download_id), download_info);
|
||
+ self->to_download_total += arg_total_to_download;
|
||
+
|
||
+ if (self->app != NULL)
|
||
+ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total);
|
||
+ gs_dnf5_update_download_progress (self);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_download_end_cb (GsDnf5Base *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_download_id,
|
||
+ guint arg_transfer_status,
|
||
+ const gchar *arg_message,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ DownloadInfo *download_info;
|
||
+
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: download_id:'%s' transfer_status:%u message:'%s'",
|
||
+ G_STRFUNC, arg_download_id, arg_transfer_status, arg_message);
|
||
+ g_return_if_fail (arg_download_id != NULL);
|
||
+
|
||
+ download_info = g_hash_table_lookup (self->ongoing_downloads, arg_download_id);
|
||
+ if (download_info != NULL) {
|
||
+ self->downloaded_total -= download_info->downloaded;
|
||
+ self->downloaded_total += download_info->total;
|
||
+
|
||
+ /* remove after being done with the `download_info`, because it frees it */
|
||
+ g_hash_table_remove (self->ongoing_downloads, arg_download_id);
|
||
+ download_info = NULL;
|
||
+
|
||
+ /* it's the last package to be downloaded */
|
||
+ if (g_hash_table_size (self->ongoing_downloads) == 0) {
|
||
+ gs_dnf5_helper_set_progress (self, GS_APP_PROGRESS_UNKNOWN);
|
||
+ if (self->app != NULL) {
|
||
+ gs_app_set_state (self->app, self->recover_download_state);
|
||
+ gs_app_set_size_download (self->app, GS_SIZE_TYPE_UNKNOWN, 0);
|
||
+ }
|
||
+ } else {
|
||
+ gs_dnf5_update_download_progress (self);
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_download_mirror_failure_cb (GsDnf5Base *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_download_id,
|
||
+ const gchar *arg_message,
|
||
+ const gchar *arg_url,
|
||
+ const gchar *arg_metadata,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: download_id:'%s' message:'%s' url:'%s' metadata:'%s'",
|
||
+ G_STRFUNC, arg_download_id, arg_message, arg_url, arg_metadata);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_download_progress_cb (GsDnf5Base *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_download_id,
|
||
+ gint64 arg_total_to_download,
|
||
+ gint64 arg_downloaded,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ DownloadInfo *download_info;
|
||
+
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: download_id:'%s' total_to_download:%" G_GINT64_FORMAT " downloaded:%" G_GINT64_FORMAT,
|
||
+ G_STRFUNC, arg_download_id, arg_total_to_download, arg_downloaded);
|
||
+
|
||
+ download_info = g_hash_table_lookup (self->ongoing_downloads, arg_download_id);
|
||
+ if (download_info != NULL) {
|
||
+ self->downloaded_total += arg_downloaded - download_info->downloaded;
|
||
+ download_info->downloaded = arg_downloaded;
|
||
+ if (download_info->downloaded != (guint64) arg_total_to_download) {
|
||
+ self->to_download_total -= download_info->total;
|
||
+ download_info->total = arg_total_to_download;
|
||
+ self->to_download_total += download_info->total;
|
||
+ if (self->app != NULL)
|
||
+ gs_app_set_size_download (self->app, GS_SIZE_TYPE_VALID, self->to_download_total);
|
||
+ }
|
||
+ gs_dnf5_update_download_progress (self);
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_before_begin_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ gs_dnf5_helper_set_progress (self, 0);
|
||
+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_after_complete_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ gboolean arg_success,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: success:%d", G_STRFUNC, arg_success);
|
||
+ gs_dnf5_helper_set_progress (self, 100);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_elem_progress_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_nevra,
|
||
+ guint64 arg_processed,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: nevra:'%s' progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_processed, arg_total);
|
||
+ if (arg_total != 0)
|
||
+ gs_dnf5_helper_set_progress (self, arg_processed * 100 / arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_action_progress_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_nevra,
|
||
+ guint64 arg_processed,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: nevra:'%s' progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_processed, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_action_start_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_nevra,
|
||
+ guint arg_action,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: nevra:'%s' action:%u total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_action, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_action_stop_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_nevra,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: nevra:'%s' total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_script_error_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_nevra,
|
||
+ guint arg_scriptlet_type,
|
||
+ guint64 arg_return_code,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: nevra:'%s' scriptlet-type:%u return-code:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_scriptlet_type, arg_return_code);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_script_start_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_nevra,
|
||
+ guint arg_scriptlet_type,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: nevra:'%s' scriptlet-type:%u", G_STRFUNC, arg_nevra, arg_scriptlet_type);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_script_stop_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_nevra,
|
||
+ guint arg_scriptlet_type,
|
||
+ guint64 arg_return_code,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: nevra:'%s' scriptlet-type:%u return-code:%" G_GUINT64_FORMAT, G_STRFUNC, arg_nevra, arg_scriptlet_type, arg_return_code);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_transaction_progress_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ guint64 arg_processed,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_processed, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_transaction_start_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_transaction_stop_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_unpack_error_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_nevra,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: nevra:'%s'", G_STRFUNC, arg_nevra);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_verify_progress_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ guint64 arg_processed,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: progress:%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, G_STRFUNC, arg_processed, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_verify_start_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_verify_stop_cb (GsDnf5RpmRpm *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ guint64 arg_total,
|
||
+ GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ if (g_strcmp0 (self->session_object_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+ g_debug ("%s: total:%" G_GUINT64_FORMAT, G_STRFUNC, arg_total);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_progress_helper_dispose (GObject *object)
|
||
+{
|
||
+ GsDnf5ProgressHelper *self = GS_DNF5_PROGRESS_HELPER (object);
|
||
+ GObject *base_proxy = G_OBJECT (self->base_proxy);
|
||
+ GObject *rpm_proxy = G_OBJECT (self->rpm_proxy);
|
||
+
|
||
+ #define clear_signal_handler(_proxy, _id) G_STMT_START { \
|
||
+ if (_id) { \
|
||
+ g_signal_handler_disconnect (_proxy, _id); \
|
||
+ _id = 0; \
|
||
+ } \
|
||
+ } G_STMT_END
|
||
+
|
||
+ clear_signal_handler (base_proxy, self->download_add_new_id);
|
||
+ clear_signal_handler (base_proxy, self->download_progress_id);
|
||
+ clear_signal_handler (base_proxy, self->download_mirror_failure_id);
|
||
+ clear_signal_handler (base_proxy, self->download_end_id);
|
||
+
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_before_begin_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_after_complete_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_elem_progress_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_action_progress_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_action_start_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_action_stop_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_script_error_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_script_start_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_script_stop_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_transaction_progress_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_transaction_start_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_transaction_stop_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_unpack_error_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_verify_progress_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_verify_start_id);
|
||
+ clear_signal_handler (rpm_proxy, self->transaction_verify_stop_id);
|
||
+
|
||
+ #undef clear_signal_handler
|
||
+
|
||
+ gs_dnf5_helper_set_progress (self, GS_APP_PROGRESS_UNKNOWN);
|
||
+
|
||
+ G_OBJECT_CLASS (gs_dnf5_progress_helper_parent_class)->dispose (object);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_progress_helper_finalize (GObject *object)
|
||
+{
|
||
+ GsDnf5ProgressHelper *self = GS_DNF5_PROGRESS_HELPER (object);
|
||
+
|
||
+ g_clear_object (&self->base_proxy);
|
||
+ g_clear_object (&self->rpm_proxy);
|
||
+ g_clear_object (&self->app);
|
||
+ g_clear_object (&self->list);
|
||
+ g_clear_pointer (&self->session_object_path, g_free);
|
||
+ g_clear_pointer (&self->ongoing_downloads, g_hash_table_destroy);
|
||
+
|
||
+ G_OBJECT_CLASS (gs_dnf5_progress_helper_parent_class)->finalize (object);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_progress_helper_class_init (GsDnf5ProgressHelperClass *klass)
|
||
+{
|
||
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
+
|
||
+ object_class->dispose = gs_dnf5_progress_helper_dispose;
|
||
+ object_class->finalize = gs_dnf5_progress_helper_finalize;
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_progress_helper_init (GsDnf5ProgressHelper *self)
|
||
+{
|
||
+ self->ongoing_downloads = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||
+}
|
||
+
|
||
+GsDnf5ProgressHelper *
|
||
+gs_dnf5_progress_helper_new (GsDnf5Base *base_proxy,
|
||
+ GsDnf5RpmRpm *rpm_proxy,
|
||
+ GsApp *progress_app,
|
||
+ GsAppList *progress_list,
|
||
+ const gchar *session_object_path)
|
||
+{
|
||
+ GsDnf5ProgressHelper *self;
|
||
+
|
||
+ g_return_val_if_fail (GS_DNF5_IS_BASE (base_proxy), NULL);
|
||
+ g_return_val_if_fail (GS_DNF5_IS_RPM_RPM (rpm_proxy), NULL);
|
||
+ g_return_val_if_fail (GS_IS_APP (progress_app) || GS_IS_APP_LIST (progress_list), NULL);
|
||
+ g_return_val_if_fail (session_object_path != NULL, NULL);
|
||
+
|
||
+ self = g_object_new (GS_TYPE_DNF5_PROGRESS_HELPER, NULL);
|
||
+ self->base_proxy = g_object_ref (base_proxy);
|
||
+ self->rpm_proxy = g_object_ref (rpm_proxy);
|
||
+ self->app = progress_app ? g_object_ref (progress_app) : NULL;
|
||
+ self->list = progress_list ? g_object_ref (progress_list) : NULL;
|
||
+ self->session_object_path = g_strdup (session_object_path);
|
||
+
|
||
+ gs_dnf5_helper_set_progress (self, GS_APP_PROGRESS_UNKNOWN);
|
||
+
|
||
+ self->download_add_new_id =
|
||
+ g_signal_connect_object (base_proxy, "download_add_new",
|
||
+ G_CALLBACK (gs_dnf5_download_add_new_cb), self, 0);
|
||
+ self->download_progress_id =
|
||
+ g_signal_connect_object (base_proxy, "download_progress",
|
||
+ G_CALLBACK (gs_dnf5_download_progress_cb), self, 0);
|
||
+ self->download_mirror_failure_id =
|
||
+ g_signal_connect_object (base_proxy, "download_mirror_failure",
|
||
+ G_CALLBACK (gs_dnf5_download_mirror_failure_cb), self, 0);
|
||
+ self->download_end_id =
|
||
+ g_signal_connect_object (base_proxy, "download_end",
|
||
+ G_CALLBACK (gs_dnf5_download_end_cb), self, 0);
|
||
+
|
||
+ self->transaction_before_begin_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_before_begin",
|
||
+ G_CALLBACK (gs_dnf5_transaction_before_begin_cb), self, 0);
|
||
+ self->transaction_after_complete_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_after_complete",
|
||
+ G_CALLBACK (gs_dnf5_transaction_after_complete_cb), self, 0);
|
||
+ self->transaction_elem_progress_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_elem_progress",
|
||
+ G_CALLBACK (gs_dnf5_transaction_elem_progress_cb), self, 0);
|
||
+ self->transaction_action_progress_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_action_progress",
|
||
+ G_CALLBACK (gs_dnf5_transaction_action_progress_cb), self, 0);
|
||
+ self->transaction_action_start_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_action_start",
|
||
+ G_CALLBACK (gs_dnf5_transaction_action_start_cb), self, 0);
|
||
+ self->transaction_action_stop_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_action_stop",
|
||
+ G_CALLBACK (gs_dnf5_transaction_action_stop_cb), self, 0);
|
||
+ self->transaction_script_error_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_script_error",
|
||
+ G_CALLBACK (gs_dnf5_transaction_script_error_cb), self, 0);
|
||
+ self->transaction_script_start_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_script_start",
|
||
+ G_CALLBACK (gs_dnf5_transaction_script_start_cb), self, 0);
|
||
+ self->transaction_script_stop_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_script_stop",
|
||
+ G_CALLBACK (gs_dnf5_transaction_script_stop_cb), self, 0);
|
||
+ self->transaction_transaction_progress_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_transaction_progress",
|
||
+ G_CALLBACK (gs_dnf5_transaction_transaction_progress_cb), self, 0);
|
||
+ self->transaction_transaction_start_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_transaction_start",
|
||
+ G_CALLBACK (gs_dnf5_transaction_transaction_start_cb), self, 0);
|
||
+ self->transaction_transaction_stop_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_transaction_stop",
|
||
+ G_CALLBACK (gs_dnf5_transaction_transaction_stop_cb), self, 0);
|
||
+ self->transaction_unpack_error_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_unpack_error",
|
||
+ G_CALLBACK (gs_dnf5_transaction_unpack_error_cb), self, 0);
|
||
+ self->transaction_verify_progress_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_verify_progress",
|
||
+ G_CALLBACK (gs_dnf5_transaction_verify_progress_cb), self, 0);
|
||
+ self->transaction_verify_start_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_verify_start",
|
||
+ G_CALLBACK (gs_dnf5_transaction_verify_start_cb), self, 0);
|
||
+ self->transaction_verify_stop_id =
|
||
+ g_signal_connect_object (rpm_proxy, "transaction_verify_stop",
|
||
+ G_CALLBACK (gs_dnf5_transaction_verify_stop_cb), self, 0);
|
||
+
|
||
+ return self;
|
||
+}
|
||
diff --git a/plugins/dnf5/gs-dnf5-progress-helper.h b/plugins/dnf5/gs-dnf5-progress-helper.h
|
||
new file mode 100644
|
||
index 000000000..5d522468c
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/gs-dnf5-progress-helper.h
|
||
@@ -0,0 +1,31 @@
|
||
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
|
||
+ *
|
||
+ * Copyright (C) 2024 Red Hat <www.redhat.com>
|
||
+ *
|
||
+ * SPDX-License-Identifier: GPL-2.0+
|
||
+ */
|
||
+
|
||
+#pragma once
|
||
+
|
||
+#include <glib.h>
|
||
+#include <glib-object.h>
|
||
+
|
||
+#include <gnome-software.h>
|
||
+
|
||
+#include "gs-dnf5-generated.h"
|
||
+
|
||
+G_BEGIN_DECLS
|
||
+
|
||
+#define GS_TYPE_DNF5_PROGRESS_HELPER (gs_dnf5_progress_helper_get_type ())
|
||
+
|
||
+G_DECLARE_FINAL_TYPE (GsDnf5ProgressHelper, gs_dnf5_progress_helper, GS, DNF5_PROGRESS_HELPER, GObject)
|
||
+
|
||
+GsDnf5ProgressHelper *
|
||
+ gs_dnf5_progress_helper_new (GsDnf5Base *base_proxy,
|
||
+ GsDnf5RpmRpm *rpm_proxy,
|
||
+ GsApp *progress_app,
|
||
+ GsAppList *progress_list,
|
||
+ const gchar *session_object_path);
|
||
+
|
||
+G_END_DECLS
|
||
diff --git a/plugins/dnf5/gs-plugin-dnf5.c b/plugins/dnf5/gs-plugin-dnf5.c
|
||
new file mode 100644
|
||
index 000000000..82f3e19a8
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/gs-plugin-dnf5.c
|
||
@@ -0,0 +1,4589 @@
|
||
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
|
||
+ *
|
||
+ * Copyright (C) 2024 Red Hat <www.redhat.com>
|
||
+ *
|
||
+ * SPDX-License-Identifier: GPL-2.0+
|
||
+ */
|
||
+
|
||
+/*
|
||
+ * SECTION: dnf5 plugin.
|
||
+ *
|
||
+ * Talks to dnf5 to manage packages through dnf.
|
||
+ */
|
||
+
|
||
+#include <config.h>
|
||
+
|
||
+#include <glib/gi18n-lib.h>
|
||
+#include <gnome-software.h>
|
||
+
|
||
+#include "gs-app-private.h"
|
||
+
|
||
+#include "gs-worker-thread.h"
|
||
+#include "gs-dnf5-generated.h"
|
||
+#include "gs-dnf5-rpm-generated.h"
|
||
+#include "gs-dnf5-progress-helper.h"
|
||
+
|
||
+#include "gs-plugin-dnf5.h"
|
||
+
|
||
+#include "../packagekit/gs-markdown.h"
|
||
+
|
||
+#define GS_DNF5_RELEASEVER_DEFAULT NULL
|
||
+#define GS_DNF5_INTERFACE_RPM_DNF "org.rpm.dnf.v0"
|
||
+#define GS_DNF5_OBJECT_PATH_RPM_DNF "/org/rpm/dnf/v0"
|
||
+
|
||
+#define GS_DNF5_ADVISORIES_KEY "gs-dnf5::advisories"
|
||
+#define GS_DNF5_CHANGELOGS_KEY "gs-dnf5::changelogs"
|
||
+
|
||
+/* for how long a session can be left opened until it's auto-closed */
|
||
+#define GS_SESSION_LIFETIME_SECS (5 * 60)
|
||
+
|
||
+struct _GsPluginDnf5
|
||
+{
|
||
+ GsPlugin parent;
|
||
+
|
||
+ GsWorkerThread *worker; /* (owned) */
|
||
+ GDBusConnection *connection;
|
||
+ GsDnf5RpmTransaction *rpm_transaction_proxy;
|
||
+ guint rpm_transaction_watch_id;
|
||
+ gint calling_rpm;
|
||
+
|
||
+ struct _SessionData {
|
||
+ GMutex mutex;
|
||
+ GCond cond;
|
||
+ guint autoclose_timer;
|
||
+ GsDnf5SessionManager *proxy;
|
||
+ gchar *object_path;
|
||
+ guint n_used;
|
||
+ gint needs_reset;
|
||
+ } session_data;
|
||
+
|
||
+ struct _DependencySizesData {
|
||
+ GMutex mutex;
|
||
+ GPtrArray *apps; /* (element-type GsApp) */
|
||
+ GCancellable *cancellable; /* non-NULL, when an op is running */
|
||
+ } dependency_sizes_data;
|
||
+};
|
||
+
|
||
+G_DEFINE_TYPE (GsPluginDnf5, gs_plugin_dnf5, GS_TYPE_PLUGIN)
|
||
+
|
||
+#define assert_in_worker(self) \
|
||
+ g_assert (gs_worker_thread_is_in_worker_context (self->worker))
|
||
+
|
||
+static gint
|
||
+gs_dnf5_get_priority_for_interactivity (gboolean interactive)
|
||
+{
|
||
+ return interactive ? G_PRIORITY_DEFAULT : G_PRIORITY_LOW;
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_convert_error (GError **error)
|
||
+{
|
||
+ gboolean not_authorized;
|
||
+ if (error == NULL || *error == NULL)
|
||
+ return;
|
||
+
|
||
+ /* The message is not localized in the dnf5, thus this should work */
|
||
+ not_authorized = strstr ((*error)->message, "GDBus.Error:org.rpm.dnf.v0.Error: Not authorized") != NULL;
|
||
+
|
||
+ g_dbus_error_strip_remote_error (*error);
|
||
+ gs_utils_error_convert_gdbus (error);
|
||
+ if (not_authorized && g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) {
|
||
+ (*error)->domain = GS_PLUGIN_ERROR;
|
||
+ (*error)->code = GS_PLUGIN_ERROR_AUTH_REQUIRED;
|
||
+ } else if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) || (
|
||
+ !gs_utils_error_convert_gdbus (error) &&
|
||
+ !gs_utils_error_convert_gio (error))) {
|
||
+ (*error)->domain = GS_PLUGIN_ERROR;
|
||
+ (*error)->code = GS_PLUGIN_ERROR_FAILED;
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_report_error (GsPluginDnf5 *self,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GsApp *app,
|
||
+ const GError *error,
|
||
+ gboolean interactive)
|
||
+{
|
||
+ g_autoptr(GsPluginEvent) event = NULL;
|
||
+
|
||
+ if (!event_callback)
|
||
+ return;
|
||
+
|
||
+ event = gs_plugin_event_new ("error", error,
|
||
+ app ? "app" : NULL, app,
|
||
+ NULL);
|
||
+ if (interactive)
|
||
+ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_INTERACTIVE);
|
||
+ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING);
|
||
+ event_callback (GS_PLUGIN (self), event, event_user_data);
|
||
+}
|
||
+
|
||
+/* The session reset is used to refresh information about the packages,
|
||
+ like after a package install/uninstall, it might not be noticed by
|
||
+ the daemon internal structures that the package is installed/uninstalled. */
|
||
+static void
|
||
+gs_dnf5_mark_session_needs_reset (GsPluginDnf5 *self)
|
||
+{
|
||
+ g_atomic_int_set (&self->session_data.needs_reset, 1);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_rpm_start_transaction_cb (GsDnf5RpmTransaction *proxy,
|
||
+ const gchar *dbcookie,
|
||
+ guint transaction_id,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = user_data;
|
||
+
|
||
+ g_debug ("%s: %p: dbcookie:'%s' transaction_id:%u", G_STRFUNC, self, dbcookie, transaction_id);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_rpm_end_transaction_cb (GsDnf5RpmTransaction *proxy,
|
||
+ const gchar *dbcookie,
|
||
+ guint transaction_id,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = user_data;
|
||
+
|
||
+ g_debug ("%s: %p: dbcookie:'%s' transaction_id:%u", G_STRFUNC, self, dbcookie, transaction_id);
|
||
+
|
||
+ /* the transactions are for install/uninstall, thus
|
||
+ mark the connection to require a reset() */
|
||
+ gs_dnf5_mark_session_needs_reset (self);
|
||
+
|
||
+ /* also do not know what precisely changed, thus a heavy hammer */
|
||
+ gs_plugin_reload (GS_PLUGIN (self));
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_got_rpm_transaction_proxy_cb (GObject *source_object,
|
||
+ GAsyncResult *result,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ g_autoptr(GsPluginDnf5) self = user_data;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ self->rpm_transaction_proxy = gs_dnf5_rpm_transaction_proxy_new_finish (result, &local_error);
|
||
+
|
||
+ if (self->rpm_transaction_proxy != NULL) {
|
||
+ g_debug ("Opened RPM Transaction D-Bus proxy");
|
||
+
|
||
+ g_signal_connect_object (self->rpm_transaction_proxy, "start-transaction",
|
||
+ G_CALLBACK (gs_dnf5_rpm_start_transaction_cb), self, 0);
|
||
+ g_signal_connect_object (self->rpm_transaction_proxy, "end-transaction",
|
||
+ G_CALLBACK (gs_dnf5_rpm_end_transaction_cb), self, 0);
|
||
+ } else {
|
||
+ g_debug ("Failed to open RPM Transaction proxy: %s", local_error ? local_error->message : "Unknown error");
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_rpm_transaction_appeared_cb (GDBusConnection *connection,
|
||
+ const gchar *name,
|
||
+ const gchar *name_owner,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = user_data;
|
||
+
|
||
+ g_debug ("%s: name:'%s' owner:'%s' existing-proxy:%p calling_rpm:%d",
|
||
+ G_STRFUNC, name, name_owner, self->rpm_transaction_proxy,
|
||
+ g_atomic_int_get (&self->calling_rpm));
|
||
+
|
||
+ if (name_owner != NULL && *name_owner != '\0' && !g_atomic_int_get (&self->calling_rpm)) {
|
||
+ g_clear_object (&self->rpm_transaction_proxy);
|
||
+ gs_dnf5_rpm_transaction_proxy_new (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ "org.rpm.announce",
|
||
+ "/org/rpm/Transaction",
|
||
+ NULL,
|
||
+ gs_dnf5_got_rpm_transaction_proxy_cb,
|
||
+ g_object_ref (self));
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_rpm_transaction_vanished_cb (GDBusConnection *connection,
|
||
+ const gchar *name,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = user_data;
|
||
+
|
||
+ g_debug ("%s: name:'%s' existing-proxy:%p", G_STRFUNC, name, self->rpm_transaction_proxy);
|
||
+
|
||
+ g_clear_object (&self->rpm_transaction_proxy);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_bus_get_cb (GObject *source_object,
|
||
+ GAsyncResult *result,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ g_autoptr(GTask) task = user_data;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (g_task_get_source_object (task));
|
||
+
|
||
+ self->connection = g_bus_get_finish (result, &local_error);
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+
|
||
+ if (self->connection != NULL) {
|
||
+ self->rpm_transaction_watch_id = g_bus_watch_name_on_connection (self->connection,
|
||
+ "org.rpm.announce",
|
||
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||
+ gs_dnf5_rpm_transaction_appeared_cb,
|
||
+ gs_dnf5_rpm_transaction_vanished_cb,
|
||
+ self,
|
||
+ NULL);
|
||
+ }
|
||
+
|
||
+ if (self->connection != NULL)
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ else
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_setup_async (GsPlugin *plugin,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+
|
||
+ g_debug ("dnf5 setup");
|
||
+
|
||
+ /* Start up a worker thread to process all the plugin’s function calls. */
|
||
+ self->worker = gs_worker_thread_new ("gs-plugin-dnf5");
|
||
+
|
||
+ task = g_task_new (plugin, cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_setup_async);
|
||
+
|
||
+ g_bus_get (G_BUS_TYPE_SYSTEM, cancellable, gs_dnf5_bus_get_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_setup_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_shutdown_cb (GObject *source_object,
|
||
+ GAsyncResult *result,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ g_autoptr(GTask) task = user_data;
|
||
+ GsPluginDnf5 *self = g_task_get_source_object (task);
|
||
+ g_autoptr(GsWorkerThread) worker = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ worker = g_steal_pointer (&self->worker);
|
||
+
|
||
+ if (!gs_worker_thread_shutdown_finish (worker, result, &local_error))
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ else
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_shutdown_async (GsPlugin *plugin,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+ g_autoptr(GCancellable) dependency_sizes_cancellable = NULL;
|
||
+
|
||
+ task = g_task_new (plugin, cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_shutdown_async);
|
||
+
|
||
+ /* stop any dependency sizes ongoing operation */
|
||
+ g_mutex_lock (&self->dependency_sizes_data.mutex);
|
||
+ if (self->dependency_sizes_data.cancellable)
|
||
+ dependency_sizes_cancellable = g_object_ref (self->dependency_sizes_data.cancellable);
|
||
+ g_mutex_unlock (&self->dependency_sizes_data.mutex);
|
||
+ if (dependency_sizes_cancellable != NULL)
|
||
+ g_cancellable_cancel (dependency_sizes_cancellable);
|
||
+
|
||
+ /* Stop the worker thread. */
|
||
+ gs_worker_thread_shutdown_async (self->worker, cancellable, gs_dnf5_shutdown_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_shutdown_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_close_session_real (GsDnf5SessionManager *proxy,
|
||
+ const gchar *session_path)
|
||
+{
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ gboolean result = FALSE;
|
||
+
|
||
+ /* Not passing operation's cancellable, because it can be cancelled by the user,
|
||
+ but the session needs to be closed. */
|
||
+ if (gs_dnf5_session_manager_call_close_session_sync (proxy, session_path, &result, NULL, &local_error))
|
||
+ g_warn_if_fail (result);
|
||
+ else
|
||
+ g_debug ("Failed to close session: %s", local_error->message);
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_autoclose_session_cb (gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = user_data;
|
||
+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex);
|
||
+
|
||
+ self->session_data.autoclose_timer = 0;
|
||
+ if (self->session_data.n_used == 0 && self->session_data.proxy != NULL) {
|
||
+ gs_dnf5_close_session_real (self->session_data.proxy, self->session_data.object_path);
|
||
+
|
||
+ g_clear_object (&self->session_data.proxy);
|
||
+ g_clear_pointer (&self->session_data.object_path, g_free);
|
||
+ }
|
||
+
|
||
+ return G_SOURCE_REMOVE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_reset_session_sync (GsPluginDnf5 *self,
|
||
+ const gchar *session_path,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autoptr(GsDnf5Base) base_proxy = NULL;
|
||
+ g_autofree gchar *error_msg = NULL;
|
||
+ gboolean success = FALSE;
|
||
+
|
||
+ base_proxy = gs_dnf5_base_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ if (base_proxy == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Base proxy: ");
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ if (!gs_dnf5_base_call_reset_sync (base_proxy, &success, &error_msg, cancellable, error)) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to call Base::reset(): ");
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ if (!success) {
|
||
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to execute Base::reset(): %s", error_msg ? error_msg : "Unknown error");
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gchar * /* (transfer full) */
|
||
+gs_dnf5_open_session (GsPluginDnf5 *self,
|
||
+ const gchar *releasever,
|
||
+ GsDnf5SessionManager **out_proxy, /* (transfer full) */
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex);
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+
|
||
+ if (releasever == GS_DNF5_RELEASEVER_DEFAULT) {
|
||
+ if (self->session_data.autoclose_timer) {
|
||
+ g_source_remove (self->session_data.autoclose_timer);
|
||
+ self->session_data.autoclose_timer = 0;
|
||
+ }
|
||
+
|
||
+ if (self->session_data.proxy != NULL) {
|
||
+ GsDnf5SessionManager *proxy = self->session_data.proxy;
|
||
+
|
||
+ while (g_atomic_int_get (&self->session_data.needs_reset) &&
|
||
+ !g_cancellable_is_cancelled (cancellable)) {
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ if (self->session_data.n_used > 0) {
|
||
+ g_debug ("Existing session needs reset, waiting for opened operations to finish");
|
||
+ while (self->session_data.n_used && g_atomic_int_get (&self->session_data.needs_reset)) {
|
||
+ g_cond_wait_until (&self->session_data.cond, &self->session_data.mutex,
|
||
+ /* check whether was cancelled up to three times per second */
|
||
+ g_get_monotonic_time () + (G_TIME_SPAN_SECOND / 3));
|
||
+
|
||
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||
+ return NULL;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||
+ return NULL;
|
||
+
|
||
+ if (g_atomic_int_get (&self->session_data.needs_reset)) {
|
||
+ gboolean success;
|
||
+
|
||
+ success = gs_dnf5_reset_session_sync (self, self->session_data.object_path, cancellable, &local_error);
|
||
+ g_atomic_int_set (&self->session_data.needs_reset, 0);
|
||
+
|
||
+ /* let any waiter for session reset know the conditions changed */
|
||
+ g_cond_broadcast (&self->session_data.cond);
|
||
+
|
||
+ if (success) {
|
||
+ g_debug ("Successfully reset existing session");
|
||
+ } else {
|
||
+ g_debug ("Failed to reset existing session: %s", local_error ? local_error->message : "Unknown error");
|
||
+ break;
|
||
+ }
|
||
+ } else {
|
||
+ g_debug ("Wanted to reset the session, but someone else did that already, thus skipping the reset");
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||
+ return NULL;
|
||
+
|
||
+ if (proxy == self->session_data.proxy) {
|
||
+ self->session_data.n_used++;
|
||
+ g_debug ("Using existing session");
|
||
+ *out_proxy = g_object_ref (self->session_data.proxy);
|
||
+ return g_strdup (self->session_data.object_path);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ g_debug ("Creating new session");
|
||
+ *out_proxy = gs_dnf5_session_manager_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_NONE,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ GS_DNF5_OBJECT_PATH_RPM_DNF,
|
||
+ cancellable,
|
||
+ error);
|
||
+
|
||
+ if (*out_proxy == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to get Session Manager: ");
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+
|
||
+ if (releasever != GS_DNF5_RELEASEVER_DEFAULT) {
|
||
+ g_variant_builder_add (options_builder, "{sv}", "releasever",
|
||
+ g_variant_new_string (releasever));
|
||
+ } else {
|
||
+ g_autoptr(GVariantBuilder) config = NULL;
|
||
+
|
||
+ config = g_variant_builder_new (G_VARIANT_TYPE ("a{ss}"));
|
||
+ g_variant_builder_add (config, "{ss}", "optional_metadata_types",
|
||
+ "appstream");
|
||
+
|
||
+ g_variant_builder_add (options_builder, "{sv}", "config",
|
||
+ g_variant_builder_end (config));
|
||
+ }
|
||
+
|
||
+ if (!gs_dnf5_session_manager_call_open_session_sync (*out_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &session_path,
|
||
+ cancellable,
|
||
+ error)) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to open session: ");
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ if (releasever == GS_DNF5_RELEASEVER_DEFAULT) {
|
||
+ g_assert (self->session_data.proxy == NULL);
|
||
+ self->session_data.proxy = g_object_ref (*out_proxy);
|
||
+ self->session_data.object_path = g_strdup (session_path);
|
||
+ self->session_data.n_used = 1;
|
||
+ }
|
||
+
|
||
+ return g_steal_pointer (&session_path);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_close_session (GsPluginDnf5 *self,
|
||
+ GsDnf5SessionManager *proxy,
|
||
+ const gchar *session_path)
|
||
+{
|
||
+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex);
|
||
+
|
||
+ if (self->session_data.proxy == proxy) {
|
||
+ g_assert (self->session_data.n_used > 0);
|
||
+ self->session_data.n_used--;
|
||
+
|
||
+ /* let any waiter for session reset know the n_used changed */
|
||
+ g_cond_broadcast (&self->session_data.cond);
|
||
+
|
||
+ if (self->session_data.n_used == 0) {
|
||
+ g_assert (self->session_data.autoclose_timer == 0);
|
||
+ self->session_data.autoclose_timer = g_timeout_add_seconds (GS_SESSION_LIFETIME_SECS, gs_dnf5_autoclose_session_cb, self);
|
||
+ }
|
||
+ } else {
|
||
+ gs_dnf5_close_session_real (proxy, session_path);
|
||
+ }
|
||
+}
|
||
+
|
||
+/* Returns whether to continue */
|
||
+typedef gboolean (* GsDnf5ForeachFunc) (GsPluginDnf5 *self,
|
||
+ GVariant *value,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error);
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_foreach_item (GsPluginDnf5 *self,
|
||
+ GVariant *array,
|
||
+ GsDnf5ForeachFunc func,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GVariantIter iter;
|
||
+ GVariant *child;
|
||
+
|
||
+ g_variant_iter_init (&iter, array);
|
||
+ while (child = g_variant_iter_next_value (&iter), child != NULL) {
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ gboolean done;
|
||
+
|
||
+ done = !func (self, child, user_data, cancellable, &local_error);
|
||
+ g_variant_unref (child);
|
||
+
|
||
+ if (local_error) {
|
||
+ g_propagate_error (error, g_steal_pointer (&local_error));
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
||
+ return FALSE;
|
||
+
|
||
+ if (done)
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_app_set_str (GVariantDict *dict,
|
||
+ const gchar *key,
|
||
+ GsApp *app,
|
||
+ void (*set_func) (GsApp *app,
|
||
+ const gchar *value))
|
||
+{
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE_STRING);
|
||
+ if (value != NULL)
|
||
+ set_func (app, g_variant_get_string (value, NULL));
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_app_set_str2 (GVariantDict *dict,
|
||
+ const gchar *key,
|
||
+ GsApp *app,
|
||
+ void (*set_func) (GsApp *app,
|
||
+ GsAppQuality quality,
|
||
+ const gchar *value))
|
||
+{
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE_STRING);
|
||
+ if (value != NULL)
|
||
+ set_func (app, GS_APP_QUALITY_NORMAL, g_variant_get_string (value, NULL));
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_app_set_size (GVariantDict *dict,
|
||
+ const gchar *key,
|
||
+ GsApp *app,
|
||
+ void (*set_func) (GsApp *app,
|
||
+ GsSizeType size_type,
|
||
+ guint64 size_bytes))
|
||
+{
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, key, G_VARIANT_TYPE_UINT64);
|
||
+ if (value != NULL)
|
||
+ set_func (app, GS_SIZE_TYPE_VALID, g_variant_get_uint64 (value));
|
||
+}
|
||
+
|
||
+static gchar *
|
||
+gs_dnf5_dup_version_from_dict (GVariantDict *dict)
|
||
+{
|
||
+ g_autoptr(GVariant) version = NULL;
|
||
+
|
||
+ version = g_variant_dict_lookup_value (dict, "version", G_VARIANT_TYPE_STRING);
|
||
+ if (version != NULL) {
|
||
+ const gchar *version_str = g_variant_get_string (version, NULL);
|
||
+ if (version_str != NULL && *version_str != '\0') {
|
||
+ g_autoptr(GVariant) release = NULL;
|
||
+ const gchar *release_str = NULL;
|
||
+ release = g_variant_dict_lookup_value (dict, "release", G_VARIANT_TYPE_STRING);
|
||
+ if (release != NULL)
|
||
+ release_str = g_variant_get_string (release, NULL);
|
||
+ if (release_str != NULL && *release_str != '\0') {
|
||
+ g_autofree gchar *version_release_str = g_strconcat (version_str, "-", release_str, NULL);
|
||
+ return g_steal_pointer (&version_release_str);
|
||
+ } else {
|
||
+ return g_strdup (version_str);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_app_set_version (GVariantDict *dict,
|
||
+ GsApp *app,
|
||
+ void (*set_func) (GsApp *app,
|
||
+ const gchar *value))
|
||
+{
|
||
+ g_autofree gchar *ver_str = gs_dnf5_dup_version_from_dict (dict);
|
||
+ if (ver_str != NULL)
|
||
+ set_func (app, ver_str);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_set_packaging_format (GsApp *app)
|
||
+{
|
||
+ gs_app_set_metadata (app, "GnomeSoftware::PackagingFormat", "RPM");
|
||
+ gs_app_set_metadata (app, "GnomeSoftware::PackagingBaseCssColor", "error_color");
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_update_app_state (GsApp *app,
|
||
+ GsAppState state,
|
||
+ GVariantDict *dict)
|
||
+{
|
||
+ if (state == GS_APP_STATE_UNKNOWN) {
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+ value = g_variant_dict_lookup_value (dict, "is_installed", G_VARIANT_TYPE_BOOLEAN);
|
||
+ if (value == NULL)
|
||
+ return;
|
||
+ if (g_variant_get_boolean (value))
|
||
+ state = GS_APP_STATE_INSTALLED;
|
||
+ else
|
||
+ state = GS_APP_STATE_AVAILABLE;
|
||
+ }
|
||
+ /* The state can be only installed, available or updatable */
|
||
+ if (gs_app_get_state (app) == state ||
|
||
+ gs_app_get_state (app) == GS_APP_STATE_UPDATABLE)
|
||
+ return;
|
||
+ if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN)
|
||
+ gs_app_set_state (app, state);
|
||
+ else if (gs_app_get_state (app) != GS_APP_STATE_INSTALLED ||
|
||
+ state == GS_APP_STATE_UPDATABLE)
|
||
+ gs_app_set_state (app, state);
|
||
+}
|
||
+
|
||
+typedef enum {
|
||
+ GS_DNF5_READ_PACKAGE_FLAG_NONE = 0,
|
||
+ GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED = 1 << 0,
|
||
+ GS_DNF5_READ_PACKAGE_FLAG_CHANGELOGS = 1 << 1
|
||
+} GsDnf5ReadPackageFlags;
|
||
+
|
||
+static void
|
||
+gs_dnf5_update_app_changelogs (GsApp *app,
|
||
+ GsDnf5ReadPackageFlags flags,
|
||
+ GVariantDict *dict)
|
||
+{
|
||
+ GVariantIter iter;
|
||
+ GVariant *child;
|
||
+ g_autoptr(GString) changes = NULL;
|
||
+ g_autoptr(GVariant) changelogs = NULL;
|
||
+
|
||
+ changelogs = g_variant_dict_lookup_value (dict, "changelogs", NULL);
|
||
+ if (changelogs == NULL)
|
||
+ return;
|
||
+
|
||
+ g_variant_iter_init (&iter, changelogs);
|
||
+ while (child = g_variant_iter_next_value (&iter), child != NULL) {
|
||
+ gint64 when = 0;
|
||
+ const gchar *whom = NULL;
|
||
+ const gchar *what = NULL;
|
||
+
|
||
+ g_variant_get (child, "(x&s&s)", &when, &whom, &what);
|
||
+
|
||
+ if (whom != NULL && what != NULL) {
|
||
+ g_autofree gchar *tmp = NULL;
|
||
+ const gchar *eml_start, *eml_end;
|
||
+
|
||
+ /* Hide the email address in the changes */
|
||
+ eml_start = strchr (whom, '<');
|
||
+ eml_end = strrchr (whom, '>');
|
||
+ if (eml_start != NULL && eml_start < eml_end) {
|
||
+ if (g_ascii_isspace (eml_end[1]))
|
||
+ eml_end++;
|
||
+ tmp = g_malloc0 (strlen (whom) + 1);
|
||
+ strncpy (tmp, whom, eml_start - whom);
|
||
+ strcat (tmp, eml_end + 1);
|
||
+ whom = tmp;
|
||
+ }
|
||
+
|
||
+ if (changes == NULL)
|
||
+ changes = g_string_new ("");
|
||
+ else
|
||
+ g_string_append (changes, "\n\n");
|
||
+ g_string_append (changes, whom);
|
||
+ g_string_append_c (changes, '\n');
|
||
+ g_string_append (changes, what);
|
||
+ }
|
||
+
|
||
+ g_variant_unref (child);
|
||
+ }
|
||
+
|
||
+ if (changes != NULL) {
|
||
+ if ((flags & GS_DNF5_READ_PACKAGE_FLAG_CHANGELOGS) != 0)
|
||
+ gs_app_set_update_details_text (app, changes->str);
|
||
+ else
|
||
+ gs_app_set_metadata (app, GS_DNF5_CHANGELOGS_KEY, changes->str);
|
||
+ }
|
||
+}
|
||
+
|
||
+static GsApp * /* (transfer full) */
|
||
+gs_dnf5_read_package_from_dict (GsPluginDnf5 *self,
|
||
+ GVariantDict *dict,
|
||
+ GsDnf5ReadPackageFlags flags,
|
||
+ GsAppState set_state)
|
||
+{
|
||
+ g_autoptr(GsApp) app = NULL;
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+ g_autofree gchar *name = NULL;
|
||
+
|
||
+ if ((flags & GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED) != 0) {
|
||
+ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING);
|
||
+ if (value != NULL) {
|
||
+ const gchar *name_const = g_variant_get_string (value, NULL);
|
||
+ app = gs_plugin_cache_lookup (GS_PLUGIN (self), name_const);
|
||
+ if (app == NULL)
|
||
+ name = g_strdup (name_const);
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (app == NULL) {
|
||
+ app = gs_app_new (NULL);
|
||
+ gs_app_set_management_plugin (app, GS_PLUGIN (self));
|
||
+ gs_app_set_metadata (app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self)));
|
||
+ gs_plugin_dnf5_set_packaging_format (app);
|
||
+ gs_app_set_kind (app, AS_COMPONENT_KIND_GENERIC);
|
||
+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE);
|
||
+ gs_app_set_scope (app, AS_COMPONENT_SCOPE_SYSTEM);
|
||
+
|
||
+ gs_dnf5_app_set_str (dict, "name", app, gs_app_add_source);
|
||
+ gs_dnf5_app_set_str (dict, "nevra", app, gs_app_add_source_id);
|
||
+ gs_dnf5_app_set_str2 (dict, "name", app, gs_app_set_name);
|
||
+ gs_dnf5_app_set_str2 (dict, "summary", app, gs_app_set_summary);
|
||
+ gs_dnf5_app_set_str2 (dict, "description", app, gs_app_set_description);
|
||
+ gs_dnf5_app_set_str2 (dict, "license", app, gs_app_set_license);
|
||
+ gs_dnf5_app_set_size (dict, "install_size", app, gs_app_set_size_installed);
|
||
+ gs_dnf5_app_set_size (dict, "download_size", app, gs_app_set_size_download);
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, "url", G_VARIANT_TYPE_STRING);
|
||
+ if (value != NULL) {
|
||
+ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, g_variant_get_string (value, NULL));
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ }
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, "buildtime", G_VARIANT_TYPE_UINT64);
|
||
+ if (value != NULL) {
|
||
+ gs_app_set_install_date (app, (gint64) g_variant_get_uint64 (value));
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ }
|
||
+
|
||
+ if ((flags & GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED) != 0)
|
||
+ gs_plugin_cache_add (GS_PLUGIN (self), name, app);
|
||
+ }
|
||
+
|
||
+ gs_dnf5_update_app_changelogs (app, flags, dict);
|
||
+ gs_dnf5_update_app_state (app, set_state, dict);
|
||
+
|
||
+ if (set_state == GS_APP_STATE_UPDATABLE) {
|
||
+ gs_dnf5_app_set_version (dict, app, gs_app_set_update_version);
|
||
+ gs_app_add_quirk (app, GS_APP_QUIRK_NEEDS_REBOOT);
|
||
+ } else {
|
||
+ gs_dnf5_app_set_version (dict, app, gs_app_set_version);
|
||
+ }
|
||
+
|
||
+ return g_steal_pointer (&app);
|
||
+}
|
||
+
|
||
+typedef struct {
|
||
+ GsAppList *list;
|
||
+ GsAppState set_state;
|
||
+ GHashTable *nevra_to_app; /* (nullable): gchar *nevra ~> GsApp * */
|
||
+ GHashTable *replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> gchar *old_version */
|
||
+ GHashTable *used_replaces; /* (nullable): GINT_TO_POINTER(pkg-id) ~> NULL */
|
||
+ gboolean cover_unused_replaces;
|
||
+ gint64 timestamp;
|
||
+} ReadPackageData;
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_read_package_cb (GsPluginDnf5 *self,
|
||
+ GVariant *package_array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ ReadPackageData *rpd = user_data;
|
||
+ GsAppList *list = rpd->list;
|
||
+ GHashTable *nevra_to_app = rpd->nevra_to_app;
|
||
+ g_autoptr(GsApp) app = NULL;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+
|
||
+ dict = g_variant_dict_new (package_array);
|
||
+
|
||
+ app = gs_dnf5_read_package_from_dict (self, dict, GS_DNF5_READ_PACKAGE_FLAG_CAN_CACHED | GS_DNF5_READ_PACKAGE_FLAG_CHANGELOGS, rpd->set_state);
|
||
+
|
||
+ if (nevra_to_app != NULL) {
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+ value = g_variant_dict_lookup_value (dict, "nevra", G_VARIANT_TYPE_STRING);
|
||
+ if (value != NULL) {
|
||
+ const gchar *nevra_const = g_variant_get_string (value, NULL);
|
||
+ if (nevra_const != NULL && *nevra_const != '\0')
|
||
+ g_hash_table_insert (nevra_to_app, g_strdup (nevra_const), g_object_ref (app));
|
||
+ }
|
||
+ }
|
||
+
|
||
+ gs_app_list_add (list, app);
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_read_repo_cb (GsPluginDnf5 *self,
|
||
+ GVariant *package_array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GsAppList *list = user_data;
|
||
+ g_autoptr(GsApp) app = NULL;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+ const gchar *id;
|
||
+
|
||
+ dict = g_variant_dict_new (package_array);
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_STRING);
|
||
+ if (value == NULL)
|
||
+ return TRUE;
|
||
+
|
||
+ id = g_variant_get_string (value, NULL);
|
||
+ if (id == NULL || *id == '\0')
|
||
+ return TRUE;
|
||
+
|
||
+ /* Skip sub-repos */
|
||
+ if (g_str_has_suffix (id, "-source") ||
|
||
+ g_str_has_suffix (id, "-debuginfo") ||
|
||
+ g_str_has_suffix (id, "-testing"))
|
||
+ return TRUE;
|
||
+
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ id = NULL;
|
||
+
|
||
+ app = gs_app_new (NULL);
|
||
+ gs_app_set_management_plugin (app, GS_PLUGIN (self));
|
||
+ gs_app_set_kind (app, AS_COMPONENT_KIND_REPOSITORY);
|
||
+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE);
|
||
+ gs_app_set_scope (app, AS_COMPONENT_SCOPE_SYSTEM);
|
||
+ gs_app_add_quirk (app, GS_APP_QUIRK_NOT_LAUNCHABLE);
|
||
+ gs_app_set_metadata (app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self)));
|
||
+ gs_plugin_dnf5_set_packaging_format (app);
|
||
+ gs_app_set_metadata (app, "GnomeSoftware::SortKey", "300");
|
||
+ gs_app_set_origin_ui (app, _("Packages"));
|
||
+
|
||
+ gs_dnf5_app_set_str (dict, "id", app, gs_app_set_id);
|
||
+ gs_dnf5_app_set_str2 (dict, "name", app, gs_app_set_name);
|
||
+ gs_dnf5_app_set_str2 (dict, "name", app, gs_app_set_description);
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, "enabled", G_VARIANT_TYPE_BOOLEAN);
|
||
+ if (value != NULL) {
|
||
+ gs_app_set_state (app, g_variant_get_boolean (value) ?
|
||
+ GS_APP_STATE_INSTALLED : GS_APP_STATE_AVAILABLE);
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ } else {
|
||
+ gs_app_set_state (app, GS_APP_STATE_INSTALLED);
|
||
+ }
|
||
+
|
||
+ gs_app_list_add (list, app);
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_refine_from_advisory_packages_cb (GsPluginDnf5 *self,
|
||
+ GVariant *array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GHashTable *nevra_to_app = user_data;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) var_nevra = NULL;
|
||
+
|
||
+ dict = g_variant_dict_new (array);
|
||
+
|
||
+ var_nevra = g_variant_dict_lookup_value (dict, "nevra", G_VARIANT_TYPE_STRING);
|
||
+ if (var_nevra != NULL) {
|
||
+ const gchar *nevra = g_variant_get_string (var_nevra, NULL);
|
||
+ if (nevra != NULL) {
|
||
+ GsApp *app;
|
||
+
|
||
+ app = g_hash_table_lookup (nevra_to_app, nevra);
|
||
+ if (app != NULL)
|
||
+ gs_app_set_update_urgency (app, AS_URGENCY_KIND_CRITICAL);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_refine_from_advisory_collections_cb (GsPluginDnf5 *self,
|
||
+ GVariant *array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GHashTable *nevra_to_app = user_data;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) packages = NULL;
|
||
+ gboolean res = TRUE;
|
||
+
|
||
+ dict = g_variant_dict_new (array);
|
||
+
|
||
+ packages = g_variant_dict_lookup_value (dict, "packages", G_VARIANT_TYPE_ARRAY);
|
||
+ if (packages != NULL)
|
||
+ res = gs_dnf5_foreach_item (self, packages, gs_dnf5_refine_from_advisory_packages_cb, nevra_to_app, cancellable, error);
|
||
+
|
||
+ return res;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_refine_from_advisory_cb (GsPluginDnf5 *self,
|
||
+ GVariant *array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GHashTable *nevra_to_app = user_data;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) collections = NULL;
|
||
+ g_autoptr(GVariant) severity = NULL;
|
||
+ gboolean res = TRUE;
|
||
+
|
||
+ dict = g_variant_dict_new (array);
|
||
+
|
||
+ /* ensure only advisories with severity 'critical' are traversed */
|
||
+ severity = g_variant_dict_lookup_value (dict, "severity", G_VARIANT_TYPE_STRING);
|
||
+ if (severity == NULL || g_variant_get_string (severity, NULL) == NULL ||
|
||
+ g_ascii_strcasecmp (g_variant_get_string (severity, NULL), "critical") != 0)
|
||
+ return TRUE;
|
||
+
|
||
+ collections = g_variant_dict_lookup_value (dict, "collections", G_VARIANT_TYPE_ARRAY);
|
||
+ if (collections != NULL)
|
||
+ res = gs_dnf5_foreach_item (self, collections, gs_dnf5_refine_from_advisory_collections_cb, nevra_to_app, cancellable, error);
|
||
+
|
||
+ return res;
|
||
+}
|
||
+
|
||
+static GVariant *
|
||
+gs_dnf5_dup_package_attrs (void)
|
||
+{
|
||
+ g_autoptr(GVariantBuilder) attrs_builder = NULL;
|
||
+
|
||
+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (attrs_builder, "s", "nevra");
|
||
+ g_variant_builder_add (attrs_builder, "s", "name");
|
||
+ g_variant_builder_add (attrs_builder, "s", "epoch");
|
||
+ g_variant_builder_add (attrs_builder, "s", "version");
|
||
+ g_variant_builder_add (attrs_builder, "s", "release");
|
||
+ g_variant_builder_add (attrs_builder, "s", "arch");
|
||
+ g_variant_builder_add (attrs_builder, "s", "repo_id");
|
||
+ g_variant_builder_add (attrs_builder, "s", "install_size");
|
||
+ g_variant_builder_add (attrs_builder, "s", "download_size");
|
||
+ g_variant_builder_add (attrs_builder, "s", "is_installed");
|
||
+ g_variant_builder_add (attrs_builder, "s", "summary");
|
||
+ g_variant_builder_add (attrs_builder, "s", "url");
|
||
+ g_variant_builder_add (attrs_builder, "s", "license");
|
||
+ g_variant_builder_add (attrs_builder, "s", "description");
|
||
+ g_variant_builder_add (attrs_builder, "s", "buildtime");
|
||
+ g_variant_builder_add (attrs_builder, "s", "changelogs");
|
||
+
|
||
+ return g_variant_builder_end (attrs_builder);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_gather_async_result_cb (GObject *source_object,
|
||
+ GAsyncResult *result,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GAsyncResult **out_result = user_data;
|
||
+ *out_result = g_object_ref (result);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_confirm_key_response_cb (GObject *source_object,
|
||
+ GAsyncResult *result,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ if (!gs_dnf5_rpm_repo_call_confirm_key_finish (GS_DNF5_RPM_REPO (source_object), result, &local_error))
|
||
+ g_debug ("Failed to confirm key: %s", local_error->message);
|
||
+}
|
||
+
|
||
+typedef struct {
|
||
+ gchar *title;
|
||
+ gchar *msg;
|
||
+ gchar *details;
|
||
+ gchar *accept_label;
|
||
+ gchar *key_id;
|
||
+ gchar *key_filename;
|
||
+ gchar *session_path;
|
||
+ GCancellable *cancellable; /* (owned) */
|
||
+ GsPluginDnf5 *self; /* (owned) */
|
||
+ GWeakRef repo_proxy_weakref; /* GsDnf5RpmRepo */
|
||
+ gboolean interactive;
|
||
+ gboolean confirmed_by_repo;
|
||
+} QuestionData;
|
||
+
|
||
+static void
|
||
+question_data_free (QuestionData *data)
|
||
+{
|
||
+ if (data != NULL) {
|
||
+ g_free (data->title);
|
||
+ g_free (data->msg);
|
||
+ g_free (data->details);
|
||
+ g_free (data->accept_label);
|
||
+ g_free (data->key_id);
|
||
+ g_free (data->key_filename);
|
||
+ g_free (data->session_path);
|
||
+ g_clear_object (&data->cancellable);
|
||
+ g_clear_object (&data->self);
|
||
+ g_weak_ref_clear (&data->repo_proxy_weakref);
|
||
+ g_free (data);
|
||
+ }
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_ask_question_idle_cb (gpointer user_data)
|
||
+{
|
||
+ QuestionData *data = user_data;
|
||
+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL;
|
||
+
|
||
+ repo_proxy = g_weak_ref_get (&data->repo_proxy_weakref);
|
||
+ if (repo_proxy != NULL) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ gboolean confirmed;
|
||
+
|
||
+ /* auto-accept new keys, just as PacakgeKit plugin used to do */
|
||
+ confirmed = data->confirmed_by_repo;
|
||
+ if (!confirmed)
|
||
+ confirmed = gs_plugin_ask_untrusted (GS_PLUGIN (data->self), data->title, data->msg, data->details, data->accept_label);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "interactive",
|
||
+ g_variant_new_boolean (data->interactive));
|
||
+
|
||
+ gs_dnf5_rpm_repo_call_confirm_key_with_options (repo_proxy,
|
||
+ data->key_id,
|
||
+ confirmed,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ data->cancellable,
|
||
+ gs_dnf5_confirm_key_response_cb,
|
||
+ NULL);
|
||
+ }
|
||
+
|
||
+ return G_SOURCE_REMOVE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_read_package_repo_ids_cb (GsPluginDnf5 *self,
|
||
+ GVariant *package_array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GPtrArray **out_repo_ids = user_data;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+
|
||
+ dict = g_variant_dict_new (package_array);
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, "from_repo_id", G_VARIANT_TYPE_STRING);
|
||
+ if (value != NULL) {
|
||
+ const gchar *repo_id_const = g_variant_get_string (value, NULL);
|
||
+ if (repo_id_const != NULL && *repo_id_const != '\0') {
|
||
+ if (*out_repo_ids == NULL)
|
||
+ *out_repo_ids = g_ptr_array_new_with_free_func (g_free);
|
||
+ g_ptr_array_add (*out_repo_ids, g_strdup (repo_id_const));
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_read_repo_gpg_info_cb (GsPluginDnf5 *self,
|
||
+ GVariant *package_array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ guint *out_n_good = user_data;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) id_value = NULL;
|
||
+ g_autoptr(GVariant) gpgcheck_value = NULL;
|
||
+
|
||
+ dict = g_variant_dict_new (package_array);
|
||
+
|
||
+ id_value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_STRING);
|
||
+ gpgcheck_value = g_variant_dict_lookup_value (dict, "gpgcheck", G_VARIANT_TYPE_BOOLEAN);
|
||
+
|
||
+ if (id_value != NULL && gpgcheck_value != NULL) {
|
||
+ gboolean gpgcheck = g_variant_get_boolean (gpgcheck_value);
|
||
+
|
||
+ if (gpgcheck)
|
||
+ *out_n_good = (*out_n_good) + 1;
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gpointer
|
||
+gs_dnf5_check_key_source_thread (gpointer user_data)
|
||
+{
|
||
+ QuestionData *data = user_data;
|
||
+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL;
|
||
+ g_autoptr(GPtrArray) repo_ids = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ gboolean success;
|
||
+
|
||
+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (data->self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ data->session_path,
|
||
+ data->cancellable,
|
||
+ &local_error);
|
||
+ success = rpm_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: ");
|
||
+ }
|
||
+
|
||
+ /* try to get whether the key comes from any package */
|
||
+ if (success) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) package_attrs_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) whatprovides_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT);
|
||
+
|
||
+ package_attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (package_attrs_builder, "s", "from_repo_id");
|
||
+
|
||
+ whatprovides_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (whatprovides_builder, "s", data->key_filename);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "package_attrs",
|
||
+ g_variant_builder_end (package_attrs_builder));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "whatprovides",
|
||
+ g_variant_builder_end (whatprovides_builder));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "scope",
|
||
+ g_variant_new_string ("installed"));
|
||
+
|
||
+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &result,
|
||
+ data->cancellable,
|
||
+ &local_error);
|
||
+
|
||
+ if (success) {
|
||
+ success = gs_dnf5_foreach_item (data->self, result, gs_dnf5_read_package_repo_ids_cb, &repo_ids, data->cancellable, &local_error);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call rpm::list: ");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (success && repo_ids != NULL) {
|
||
+ g_autoptr(GVariantBuilder) patterns_builder = NULL;
|
||
+ guint n_verify = 0;
|
||
+
|
||
+ for (guint i = 0; i < repo_ids->len; i++) {
|
||
+ const gchar *repo_id = g_ptr_array_index (repo_ids, i);
|
||
+ /* those are special repos, like `@commandline` */
|
||
+ if (*repo_id != '@') {
|
||
+ if (patterns_builder == NULL)
|
||
+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (patterns_builder, "s", repo_id);
|
||
+ n_verify++;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (patterns_builder != NULL) {
|
||
+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL;
|
||
+
|
||
+ repo_proxy = g_weak_ref_get (&data->repo_proxy_weakref);
|
||
+ if (repo_proxy != NULL) {
|
||
+ g_autoptr(GVariantBuilder) repo_attrs_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+
|
||
+ repo_attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (repo_attrs_builder, "s", "id");
|
||
+ g_variant_builder_add (repo_attrs_builder, "s", "gpgcheck");
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "patterns",
|
||
+ g_variant_builder_end (patterns_builder));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "repo_attrs",
|
||
+ g_variant_builder_end (repo_attrs_builder));
|
||
+
|
||
+ success = gs_dnf5_rpm_repo_call_list_sync (repo_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &result,
|
||
+ data->cancellable,
|
||
+ &local_error);
|
||
+
|
||
+ if (success) {
|
||
+ guint n_good = 0;
|
||
+
|
||
+ success = gs_dnf5_foreach_item (data->self, result, gs_dnf5_read_repo_gpg_info_cb, &n_good, data->cancellable, &local_error);
|
||
+
|
||
+ data->confirmed_by_repo = success && n_verify == n_good;
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call repo::list: ");
|
||
+ }
|
||
+ } else {
|
||
+ success = FALSE;
|
||
+ g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED, "Repo proxy vanished");
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_dnf5_ask_question_idle_cb, data, (GDestroyNotify) question_data_free);
|
||
+
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
+typedef struct {
|
||
+ GsPluginDnf5 *self; /* (not owned) */
|
||
+ const gchar *session_path; /* (not owned) */
|
||
+ GsDnf5RpmRepo *repo_proxy; /* (not owned) */
|
||
+ GCancellable *cancellable; /* (not owned) */
|
||
+ gboolean interactive;
|
||
+} RepoKeyImportData;
|
||
+
|
||
+static void
|
||
+gs_dnf5_repo_key_import_request_cb (GsDnf5Base *object,
|
||
+ const gchar *arg_session_object_path,
|
||
+ const gchar *arg_key_id,
|
||
+ const gchar *const *arg_user_ids,
|
||
+ const gchar *arg_key_fingerprint,
|
||
+ const gchar *arg_key_url,
|
||
+ gint64 arg_timestamp,
|
||
+ RepoKeyImportData *repo_key_import_data)
|
||
+{
|
||
+ QuestionData *data;
|
||
+ g_autoptr(GString) user_ids = NULL;
|
||
+ g_autofree gchar *key_filename = NULL;
|
||
+ g_autoptr(GString) str_fingerprint = NULL;
|
||
+ GString *details = NULL;
|
||
+ const gchar *fingerprint;
|
||
+ const gchar *from;
|
||
+
|
||
+ if (g_strcmp0 (repo_key_import_data->session_path, arg_session_object_path) != 0)
|
||
+ return;
|
||
+
|
||
+ user_ids = g_string_new (NULL);
|
||
+ for (guint i = 0; arg_user_ids != NULL && arg_user_ids[i] != NULL; i++) {
|
||
+ if (i > 0)
|
||
+ g_string_append (user_ids, ", ");
|
||
+ g_string_append_c (user_ids, '"');
|
||
+ g_string_append (user_ids, arg_user_ids[i]);
|
||
+ g_string_append_c (user_ids, '"');
|
||
+ }
|
||
+
|
||
+ g_debug ("%s: key_id:'%s' user_ids:[%s] key_fingerprint:'%s' key_url:'%s' timestamp:%" G_GINT64_FORMAT,
|
||
+ G_STRFUNC, arg_key_id, user_ids->str, arg_key_fingerprint, arg_key_url, arg_timestamp);
|
||
+
|
||
+ if (g_ascii_strncasecmp (arg_key_url, "file:", 5) == 0)
|
||
+ key_filename = g_filename_from_uri (arg_key_url, NULL, NULL);
|
||
+
|
||
+ if ((strlen (arg_key_fingerprint) % 4) == 0) {
|
||
+ str_fingerprint = g_string_new (arg_key_fingerprint);
|
||
+ for (gint i = str_fingerprint->len - 4; i > 0; i -= 4) {
|
||
+ g_string_insert_c (str_fingerprint, i, ' ');
|
||
+ }
|
||
+ }
|
||
+
|
||
+ details = g_string_new ("");
|
||
+
|
||
+ from = key_filename != NULL ? key_filename : arg_key_url;
|
||
+ fingerprint = str_fingerprint != NULL ? str_fingerprint->str : arg_key_fingerprint;
|
||
+
|
||
+ #define add_nonempty_line(_format, _value) G_STMT_START { \
|
||
+ if ((_value) != NULL && *(_value) != '\0') { \
|
||
+ g_string_append_printf (details, _format, _value); \
|
||
+ g_string_append_c (details, '\n'); \
|
||
+ } \
|
||
+ } G_STMT_END
|
||
+
|
||
+ /* Translators: the '%s' is replaced with the key user name */
|
||
+ add_nonempty_line (_("Key user: %s"), user_ids->str);
|
||
+ /* Translators: the '%s' is replaced with the key fingerprint, a few hex digits */
|
||
+ add_nonempty_line (_("Fingerprint: %s"), fingerprint);
|
||
+ /* Translators: the '%s' is replaced with the local path or a URI to the key */
|
||
+ add_nonempty_line (_("From: %s"), from);
|
||
+
|
||
+ #undef add_nonempty_line
|
||
+
|
||
+ data = g_new0 (QuestionData, 1);
|
||
+ data->title = g_strdup (_("Import Key"));
|
||
+ /* Translators: the '%s' is replaced with the key ID, usually a few hex digits */
|
||
+ data->msg = g_strdup_printf (_("Do you want to import key %s?"), arg_key_id);
|
||
+ data->details = g_string_free (details, FALSE);
|
||
+ data->accept_label = g_strdup (_("_Import Key"));
|
||
+ data->key_id = g_strdup (arg_key_id);
|
||
+ data->key_filename = g_steal_pointer (&key_filename);
|
||
+ data->session_path = g_strdup (repo_key_import_data->session_path);
|
||
+ data->self = g_object_ref (repo_key_import_data->self);
|
||
+ data->interactive = repo_key_import_data->interactive;
|
||
+ data->confirmed_by_repo = FALSE;
|
||
+ g_set_object (&data->cancellable, repo_key_import_data->cancellable);
|
||
+ g_weak_ref_init (&data->repo_proxy_weakref, repo_key_import_data->repo_proxy);
|
||
+
|
||
+ if (data->key_filename != NULL)
|
||
+ g_thread_unref (g_thread_new ("gs-dnf5-check-key-source_thread", gs_dnf5_check_key_source_thread, data));
|
||
+ else
|
||
+ g_idle_add_full (G_PRIORITY_HIGH_IDLE, gs_dnf5_ask_question_idle_cb, data, (GDestroyNotify) question_data_free);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_goal_cancelled_cb (GObject *source_object,
|
||
+ GAsyncResult *result,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ g_autoptr(GCancellable) run_cancellable = user_data;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ g_autofree gchar *error_message = NULL;
|
||
+ gboolean succeeded = FALSE;
|
||
+
|
||
+ if (gs_dnf5_goal_call_cancel_finish (GS_DNF5_GOAL (source_object), &succeeded, &error_message, result, &local_error)) {
|
||
+ if (succeeded) {
|
||
+ g_debug ("%s: Transaction cancelled by the user", G_STRFUNC);
|
||
+ g_cancellable_cancel (run_cancellable);
|
||
+ } else {
|
||
+ g_debug ("%s: Cannot cancel transaction: %s", G_STRFUNC, error_message);
|
||
+ }
|
||
+ } else if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||
+ g_debug ("%s: Failed to cancel transaction: %s", G_STRFUNC, local_error->message);
|
||
+ }
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_reset_transaction_sync (GsPluginDnf5 *self,
|
||
+ const gchar *session_path,
|
||
+ GsDnf5RpmRpm *rpm_proxy,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autoptr(GsDnf5Goal) goal_proxy = NULL;
|
||
+
|
||
+ goal_proxy = gs_dnf5_goal_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ if (goal_proxy == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Goal proxy: ");
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ return gs_dnf5_goal_call_reset_sync (goal_proxy, cancellable, error);
|
||
+}
|
||
+
|
||
+/* this is needed to receive D-Bus signals about progress in the worker thread */
|
||
+static gboolean
|
||
+gs_dnf5_goal_call_do_transaction_sync_wrapper (GsDnf5Goal *proxy,
|
||
+ GVariant *arg_options,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autoptr(GMainContext) main_context = g_main_context_ref_thread_default ();
|
||
+ g_autoptr(GAsyncResult) async_result = NULL;
|
||
+ gs_dnf5_goal_call_do_transaction (proxy, arg_options, cancellable, gs_dnf5_gather_async_result_cb, &async_result);
|
||
+ while (!async_result)
|
||
+ g_main_context_iteration (main_context, TRUE);
|
||
+ return gs_dnf5_goal_call_do_transaction_finish (proxy, async_result, error);
|
||
+}
|
||
+
|
||
+typedef struct {
|
||
+ GsDnf5Goal *goal_proxy;
|
||
+ GCancellable *run_cancellable;
|
||
+} TransactionData;
|
||
+
|
||
+static void
|
||
+gs_dnf5_transaction_cancelled_cb (GCancellable *parent_cancellable,
|
||
+ TransactionData *transaction_data)
|
||
+{
|
||
+ gs_dnf5_goal_call_cancel (transaction_data->goal_proxy, transaction_data->run_cancellable,
|
||
+ gs_dnf5_goal_cancelled_cb, g_object_ref (transaction_data->run_cancellable));
|
||
+}
|
||
+
|
||
+typedef enum {
|
||
+ GS_DNF5_TRANSACTION_FLAG_NONE = 0,
|
||
+ GS_DNF5_TRANSACTION_FLAG_OFFLINE = 1 << 0,
|
||
+ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY = 1 << 1,
|
||
+ GS_DNF5_TRANSACTION_FLAG_DISALLOW_ERASING = 1 << 2, /* negative flag, to not have it everywhere */
|
||
+ GS_DNF5_TRANSACTION_FLAG_INTERACTIVE = 1 << 3,
|
||
+ GS_DNF5_TRANSACTION_FLAG_DOWNLOAD_ONLY = 1 << 4
|
||
+} GsDnf5TransactionFlags;
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_run_transaction (GsPluginDnf5 *self,
|
||
+ const gchar *session_path,
|
||
+ GsDnf5RpmRpm *rpm_proxy,
|
||
+ GsApp *progress_app,
|
||
+ GsAppList *progress_list,
|
||
+ GsDnf5TransactionFlags flags,
|
||
+ GVariant **out_transaction,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autoptr(GsDnf5Base) base_proxy = NULL;
|
||
+ g_autoptr(GsDnf5Goal) goal_proxy = NULL;
|
||
+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL;
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariant) transaction = NULL;
|
||
+ g_autoptr(GsDnf5ProgressHelper) helper = NULL;
|
||
+ g_autoptr(GCancellable) run_cancellable = NULL;
|
||
+ RepoKeyImportData repo_key_import_data;
|
||
+ TransactionData transaction_data;
|
||
+ gulong repo_key_import_id;
|
||
+ gulong cancellable_id = 0;
|
||
+ guint result = 0;
|
||
+ gboolean interactive = (flags & GS_DNF5_TRANSACTION_FLAG_INTERACTIVE) != 0;
|
||
+ gboolean success;
|
||
+
|
||
+ base_proxy = gs_dnf5_base_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ if (base_proxy == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Base proxy: ");
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ if (repo_proxy == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Repo proxy: ");
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ goal_proxy = gs_dnf5_goal_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ if (goal_proxy == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Goal proxy: ");
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (goal_proxy), G_MAXINT);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+
|
||
+ g_variant_builder_add (options_builder, "{sv}", "allow_erasing",
|
||
+ g_variant_new_boolean ((flags & GS_DNF5_TRANSACTION_FLAG_DISALLOW_ERASING) == 0));
|
||
+
|
||
+ if (progress_app != NULL || progress_list != NULL)
|
||
+ helper = gs_dnf5_progress_helper_new (base_proxy, rpm_proxy, progress_app, progress_list, session_path);
|
||
+
|
||
+ repo_key_import_data.self = self;
|
||
+ repo_key_import_data.session_path = session_path;
|
||
+ repo_key_import_data.repo_proxy = repo_proxy;
|
||
+ repo_key_import_data.interactive = interactive;
|
||
+ repo_key_import_data.cancellable = cancellable;
|
||
+ repo_key_import_id = g_signal_connect (base_proxy, "repo_key_import_request",
|
||
+ G_CALLBACK (gs_dnf5_repo_key_import_request_cb), &repo_key_import_data);
|
||
+
|
||
+ if (!gs_dnf5_goal_call_resolve_sync (goal_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &transaction,
|
||
+ &result,
|
||
+ cancellable,
|
||
+ error)) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to resolve transaction: ");
|
||
+ g_signal_handler_disconnect (base_proxy, repo_key_import_id);
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ if (out_transaction != NULL && transaction != NULL)
|
||
+ *out_transaction = g_variant_ref_sink (transaction);
|
||
+
|
||
+ /* 0 ... all fine, 1 ... there are warnings, 2 ... failed */
|
||
+ if (result == 2) {
|
||
+ g_auto(GStrv) problems = NULL;
|
||
+
|
||
+ if (gs_dnf5_goal_call_get_transaction_problems_string_sync (goal_proxy, &problems, cancellable, error) &&
|
||
+ problems != NULL) {
|
||
+ /* extra space at the end, but no harm, it reuses translated string */
|
||
+ GString *msg = g_string_new ("Failed to resolve transaction: ");
|
||
+ for (guint i = 0; problems[i] != NULL; i++) {
|
||
+ g_string_append_c (msg, '\n');
|
||
+ g_string_append (msg, problems[i]);
|
||
+ }
|
||
+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, msg->str);
|
||
+ } else {
|
||
+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED,
|
||
+ _("Unknown error"));
|
||
+ g_prefix_error (error, "Failed to resolve transaction: ");
|
||
+ }
|
||
+ g_signal_handler_disconnect (base_proxy, repo_key_import_id);
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ g_clear_pointer (&options_builder, g_variant_builder_unref);
|
||
+
|
||
+ if ((flags & GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY) != 0) {
|
||
+ g_signal_handler_disconnect (base_proxy, repo_key_import_id);
|
||
+ return TRUE;
|
||
+ }
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ if ((flags & GS_DNF5_TRANSACTION_FLAG_OFFLINE) != 0) {
|
||
+ g_variant_builder_add (options_builder, "{sv}", "offline",
|
||
+ g_variant_new_boolean (TRUE));
|
||
+ }
|
||
+ if ((flags & GS_DNF5_TRANSACTION_FLAG_DOWNLOAD_ONLY) != 0) {
|
||
+ g_variant_builder_add (options_builder, "{sv}", "downloadonly",
|
||
+ g_variant_new_boolean (TRUE));
|
||
+ }
|
||
+ g_variant_builder_add (options_builder, "{sv}", "interactive",
|
||
+ g_variant_new_boolean (interactive));
|
||
+
|
||
+ run_cancellable = g_cancellable_new ();
|
||
+ if (cancellable != NULL) {
|
||
+ transaction_data.goal_proxy = goal_proxy;
|
||
+ transaction_data.run_cancellable = run_cancellable;
|
||
+
|
||
+ cancellable_id = g_cancellable_connect (cancellable, G_CALLBACK (gs_dnf5_transaction_cancelled_cb),
|
||
+ &transaction_data, NULL);
|
||
+ }
|
||
+
|
||
+ success = gs_dnf5_goal_call_do_transaction_sync_wrapper (goal_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ run_cancellable,
|
||
+ error);
|
||
+
|
||
+ g_cancellable_disconnect (cancellable, cancellable_id);
|
||
+ g_signal_handler_disconnect (base_proxy, repo_key_import_id);
|
||
+
|
||
+ /* in case there is a pending async call result for the cancel() invocation */
|
||
+ g_cancellable_cancel (run_cancellable);
|
||
+
|
||
+ if (!success) {
|
||
+ g_auto(GStrv) problems = NULL;
|
||
+
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to run transaction: ");
|
||
+
|
||
+ if (error != NULL && *error != NULL &&
|
||
+ gs_dnf5_goal_call_get_transaction_problems_string_sync (goal_proxy, &problems, cancellable, NULL) &&
|
||
+ problems != NULL) {
|
||
+ GString *msg = g_string_new ((*error)->message);
|
||
+ for (guint i = 0; problems[i] != NULL; i++) {
|
||
+ g_string_append_c (msg, '\n');
|
||
+ g_string_append (msg, problems[i]);
|
||
+ }
|
||
+ g_clear_error (error);
|
||
+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, msg->str);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return success;
|
||
+}
|
||
+
|
||
+enum OpKindType { OP_KIND_UNKNOWN, OP_KIND_INSTALL, OP_KIND_UPGRADE, OP_KIND_DOWNGRADE, OP_KIND_REINSTALL, OP_KIND_REMOVE, OP_KIND_REPLACED };
|
||
+
|
||
+static enum OpKindType
|
||
+gs_dnf5_get_package_op_kind (GVariant *item_array)
|
||
+{
|
||
+ struct _op_kinds {
|
||
+ const gchar *name;
|
||
+ enum OpKindType kind;
|
||
+ } op_kinds[] = {
|
||
+ { "Install", OP_KIND_INSTALL },
|
||
+ { "Upgrade", OP_KIND_UPGRADE },
|
||
+ { "Downgrade", OP_KIND_DOWNGRADE },
|
||
+ { "Reinstall", OP_KIND_REINSTALL },
|
||
+ { "Remove", OP_KIND_REMOVE },
|
||
+ { "Replaced", OP_KIND_REPLACED }
|
||
+ };
|
||
+ const gchar *str = NULL;
|
||
+
|
||
+ g_variant_get_child (item_array, 0, "&s", &str);
|
||
+ if (str == NULL || g_ascii_strcasecmp (str, "package") != 0)
|
||
+ return OP_KIND_UNKNOWN;
|
||
+
|
||
+ g_variant_get_child (item_array, 1, "&s", &str);
|
||
+ if (str == NULL)
|
||
+ return OP_KIND_UNKNOWN;
|
||
+
|
||
+ for (guint i = 0; i < G_N_ELEMENTS (op_kinds); i++) {
|
||
+ if (g_ascii_strcasecmp (str, op_kinds[i].name) == 0) {
|
||
+ return op_kinds[i].kind;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return OP_KIND_UNKNOWN;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_gather_update_replaces_cb (GsPluginDnf5 *self,
|
||
+ GVariant *item_array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ ReadPackageData *rpd = user_data;
|
||
+ enum OpKindType op_kind = gs_dnf5_get_package_op_kind (item_array);
|
||
+
|
||
+ if (op_kind == OP_KIND_REPLACED && rpd->replaces) {
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+
|
||
+ value = g_variant_get_child_value (item_array, 4);
|
||
+ dict = g_variant_dict_new (value);
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_INT32);
|
||
+ if (value != NULL) {
|
||
+ gint32 id = g_variant_get_int32 (value);
|
||
+ gchar *version = gs_dnf5_dup_version_from_dict (dict);
|
||
+ if (version != NULL)
|
||
+ g_hash_table_insert (rpd->replaces, GINT_TO_POINTER (id), version);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+typedef struct _UpdateReplacesData {
|
||
+ GsApp *app;
|
||
+ GHashTable *replaces;
|
||
+ GHashTable *used_replaces;
|
||
+} UpdateReplacesData;
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_gather_update_replaced_pkgs_cb (GsPluginDnf5 *self,
|
||
+ GVariant *item_array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ UpdateReplacesData *upd = user_data;
|
||
+ gint32 replaces_id = g_variant_get_int32 (item_array);
|
||
+ const gchar *old_version = g_hash_table_lookup (upd->replaces, GINT_TO_POINTER (replaces_id));
|
||
+ if (old_version != NULL) {
|
||
+ gs_app_set_version (upd->app, old_version);
|
||
+ g_hash_table_add (upd->used_replaces, GINT_TO_POINTER (replaces_id));
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_gather_updates_from_transaction_cb (GsPluginDnf5 *self,
|
||
+ GVariant *item_array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ ReadPackageData *rpd = user_data;
|
||
+ enum OpKindType op_kind = gs_dnf5_get_package_op_kind (item_array);
|
||
+ const gchar *str;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+ g_autoptr(GsApp) upd_app = NULL;
|
||
+ g_autoptr(GString) nevra = NULL;
|
||
+ GsApp *known_app;
|
||
+
|
||
+ if (op_kind == OP_KIND_UNKNOWN || (!rpd->cover_unused_replaces && op_kind == OP_KIND_REPLACED) ||
|
||
+ (rpd->cover_unused_replaces && op_kind != OP_KIND_REPLACED))
|
||
+ return TRUE;
|
||
+
|
||
+ value = g_variant_get_child_value (item_array, 4);
|
||
+ dict = g_variant_dict_new (value);
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+
|
||
+ if (rpd->cover_unused_replaces) {
|
||
+ value = g_variant_dict_lookup_value (dict, "id", G_VARIANT_TYPE_INT32);
|
||
+ if (value != NULL) {
|
||
+ gint32 id = g_variant_get_int32 (value);
|
||
+ if (g_hash_table_contains (rpd->used_replaces, GINT_TO_POINTER (id)))
|
||
+ return TRUE;
|
||
+ } else {
|
||
+ return TRUE;
|
||
+ }
|
||
+
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ }
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING);
|
||
+ if (value == NULL)
|
||
+ return TRUE;
|
||
+ str = g_variant_get_string (value, NULL);
|
||
+ if (str == NULL)
|
||
+ return TRUE;
|
||
+
|
||
+ nevra = g_string_new (str);
|
||
+
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ value = g_variant_dict_lookup_value (dict, "evr", G_VARIANT_TYPE_STRING);
|
||
+ if (value == NULL)
|
||
+ return TRUE;
|
||
+ str = g_variant_get_string (value, NULL);
|
||
+ if (str == NULL)
|
||
+ return TRUE;
|
||
+
|
||
+ g_string_append_c (nevra, '-');
|
||
+ g_string_append (nevra, str);
|
||
+
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ value = g_variant_dict_lookup_value (dict, "arch", G_VARIANT_TYPE_STRING);
|
||
+ if (value == NULL)
|
||
+ return TRUE;
|
||
+ str = g_variant_get_string (value, NULL);
|
||
+ if (str == NULL)
|
||
+ return TRUE;
|
||
+
|
||
+ g_string_append_c (nevra, '.');
|
||
+ g_string_append (nevra, str);
|
||
+
|
||
+ known_app = g_hash_table_lookup (rpd->nevra_to_app, nevra->str);
|
||
+ if (known_app != NULL) {
|
||
+ upd_app = g_object_ref (known_app);
|
||
+ if (gs_app_get_version (upd_app) == NULL)
|
||
+ gs_dnf5_app_set_version (dict, upd_app, gs_app_set_version);
|
||
+ else
|
||
+ gs_dnf5_app_set_version (dict, upd_app, gs_app_set_update_version);
|
||
+
|
||
+ if (rpd->replaces != NULL) {
|
||
+ g_autoptr(GVariantDict) dict2 = NULL;
|
||
+
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ value = g_variant_get_child_value (item_array, 3);
|
||
+ dict2 = g_variant_dict_new (value);
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ value = g_variant_dict_lookup_value (dict2, "replaces", G_VARIANT_TYPE_ARRAY);
|
||
+ if (value != NULL) {
|
||
+ UpdateReplacesData upd = { 0, };
|
||
+
|
||
+ upd.app = upd_app;
|
||
+ upd.replaces = rpd->replaces;
|
||
+ upd.used_replaces = rpd->used_replaces;
|
||
+
|
||
+ if (!gs_dnf5_foreach_item (self, value, gs_dnf5_gather_update_replaced_pkgs_cb, &upd, cancellable, error))
|
||
+ return FALSE;
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ upd_app = gs_app_new (NULL);
|
||
+ gs_app_set_management_plugin (upd_app, GS_PLUGIN (self));
|
||
+ gs_app_set_metadata (upd_app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self)));
|
||
+ gs_plugin_dnf5_set_packaging_format (upd_app);
|
||
+ gs_app_set_kind (upd_app, AS_COMPONENT_KIND_GENERIC);
|
||
+ gs_app_set_bundle_kind (upd_app, AS_BUNDLE_KIND_PACKAGE);
|
||
+ gs_app_set_scope (upd_app, AS_COMPONENT_SCOPE_SYSTEM);
|
||
+ gs_app_add_quirk (upd_app, GS_APP_QUIRK_HIDE_EVERYWHERE);
|
||
+ gs_app_add_quirk (upd_app, GS_APP_QUIRK_NEEDS_REBOOT);
|
||
+
|
||
+ gs_dnf5_app_set_str (dict, "name", upd_app, gs_app_add_source);
|
||
+ gs_dnf5_app_set_str2 (dict, "name", upd_app, gs_app_set_name);
|
||
+ gs_dnf5_app_set_version (dict, upd_app, gs_app_set_version);
|
||
+ gs_app_add_source_id (upd_app, nevra->str);
|
||
+
|
||
+ gs_app_list_add (rpd->list, upd_app);
|
||
+ g_hash_table_insert (rpd->nevra_to_app, g_string_free (g_steal_pointer (&nevra), FALSE), g_object_ref (upd_app));
|
||
+ }
|
||
+
|
||
+ gs_dnf5_app_set_size (dict, "install_size", upd_app, gs_app_set_size_installed);
|
||
+ gs_dnf5_app_set_size (dict, "download_size", upd_app, gs_app_set_size_download);
|
||
+
|
||
+ switch (op_kind) {
|
||
+ case OP_KIND_INSTALL:
|
||
+ gs_app_set_state (upd_app, GS_APP_STATE_AVAILABLE);
|
||
+ break;
|
||
+ case OP_KIND_UPGRADE:
|
||
+ case OP_KIND_DOWNGRADE:
|
||
+ case OP_KIND_REINSTALL:
|
||
+ gs_app_set_state (upd_app, GS_APP_STATE_UPDATABLE);
|
||
+ break;
|
||
+ case OP_KIND_REMOVE:
|
||
+ case OP_KIND_REPLACED:
|
||
+ gs_app_set_state (upd_app, GS_APP_STATE_UNAVAILABLE);
|
||
+ gs_app_set_size_download (upd_app, GS_SIZE_TYPE_VALID, 0);
|
||
+ break;
|
||
+ case OP_KIND_UNKNOWN:
|
||
+ default:
|
||
+ g_warn_if_reached ();
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_read_history_packages_cb (GsPluginDnf5 *self,
|
||
+ GVariant *array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ ReadPackageData *rpd = user_data;
|
||
+ g_autoptr(GsApp) app = NULL;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+
|
||
+ dict = g_variant_dict_new (array);
|
||
+
|
||
+ app = gs_dnf5_read_package_from_dict (self, dict, GS_DNF5_READ_PACKAGE_FLAG_NONE, rpd->set_state);
|
||
+ if (app != NULL) {
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+ value = g_variant_dict_lookup_value (dict, "original_evr", G_VARIANT_TYPE_STRING);
|
||
+ if (value != NULL) {
|
||
+ const gchar *str = g_variant_get_string (value, NULL);
|
||
+ if (str != NULL) {
|
||
+ const gchar *delim = strchr (str, ':');
|
||
+ /* skip the epoch, if set, because it's skipped in the current version too */
|
||
+ gs_app_set_version (app, delim ? delim + 1 : str);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+ value = g_variant_dict_lookup_value (dict, "advisories", G_VARIANT_TYPE_ARRAY);
|
||
+ if (value != NULL) {
|
||
+ gs_app_set_metadata_variant (app, GS_DNF5_ADVISORIES_KEY, value);
|
||
+ }
|
||
+
|
||
+ /* do not know the exact change date/time, it uses the build time and the one from the settings as a fallback */
|
||
+ if (!gs_app_get_install_date (app))
|
||
+ gs_app_set_install_date (app, rpd->timestamp);
|
||
+ gs_app_list_add (rpd->list, app);
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_read_history_items_cb (GsPluginDnf5 *self,
|
||
+ GVariant *array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ struct _known_changes {
|
||
+ const gchar *type_of_change;
|
||
+ GsAppState set_state;
|
||
+ } known_changes[] = {
|
||
+ { "installed", GS_APP_STATE_UPDATABLE },
|
||
+ { "removed", GS_APP_STATE_UNAVAILABLE },
|
||
+ { "upgraded", GS_APP_STATE_UPDATABLE },
|
||
+ { "downgraded", GS_APP_STATE_UPDATABLE }
|
||
+ };
|
||
+ ReadPackageData *rpd = user_data;
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+ const gchar *type_of_change;
|
||
+ guint ii;
|
||
+
|
||
+ value = g_variant_get_child_value (array, 0);
|
||
+ if (value == NULL)
|
||
+ return TRUE;
|
||
+
|
||
+ type_of_change = g_variant_get_string (value, NULL);
|
||
+ if (!type_of_change)
|
||
+ return TRUE;
|
||
+
|
||
+ for (ii = 0; ii < G_N_ELEMENTS (known_changes); ii++) {
|
||
+ if (g_strcmp0 (type_of_change, known_changes[ii].type_of_change) == 0) {
|
||
+ g_autoptr(GVariant) packages = NULL;
|
||
+ packages = g_variant_get_child_value (array, 1);
|
||
+ if (packages != NULL) {
|
||
+ rpd->set_state = known_changes[ii].set_state;
|
||
+ gs_dnf5_foreach_item (self, packages, gs_dnf5_read_history_packages_cb, rpd, cancellable, error);
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_list_apps_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsAppQueryTristate is_for_update = GS_APP_QUERY_TRISTATE_UNSET;
|
||
+ GsAppQueryTristate is_historical_update = GS_APP_QUERY_TRISTATE_UNSET;
|
||
+ const AsComponentKind *component_kinds = NULL;
|
||
+ GsPluginListAppsData *data = task_data;
|
||
+ const gchar * const *provides_files = NULL;
|
||
+ const gchar *provides_tag = NULL;
|
||
+ GsAppQueryProvidesType provides_type = GS_APP_QUERY_PROVIDES_UNKNOWN;
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GsAppList) list = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ gboolean success = TRUE;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ if (data->query != NULL) {
|
||
+ is_for_update = gs_app_query_get_is_for_update (data->query);
|
||
+ is_historical_update = gs_app_query_get_is_historical_update (data->query);
|
||
+ component_kinds = gs_app_query_get_component_kinds (data->query);
|
||
+ provides_files = gs_app_query_get_provides_files (data->query);
|
||
+ provides_type = gs_app_query_get_provides (data->query, &provides_tag);
|
||
+ }
|
||
+
|
||
+ if ((is_for_update == GS_APP_QUERY_TRISTATE_UNSET &&
|
||
+ is_historical_update == GS_APP_QUERY_TRISTATE_UNSET &&
|
||
+ component_kinds == NULL &&
|
||
+ provides_tag == NULL &&
|
||
+ provides_files == NULL) ||
|
||
+ is_for_update == GS_APP_QUERY_TRISTATE_FALSE ||
|
||
+ is_historical_update == GS_APP_QUERY_TRISTATE_FALSE ||
|
||
+ (component_kinds != NULL && !gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) ||
|
||
+ gs_app_query_get_n_properties_set (data->query) != 1) {
|
||
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||
+ "Unsupported query");
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error);
|
||
+ if (session_path == NULL) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = rpm_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: ");
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT);
|
||
+ list = gs_app_list_new ();
|
||
+
|
||
+ if (is_for_update == GS_APP_QUERY_TRISTATE_TRUE) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "package_attrs",
|
||
+ gs_dnf5_dup_package_attrs ());
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_provides",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_filenames",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_src",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "scope",
|
||
+ g_variant_new_string ("upgrades"));
|
||
+
|
||
+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &result,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+
|
||
+ if (success) {
|
||
+ g_autoptr(GHashTable) nevra_to_app = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
|
||
+ g_autoptr(GHashTable) replaces = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
|
||
+ g_autoptr(GHashTable) used_replaces = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||
+ ReadPackageData rpd = { 0, };
|
||
+ rpd.list = list;
|
||
+ rpd.set_state = GS_APP_STATE_UPDATABLE;
|
||
+ rpd.nevra_to_app = nevra_to_app;
|
||
+ rpd.replaces = replaces;
|
||
+ rpd.used_replaces = used_replaces;
|
||
+
|
||
+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error);
|
||
+
|
||
+ if (success) {
|
||
+ g_autoptr(GVariantBuilder) options_builder2 = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_autoptr(GVariant) transaction = NULL;
|
||
+ const gchar * const packages[2] = { NULL, NULL };
|
||
+
|
||
+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, &local_error);
|
||
+
|
||
+ success = success &&
|
||
+ gs_dnf5_rpm_rpm_call_upgrade_sync (rpm_proxy,
|
||
+ packages,
|
||
+ g_variant_builder_end (options_builder2),
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = success &&
|
||
+ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, NULL,
|
||
+ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY,
|
||
+ &transaction, cancellable, &local_error);
|
||
+ if (success && transaction != NULL) {
|
||
+ success = gs_dnf5_foreach_item (self, transaction,
|
||
+ gs_dnf5_gather_update_replaces_cb, &rpd,
|
||
+ cancellable, &local_error);
|
||
+ rpd.cover_unused_replaces = FALSE;
|
||
+ success = success &&
|
||
+ gs_dnf5_foreach_item (self, transaction,
|
||
+ gs_dnf5_gather_updates_from_transaction_cb, &rpd,
|
||
+ cancellable, &local_error);
|
||
+ rpd.cover_unused_replaces = TRUE;
|
||
+ success = success &&
|
||
+ gs_dnf5_foreach_item (self, transaction,
|
||
+ gs_dnf5_gather_updates_from_transaction_cb, &rpd,
|
||
+ cancellable, &local_error);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (success && g_hash_table_size (nevra_to_app) > 0) {
|
||
+ g_autoptr(GsDnf5Advisory) advisory_proxy = NULL;
|
||
+ g_autoptr(GError) local_error2 = NULL;
|
||
+ gboolean adv_success;
|
||
+
|
||
+ advisory_proxy = gs_dnf5_advisory_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error2);
|
||
+ adv_success = advisory_proxy != NULL;
|
||
+
|
||
+ if (adv_success) {
|
||
+ g_autoptr(GVariantBuilder) adv_options_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) attrs_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) severities_builder = NULL;
|
||
+ g_autoptr(GVariant) adv_result = NULL;
|
||
+
|
||
+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (attrs_builder, "s", "severity");
|
||
+ g_variant_builder_add (attrs_builder, "s", "collections");
|
||
+
|
||
+ severities_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (severities_builder, "s", "critical");
|
||
+
|
||
+ adv_options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (adv_options_builder, "{sv}", "advisory_attrs",
|
||
+ g_variant_builder_end (attrs_builder));
|
||
+ g_variant_builder_add (adv_options_builder, "{sv}", "availability",
|
||
+ g_variant_new_string ("updates"));
|
||
+ g_variant_builder_add (adv_options_builder, "{sv}", "severities",
|
||
+ g_variant_builder_end (severities_builder));
|
||
+
|
||
+ adv_success = gs_dnf5_advisory_call_list_sync (advisory_proxy,
|
||
+ g_variant_builder_end (adv_options_builder),
|
||
+ &adv_result,
|
||
+ cancellable,
|
||
+ &local_error2);
|
||
+ if (adv_success) {
|
||
+ adv_success = gs_dnf5_foreach_item (self, adv_result,
|
||
+ gs_dnf5_refine_from_advisory_cb, nevra_to_app,
|
||
+ cancellable, &local_error2);
|
||
+ } else {
|
||
+ g_debug ("Failed to call Advisory::list: %s", local_error2->message);
|
||
+ }
|
||
+ } else {
|
||
+ g_debug ("Failed to create Advisory proxy: %s", local_error2->message);
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call Rpm::list: ");
|
||
+ }
|
||
+ } else if (is_historical_update == GS_APP_QUERY_TRISTATE_TRUE) {
|
||
+ g_autoptr(GSettings) settings = NULL;
|
||
+ gint64 timestamp;
|
||
+
|
||
+ /* use the option the GUI part uses */
|
||
+ settings = g_settings_new ("org.gnome.software");
|
||
+ timestamp = (gint64) g_settings_get_uint64 (settings, "packagekit-historical-updates-timestamp");
|
||
+
|
||
+ if (timestamp > 0) {
|
||
+ g_autoptr(GsDnf5History) history_proxy = NULL;
|
||
+
|
||
+ history_proxy = gs_dnf5_history_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = history_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to create History proxy: ");
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "since",
|
||
+ g_variant_new_int64 (timestamp));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "package_attrs",
|
||
+ gs_dnf5_dup_package_attrs ());
|
||
+
|
||
+ success = gs_dnf5_history_call_recent_changes_sync (history_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &result,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+
|
||
+ if (success) {
|
||
+ ReadPackageData rpd = { 0, };
|
||
+
|
||
+ rpd.list = list;
|
||
+ rpd.set_state = GS_APP_STATE_UNKNOWN;
|
||
+ rpd.nevra_to_app = NULL;
|
||
+ rpd.timestamp = timestamp;
|
||
+
|
||
+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_history_items_cb, &rpd, cancellable, &local_error);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call recent_changes: ");
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ } else if (gs_component_kind_array_contains (component_kinds, AS_COMPONENT_KIND_REPOSITORY)) {
|
||
+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL;
|
||
+
|
||
+ repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = repo_proxy != NULL;
|
||
+ if (success) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) attrs_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (repo_proxy), G_MAXINT);
|
||
+
|
||
+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (attrs_builder, "s", "id");
|
||
+ g_variant_builder_add (attrs_builder, "s", "name");
|
||
+ g_variant_builder_add (attrs_builder, "s", "enabled");
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "repo_attrs",
|
||
+ g_variant_builder_end (attrs_builder));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "enable_disable",
|
||
+ g_variant_new_string ("all"));
|
||
+
|
||
+ success = gs_dnf5_rpm_repo_call_list_sync (repo_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &result,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+
|
||
+ if (success) {
|
||
+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_repo_cb, list, cancellable, &local_error);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call list: ");
|
||
+ }
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to create Rpm repo proxy: ");
|
||
+ }
|
||
+ } else if (provides_files != NULL) {
|
||
+ g_autoptr(GVariantBuilder) patterns_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+
|
||
+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ for (guint i = 0; provides_files[i] != NULL; i++) {
|
||
+ g_variant_builder_add (patterns_builder, "s", provides_files[i]);
|
||
+ }
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "scope",
|
||
+ g_variant_new_string ("all"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_nevra",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_provides",
|
||
+ g_variant_new_boolean (TRUE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_filenames",
|
||
+ g_variant_new_boolean (TRUE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_src",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "package_attrs",
|
||
+ gs_dnf5_dup_package_attrs ());
|
||
+ g_variant_builder_add (options_builder, "{sv}", "patterns",
|
||
+ g_variant_builder_end (patterns_builder));
|
||
+
|
||
+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &result,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+
|
||
+ if (success) {
|
||
+ ReadPackageData rpd = { 0, };
|
||
+ rpd.list = list;
|
||
+ rpd.set_state = GS_APP_STATE_UNKNOWN;
|
||
+ rpd.nevra_to_app = NULL;
|
||
+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call list: ");
|
||
+ }
|
||
+ } else if (provides_tag != NULL) {
|
||
+ g_autoptr(GVariantBuilder) patterns_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+ g_autofree gchar *pattern1 = NULL, *pattern2 = NULL;
|
||
+
|
||
+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+
|
||
+ switch (provides_type) {
|
||
+ case GS_APP_QUERY_PROVIDES_PACKAGE_NAME:
|
||
+ g_variant_builder_add (patterns_builder, "s", provides_tag);
|
||
+ break;
|
||
+ case GS_APP_QUERY_PROVIDES_GSTREAMER:
|
||
+ pattern1 = g_strdup_printf ("gstreamer0.10(%s)", provides_tag);
|
||
+ pattern2 = g_strdup_printf ("gstreamer1(%s)", provides_tag);
|
||
+ break;
|
||
+ case GS_APP_QUERY_PROVIDES_FONT:
|
||
+ pattern1 = g_strdup_printf ("font(%s)", provides_tag);
|
||
+ break;
|
||
+ case GS_APP_QUERY_PROVIDES_MIME_HANDLER:
|
||
+ pattern1 = g_strdup_printf ("mimehandler(%s)", provides_tag);
|
||
+ break;
|
||
+ case GS_APP_QUERY_PROVIDES_PS_DRIVER:
|
||
+ pattern1 = g_strdup_printf ("postscriptdriver(%s)", provides_tag);
|
||
+ break;
|
||
+ case GS_APP_QUERY_PROVIDES_PLASMA:
|
||
+ pattern1 = g_strdup_printf ("plasma4(%s)", provides_tag);
|
||
+ pattern2 = g_strdup_printf ("plasma5(%s)", provides_tag);
|
||
+ break;
|
||
+ case GS_APP_QUERY_PROVIDES_UNKNOWN:
|
||
+ default:
|
||
+ g_assert_not_reached ();
|
||
+ }
|
||
+ if (pattern1 != NULL)
|
||
+ g_variant_builder_add (patterns_builder, "s", pattern1);
|
||
+ if (pattern2 != NULL)
|
||
+ g_variant_builder_add (patterns_builder, "s", pattern2);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "scope",
|
||
+ g_variant_new_string ("all"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_nevra",
|
||
+ g_variant_new_boolean (provides_type == GS_APP_QUERY_PROVIDES_PACKAGE_NAME));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_provides",
|
||
+ g_variant_new_boolean (provides_type != GS_APP_QUERY_PROVIDES_PACKAGE_NAME));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_filenames",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_src",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "package_attrs",
|
||
+ gs_dnf5_dup_package_attrs ());
|
||
+ g_variant_builder_add (options_builder, "{sv}", "patterns",
|
||
+ g_variant_builder_end (patterns_builder));
|
||
+
|
||
+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &result,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+
|
||
+ if (success) {
|
||
+ ReadPackageData rpd = { 0, };
|
||
+ rpd.list = list;
|
||
+ rpd.set_state = GS_APP_STATE_UNKNOWN;
|
||
+ rpd.nevra_to_app = NULL;
|
||
+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, &local_error);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call list: ");
|
||
+ }
|
||
+ } else {
|
||
+ g_assert_not_reached ();
|
||
+ }
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+
|
||
+ if (success) {
|
||
+ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_list_apps_async (GsPlugin *plugin,
|
||
+ GsAppQuery *query,
|
||
+ GsPluginListAppsFlags flags,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ gboolean interactive = (flags & GS_PLUGIN_LIST_APPS_FLAGS_INTERACTIVE) != 0;
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+
|
||
+ task = gs_plugin_list_apps_data_new_task (plugin, query, flags, event_callback, event_user_data,
|
||
+ cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_list_apps_async);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_list_apps_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static GsAppList *
|
||
+gs_plugin_dnf5_list_apps_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_pointer (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_gather_dependency_sizes_cb (GsPluginDnf5 *self,
|
||
+ GVariant *item_array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GsApp *main_app = user_data;
|
||
+ const gchar *str;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+ g_autoptr(GsApp) dep_app = NULL;
|
||
+
|
||
+ g_variant_get_child (item_array, 0, "&s", &str);
|
||
+ if (str == NULL || g_ascii_strcasecmp (str, "package") != 0)
|
||
+ return TRUE;
|
||
+
|
||
+ g_variant_get_child (item_array, 2, "&s", &str);
|
||
+ if (str == NULL || g_ascii_strcasecmp (str, "dependency") != 0)
|
||
+ return TRUE;
|
||
+
|
||
+ value = g_variant_get_child_value (item_array, 4);
|
||
+ dict = g_variant_dict_new (value);
|
||
+ g_clear_pointer (&value, g_variant_unref);
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING);
|
||
+ if (value == NULL)
|
||
+ return TRUE;
|
||
+ str = g_variant_get_string (value, NULL);
|
||
+ if (str == NULL)
|
||
+ return TRUE;
|
||
+ /* the app itself is in the list too */
|
||
+ if (g_strcmp0 (gs_app_get_default_source (main_app), str) == 0)
|
||
+ return TRUE;
|
||
+
|
||
+ /* always create a new app, no cached can be used here */
|
||
+ dep_app = gs_app_new (NULL);
|
||
+ gs_app_set_management_plugin (dep_app, GS_PLUGIN (self));
|
||
+ gs_app_set_metadata (dep_app, "GnomeSoftware::Creator", gs_plugin_get_name (GS_PLUGIN (self)));
|
||
+ gs_plugin_dnf5_set_packaging_format (dep_app);
|
||
+ gs_app_set_kind (dep_app, AS_COMPONENT_KIND_GENERIC);
|
||
+ gs_app_set_bundle_kind (dep_app, AS_BUNDLE_KIND_PACKAGE);
|
||
+ gs_app_set_scope (dep_app, AS_COMPONENT_SCOPE_SYSTEM);
|
||
+ gs_app_add_quirk (dep_app, GS_APP_QUIRK_HIDE_EVERYWHERE);
|
||
+
|
||
+ gs_dnf5_app_set_str (dict, "name", dep_app, gs_app_add_source);
|
||
+ gs_dnf5_app_set_str (dict, "nevra", dep_app, gs_app_add_source_id);
|
||
+ gs_dnf5_app_set_str2 (dict, "name", dep_app, gs_app_set_name);
|
||
+ gs_dnf5_app_set_str2 (dict, "summary", dep_app, gs_app_set_summary);
|
||
+ gs_dnf5_app_set_str2 (dict, "description", dep_app, gs_app_set_description);
|
||
+ gs_dnf5_app_set_str2 (dict, "license", dep_app, gs_app_set_license);
|
||
+ gs_dnf5_app_set_size (dict, "install_size", dep_app, gs_app_set_size_installed);
|
||
+ gs_dnf5_app_set_size (dict, "download_size", dep_app, gs_app_set_size_download);
|
||
+
|
||
+ gs_dnf5_update_app_state (dep_app, GS_APP_STATE_UNKNOWN, dict);
|
||
+
|
||
+ gs_app_add_related (main_app, dep_app);
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gpointer
|
||
+gs_dnf5_dependency_sizes_thread (gpointer thread_data)
|
||
+{
|
||
+ g_autofree GWeakRef *weakref = thread_data;
|
||
+ g_autoptr(GsPluginDnf5) self = g_weak_ref_get (weakref);
|
||
+ g_autoptr(GCancellable) cancellable = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ guint index = 0;
|
||
+
|
||
+ if (self == NULL)
|
||
+ return NULL;
|
||
+
|
||
+ g_mutex_lock (&self->dependency_sizes_data.mutex);
|
||
+
|
||
+ if (self->dependency_sizes_data.cancellable != NULL)
|
||
+ cancellable = g_object_ref (self->dependency_sizes_data.cancellable);
|
||
+
|
||
+ while (self->dependency_sizes_data.apps != NULL && index < self->dependency_sizes_data.apps->len &&
|
||
+ !g_cancellable_is_cancelled (self->dependency_sizes_data.cancellable)) {
|
||
+ GsApp *app = g_ptr_array_index (self->dependency_sizes_data.apps, index);
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL;
|
||
+ gboolean success;
|
||
+
|
||
+ index++;
|
||
+
|
||
+ if (gs_app_is_installed (app) ||
|
||
+ gs_app_list_length (gs_app_get_related (app)) > 0)
|
||
+ continue;
|
||
+
|
||
+ g_mutex_unlock (&self->dependency_sizes_data.mutex);
|
||
+
|
||
+ /* need to use temporary session, because the goals are piled up and cannot be cancelled yet */
|
||
+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error);
|
||
+
|
||
+ success = session_path != NULL;
|
||
+ if (!success)
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+
|
||
+ if (success) {
|
||
+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = rpm_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: ");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_autoptr(GVariant) transaction = NULL;
|
||
+ const gchar *package_name = gs_app_get_default_source (app);
|
||
+ const gchar *packages[2] = { NULL, NULL };
|
||
+
|
||
+ packages[0] = package_name;
|
||
+
|
||
+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, &local_error);
|
||
+
|
||
+ success = success &&
|
||
+ gs_dnf5_rpm_rpm_call_install_sync (rpm_proxy,
|
||
+ (const gchar * const *) packages,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = success &&
|
||
+ gs_dnf5_run_transaction (self, session_path, rpm_proxy, NULL, NULL,
|
||
+ GS_DNF5_TRANSACTION_FLAG_RESOLVE_ONLY,
|
||
+ &transaction, cancellable, &local_error);
|
||
+ if (success && transaction != NULL)
|
||
+ success = gs_dnf5_foreach_item (self, transaction, gs_dnf5_gather_dependency_sizes_cb, app, cancellable, &local_error);
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+
|
||
+ g_mutex_lock (&self->dependency_sizes_data.mutex);
|
||
+
|
||
+ if (!success)
|
||
+ break;
|
||
+ }
|
||
+
|
||
+ g_clear_pointer (&self->dependency_sizes_data.apps, g_ptr_array_unref);
|
||
+ g_clear_object (&self->dependency_sizes_data.cancellable);
|
||
+ g_mutex_unlock (&self->dependency_sizes_data.mutex);
|
||
+
|
||
+ if (local_error != NULL)
|
||
+ g_debug ("Failed to get app dependency sizes: %s", local_error->message);
|
||
+
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_get_dependency_sizes_sync (GsPluginDnf5 *self,
|
||
+ GHashTable *apps,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&self->session_data.mutex);
|
||
+ GHashTableIter iter;
|
||
+ gpointer value = NULL;
|
||
+ gboolean need_thread = FALSE;
|
||
+
|
||
+ g_hash_table_iter_init (&iter, apps);
|
||
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
|
||
+ GsApp *app = value;
|
||
+ GsAppList *related = gs_app_get_related (app);
|
||
+
|
||
+ if (gs_app_has_quirk (app, GS_APP_QUIRK_HIDE_EVERYWHERE) ||
|
||
+ gs_app_get_state (app) == GS_APP_STATE_AVAILABLE_LOCAL ||
|
||
+ gs_app_is_installed (app) ||
|
||
+ gs_app_list_length (related) > 0)
|
||
+ continue;
|
||
+
|
||
+ /* getting list of the dependencies to be installed can take a long time, thus run it
|
||
+ in a new thread, to not block the refine job */
|
||
+ if (self->dependency_sizes_data.apps == NULL) {
|
||
+ need_thread = TRUE;
|
||
+ self->dependency_sizes_data.apps = g_ptr_array_new_with_free_func (g_object_unref);
|
||
+ }
|
||
+
|
||
+ if (!g_ptr_array_find (self->dependency_sizes_data.apps, app, NULL))
|
||
+ g_ptr_array_add (self->dependency_sizes_data.apps, g_object_ref (app));
|
||
+ }
|
||
+
|
||
+ if (need_thread) {
|
||
+ GWeakRef *weakref = g_new0 (GWeakRef, 1);
|
||
+
|
||
+ g_assert (self->dependency_sizes_data.cancellable == NULL);
|
||
+ self->dependency_sizes_data.cancellable = g_cancellable_new ();
|
||
+
|
||
+ g_weak_ref_set (weakref, self);
|
||
+
|
||
+ g_thread_unref (g_thread_new ("gs-dnf5-dependency-sizes", gs_dnf5_dependency_sizes_thread, g_steal_pointer (&weakref)));
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_refine_cb (GsPluginDnf5 *self,
|
||
+ GVariant *package_array,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GHashTable *apps = user_data;
|
||
+ const gchar *name;
|
||
+ GsApp *app;
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) value = NULL;
|
||
+
|
||
+ dict = g_variant_dict_new (package_array);
|
||
+
|
||
+ value = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING);
|
||
+ if (value == NULL)
|
||
+ return TRUE;
|
||
+ name = g_variant_get_string (value, NULL);
|
||
+ if (name == NULL)
|
||
+ return TRUE;
|
||
+ app = g_hash_table_lookup (apps, name);
|
||
+ if (app == NULL)
|
||
+ return TRUE;
|
||
+
|
||
+ if (gs_app_has_management_plugin (app, NULL) &&
|
||
+ gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE &&
|
||
+ gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) {
|
||
+ gs_app_set_management_plugin (app, GS_PLUGIN (self));
|
||
+ gs_plugin_dnf5_set_packaging_format (app);
|
||
+ }
|
||
+
|
||
+ gs_dnf5_app_set_version (dict, app, gs_app_set_version);
|
||
+ gs_dnf5_app_set_size (dict, "install_size", app, gs_app_set_size_installed);
|
||
+ gs_dnf5_app_set_size (dict, "download_size", app, gs_app_set_size_download);
|
||
+
|
||
+ gs_dnf5_update_app_state (app, GS_APP_STATE_UNKNOWN, dict);
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_add_advisories_cb (GsPluginDnf5 *self,
|
||
+ GVariant *value,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GHashTable *advisories = user_data; /* gchar *name ~> gchar *description */
|
||
+ g_autofree gchar *str = NULL;
|
||
+
|
||
+ str = g_variant_dup_string (value, NULL);
|
||
+ if (str != NULL && *str != '\0')
|
||
+ g_hash_table_insert (advisories, g_steal_pointer (&str), NULL);
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_gather_update_description_for_advisory_cb (GsPluginDnf5 *self,
|
||
+ GVariant *value,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GHashTable *advisories = user_data; /* gchar *name ~> gchar *description */
|
||
+ g_autoptr(GVariantDict) dict = NULL;
|
||
+ g_autoptr(GVariant) name = NULL;
|
||
+ g_autoptr(GVariant) description = NULL;
|
||
+ const gchar *name_str, *description_str;
|
||
+
|
||
+ dict = g_variant_dict_new (value);
|
||
+
|
||
+ name = g_variant_dict_lookup_value (dict, "name", G_VARIANT_TYPE_STRING);
|
||
+ if (name == NULL)
|
||
+ return TRUE;
|
||
+
|
||
+ description = g_variant_dict_lookup_value (dict, "description", G_VARIANT_TYPE_STRING);
|
||
+ if (description == NULL)
|
||
+ return TRUE;
|
||
+
|
||
+ name_str = g_variant_get_string (name, NULL);
|
||
+ description_str = g_variant_get_string (description, NULL);
|
||
+
|
||
+ if (name_str != NULL && description_str != NULL && *description_str != '\0')
|
||
+ g_hash_table_replace (advisories, g_strdup (name_str), g_strdup (description_str));
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+typedef struct _AdvisoryUpdateDescriptionData {
|
||
+ GHashTable *advisories; /* gchar *name ~> gchar *description */
|
||
+ GString *description;
|
||
+} AdvisoryUpdateDescriptionData;
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_refine_update_description_from_advisory_cb (GsPluginDnf5 *self,
|
||
+ GVariant *value,
|
||
+ gpointer user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ AdvisoryUpdateDescriptionData *adv_data = user_data;
|
||
+ const gchar *name = NULL;
|
||
+
|
||
+ name = g_variant_get_string (value, NULL);
|
||
+ if (name != NULL && *name != '\0') {
|
||
+ const gchar *description = g_hash_table_lookup (adv_data->advisories, name);
|
||
+ if (description != NULL && *description != '\0') {
|
||
+ if (adv_data->description == NULL) {
|
||
+ adv_data->description = g_string_new (description);
|
||
+ } else {
|
||
+ g_string_append (adv_data->description, "\n\n");
|
||
+ g_string_append (adv_data->description, description);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_refine_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPlugin *plugin = GS_PLUGIN (self);
|
||
+ GsPluginRefineData *data = task_data;
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GHashTable) apps = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ gboolean success = TRUE;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
|
||
+ for (guint i = 0; i < gs_app_list_length (data->list); i++) {
|
||
+ GsApp *app = gs_app_list_index (data->list, i);
|
||
+ if (!gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD) && (
|
||
+ gs_app_get_state (app) == GS_APP_STATE_UNKNOWN ||
|
||
+ (data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_UPDATE_DETAILS) != 0 ||
|
||
+ ((data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_SIZE_DATA) != 0 &&
|
||
+ gs_app_list_length (gs_app_get_related (app)) == 0))) {
|
||
+ if (gs_app_get_default_source (app) != NULL &&
|
||
+ ((gs_app_has_management_plugin (app, NULL) &&
|
||
+ gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE &&
|
||
+ gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) ||
|
||
+ gs_app_has_management_plugin (app, plugin)))
|
||
+ g_hash_table_insert (apps, g_strdup (gs_app_get_default_source (app)), g_object_ref (app));
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (g_hash_table_size (apps) == 0) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error);
|
||
+ if (session_path == NULL) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = rpm_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: ");
|
||
+ }
|
||
+
|
||
+ if (success && (data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_UPDATE_DETAILS) != 0) {
|
||
+ g_autoptr(GHashTable) advisories = NULL; /* gchar *name ~> gchar *description */
|
||
+ GHashTableIter iter;
|
||
+ gpointer value = NULL;
|
||
+
|
||
+ data->require_flags = data->require_flags & (~GS_PLUGIN_REFINE_REQUIRE_FLAGS_UPDATE_DETAILS);
|
||
+
|
||
+ advisories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||
+
|
||
+ g_hash_table_iter_init (&iter, apps);
|
||
+ while (success && g_hash_table_iter_next (&iter, NULL, &value)) {
|
||
+ GsApp *app = value;
|
||
+ GVariant *advisories_array;
|
||
+
|
||
+ advisories_array = gs_app_get_metadata_variant (app, GS_DNF5_ADVISORIES_KEY);
|
||
+ if (advisories_array != NULL)
|
||
+ success = gs_dnf5_foreach_item (self, advisories_array, gs_dnf5_add_advisories_cb, advisories, cancellable, &local_error);
|
||
+ }
|
||
+
|
||
+ if (success && g_hash_table_size (advisories) > 0) {
|
||
+ g_autoptr(GsDnf5Advisory) advisory_proxy = NULL;
|
||
+
|
||
+ advisory_proxy = gs_dnf5_advisory_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = advisory_proxy != NULL;
|
||
+
|
||
+ if (success) {
|
||
+ g_autoptr(GVariantBuilder) adv_options_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) attrs_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) names_builder = NULL;
|
||
+ g_autoptr(GVariant) adv_result = NULL;
|
||
+ gpointer key = NULL;
|
||
+
|
||
+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (attrs_builder, "s", "name");
|
||
+ g_variant_builder_add (attrs_builder, "s", "description");
|
||
+
|
||
+ names_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_hash_table_iter_init (&iter, advisories);
|
||
+ while (g_hash_table_iter_next (&iter, &key, NULL)) {
|
||
+ const gchar *name = key;
|
||
+ g_variant_builder_add (names_builder, "s", name);
|
||
+ }
|
||
+
|
||
+ adv_options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (adv_options_builder, "{sv}", "advisory_attrs",
|
||
+ g_variant_builder_end (attrs_builder));
|
||
+ g_variant_builder_add (adv_options_builder, "{sv}", "names",
|
||
+ g_variant_builder_end (names_builder));
|
||
+ g_variant_builder_add (adv_options_builder, "{sv}", "availability",
|
||
+ g_variant_new_string ("all"));
|
||
+
|
||
+ success = gs_dnf5_advisory_call_list_sync (advisory_proxy,
|
||
+ g_variant_builder_end (adv_options_builder),
|
||
+ &adv_result,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ if (success) {
|
||
+ success = gs_dnf5_foreach_item (self, adv_result,
|
||
+ gs_dnf5_gather_update_description_for_advisory_cb, advisories,
|
||
+ cancellable, &local_error);
|
||
+ if (success) {
|
||
+ g_hash_table_iter_init (&iter, apps);
|
||
+ while (success && g_hash_table_iter_next (&iter, NULL, &value)) {
|
||
+ GsApp *app = value;
|
||
+ GVariant *advisories_array;
|
||
+
|
||
+ advisories_array = gs_app_get_metadata_variant (app, GS_DNF5_ADVISORIES_KEY);
|
||
+ if (advisories_array != NULL) {
|
||
+ AdvisoryUpdateDescriptionData adv_data = { NULL, };
|
||
+ adv_data.advisories = advisories;
|
||
+ adv_data.description = NULL;
|
||
+
|
||
+ success = gs_dnf5_foreach_item (self, advisories_array,
|
||
+ gs_dnf5_refine_update_description_from_advisory_cb,
|
||
+ &adv_data, cancellable, &local_error);
|
||
+
|
||
+ if (adv_data.description) {
|
||
+ g_autoptr(GsMarkdown) markdown = gs_markdown_new (GS_MARKDOWN_OUTPUT_PANGO);
|
||
+ g_autofree gchar *markup = gs_markdown_parse (markdown, adv_data.description->str);
|
||
+ if (markup != NULL && *markup != '\0')
|
||
+ gs_app_set_update_details_markup (app, markup);
|
||
+ else
|
||
+ gs_app_set_update_details_text (app, adv_data.description->str);
|
||
+ g_string_free (adv_data.description, TRUE);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (!success)
|
||
+ g_debug ("Failed to refine update description from advisory: %s", local_error->message);
|
||
+ } else {
|
||
+ g_debug ("Failed to gather update description for advisory: %s", local_error->message);
|
||
+ }
|
||
+ } else {
|
||
+ g_debug ("Failed to call Advisory::list: %s", local_error->message);
|
||
+ }
|
||
+ } else {
|
||
+ g_debug ("Failed to create Advisory proxy: %s", local_error->message);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* fallback to changelogs where advisory was not known */
|
||
+ g_hash_table_iter_init (&iter, apps);
|
||
+ while (success && g_hash_table_iter_next (&iter, NULL, &value)) {
|
||
+ GsApp *app = value;
|
||
+ if (!gs_app_get_update_details_markup (app)) {
|
||
+ const gchar *changelogs = gs_app_get_metadata_item (app, GS_DNF5_CHANGELOGS_KEY);
|
||
+ if (changelogs != NULL && *changelogs != '\0') {
|
||
+ gs_app_set_update_details_text (app, changelogs);
|
||
+ gs_app_set_metadata (app, GS_DNF5_CHANGELOGS_KEY, NULL);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (success && data->require_flags != 0) {
|
||
+ g_autoptr(GVariantBuilder) attrs_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) patterns_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+ GHashTableIter iter;
|
||
+ gpointer key = NULL;
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT);
|
||
+
|
||
+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_hash_table_iter_init (&iter, apps);
|
||
+ while (g_hash_table_iter_next (&iter, &key, NULL)) {
|
||
+ const gchar *name = key;
|
||
+ g_variant_builder_add (patterns_builder, "s", name);
|
||
+ }
|
||
+
|
||
+ attrs_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (attrs_builder, "s", "name");
|
||
+ g_variant_builder_add (attrs_builder, "s", "version");
|
||
+ g_variant_builder_add (attrs_builder, "s", "install_size");
|
||
+ g_variant_builder_add (attrs_builder, "s", "download_size");
|
||
+ g_variant_builder_add (attrs_builder, "s", "is_installed");
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "patterns",
|
||
+ g_variant_builder_end (patterns_builder));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "package_attrs",
|
||
+ g_variant_builder_end (attrs_builder));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_nevra",
|
||
+ g_variant_new_boolean (TRUE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_provides",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_filenames",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_src",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "scope",
|
||
+ g_variant_new_string ("all"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "latest_limit",
|
||
+ g_variant_new_int32 (1));
|
||
+
|
||
+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &result,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+
|
||
+ if (success) {
|
||
+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_refine_cb, apps, cancellable, &local_error);
|
||
+
|
||
+ if (success &&
|
||
+ (data->require_flags & GS_PLUGIN_REFINE_REQUIRE_FLAGS_SIZE_DATA) != 0) {
|
||
+ /* do not panic when cannot get dependency sizes for the apps */
|
||
+ if (!gs_dnf5_get_dependency_sizes_sync (self, apps, cancellable, &local_error)) {
|
||
+ g_debug ("Failed to get dependency sizes: %s", local_error->message);
|
||
+ g_clear_error (&local_error);
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call list: ");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+
|
||
+ if (success) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_refine_async (GsPlugin *plugin,
|
||
+ GsAppList *list,
|
||
+ GsPluginRefineFlags job_flags,
|
||
+ GsPluginRefineRequireFlags require_flags,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ gboolean interactive = (job_flags & GS_PLUGIN_REFINE_FLAGS_INTERACTIVE) != 0;
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+
|
||
+ task = gs_plugin_refine_data_new_task (plugin, list, job_flags, require_flags,
|
||
+ event_callback, event_user_data,
|
||
+ cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_refine_async);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_refine_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_refine_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_refresh_metadata_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPluginRefreshMetadataData *data = task_data;
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5Base) base_proxy = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ gboolean success = TRUE;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error);
|
||
+ if (session_path == NULL) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ base_proxy = gs_dnf5_base_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = base_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to create Base proxy: ");
|
||
+ }
|
||
+
|
||
+ if (success && data->cache_age_secs < 60) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autofree gchar *op_error_msg = NULL;
|
||
+ gboolean op_success = FALSE;
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+
|
||
+ g_variant_builder_add (options_builder, "{sv}", "interactive",
|
||
+ g_variant_new_boolean ((data->flags & GS_PLUGIN_REFRESH_METADATA_FLAGS_INTERACTIVE) != 0));
|
||
+
|
||
+ success = gs_dnf5_base_call_clean_with_options_sync (base_proxy,
|
||
+ "expire-cache",
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &op_success,
|
||
+ &op_error_msg,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ if (!success)
|
||
+ g_debug ("Failed to call expire-cache: %s", local_error->message);
|
||
+ else if (!op_success)
|
||
+ g_debug ("Failed to expire-cache: %s", op_error_msg);
|
||
+
|
||
+ /* ignore expire-cache error, only log about it */
|
||
+ g_clear_error (&local_error);
|
||
+
|
||
+ /* reset the session, to take the expiration into the effect */
|
||
+ success = gs_dnf5_reset_session_sync (self, session_path, cancellable, &local_error);
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ gboolean result = FALSE;
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (base_proxy), G_MAXINT);
|
||
+
|
||
+ success = gs_dnf5_base_call_read_all_repos_sync (base_proxy, &result, cancellable, &local_error);
|
||
+
|
||
+ /* 'result' is ignored here, due to no error provided */
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call read-all-repos: ");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+
|
||
+ if (success) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_refresh_metadata_async (GsPlugin *plugin,
|
||
+ guint64 cache_age_secs,
|
||
+ GsPluginRefreshMetadataFlags flags,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ gboolean interactive = (flags & GS_PLUGIN_REFRESH_METADATA_FLAGS_INTERACTIVE) != 0;
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+
|
||
+ task = g_task_new (self, cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_refresh_metadata_async);
|
||
+ g_task_set_task_data (task,
|
||
+ gs_plugin_refresh_metadata_data_new (cache_age_secs, flags, event_callback, event_user_data),
|
||
+ (GDestroyNotify) gs_plugin_refresh_metadata_data_free);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_refresh_metadata_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_refresh_metadata_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+void
|
||
+gs_plugin_adopt_app (GsPlugin *plugin,
|
||
+ GsApp *app)
|
||
+{
|
||
+ if (gs_app_get_bundle_kind (app) == AS_BUNDLE_KIND_PACKAGE &&
|
||
+ gs_app_get_scope (app) == AS_COMPONENT_SCOPE_SYSTEM) {
|
||
+ gs_app_set_management_plugin (app, plugin);
|
||
+ gs_plugin_dnf5_set_packaging_format (app);
|
||
+ return;
|
||
+ } else if (gs_app_get_kind (app) == AS_COMPONENT_KIND_OPERATING_SYSTEM) {
|
||
+ gs_app_set_management_plugin (app, plugin);
|
||
+ }
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_install_update (GsPlugin *plugin,
|
||
+ GsAppList *list,
|
||
+ gboolean is_install,
|
||
+ gboolean interactive,
|
||
+ gboolean can_apply,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GPtrArray) packages = NULL;
|
||
+ g_autoptr(GsAppList) used_apps = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL;
|
||
+ g_autoptr(GsApp) progress_app = NULL;
|
||
+ GsAppList *progress_list = is_install ? NULL : list;
|
||
+ gboolean success;
|
||
+
|
||
+ /* queue for install if installation needs the network */
|
||
+ if (!gs_plugin_get_network_available (plugin)) {
|
||
+ if (is_install) {
|
||
+ for (guint i = 0; i < gs_app_list_length (list); i++) {
|
||
+ GsApp *app = gs_app_list_index (list, i);
|
||
+ if (gs_app_has_management_plugin (app, plugin)) {
|
||
+ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) {
|
||
+ g_warn_if_reached ();
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ gs_app_set_state (app, GS_APP_STATE_QUEUED_FOR_INSTALL);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ return TRUE;
|
||
+ }
|
||
+
|
||
+ packages = g_ptr_array_new_with_free_func (g_free);
|
||
+ used_apps = gs_app_list_new ();
|
||
+
|
||
+ for (guint i = 0; i < gs_app_list_length (list); i++) {
|
||
+ GsApp *app = gs_app_list_index (list, i);
|
||
+ if (gs_app_has_management_plugin (app, plugin)) {
|
||
+ const gchar *source = gs_app_get_default_source (app);
|
||
+ if (source != NULL) {
|
||
+ GFile *local_file = gs_app_get_local_file (app);
|
||
+ if (local_file != NULL)
|
||
+ g_ptr_array_add (packages, g_file_get_uri (local_file));
|
||
+ else
|
||
+ g_ptr_array_add (packages, g_strdup (source));
|
||
+ gs_app_list_add (used_apps, app);
|
||
+ if (progress_list == NULL && progress_app == NULL)
|
||
+ progress_app = g_object_ref (app);
|
||
+ }
|
||
+ } else if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_PROXY)) {
|
||
+ GsAppList *related = gs_app_get_related (app);
|
||
+ gboolean is_os_update = gs_app_get_special_kind (app) == GS_APP_SPECIAL_KIND_OS_UPDATE;
|
||
+ for (guint j = 0; j < gs_app_list_length (related); j++) {
|
||
+ GsApp *rel_app = gs_app_list_index (related, j);
|
||
+ /* add only updatable packages into the list when it's an OS Update, otherwise
|
||
+ dnf5 claims "Packages for argument '$pkgname' available, but not installed." */
|
||
+ if (gs_app_has_management_plugin (rel_app, plugin) && (!is_install || !is_os_update ||
|
||
+ gs_app_get_state (rel_app) == GS_APP_STATE_UPDATABLE)) {
|
||
+ const gchar *source = gs_app_get_default_source (rel_app);
|
||
+ if (source != NULL) {
|
||
+ GFile *local_file = gs_app_get_local_file (rel_app);
|
||
+ if (local_file != NULL)
|
||
+ g_ptr_array_add (packages, g_file_get_uri (local_file));
|
||
+ else
|
||
+ g_ptr_array_add (packages, g_strdup (source));
|
||
+ gs_app_list_add (used_apps, rel_app);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ if (progress_list == NULL && progress_app == NULL)
|
||
+ progress_app = g_object_ref (app);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (packages->len == 0) {
|
||
+ g_set_error_literal (error,
|
||
+ GS_PLUGIN_ERROR,
|
||
+ GS_PLUGIN_ERROR_NOT_SUPPORTED,
|
||
+ "installing not available");
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, error);
|
||
+ if (session_path == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ for (guint i = 0; i < gs_app_list_length (used_apps); i++) {
|
||
+ GsApp *app = gs_app_list_index (used_apps, i);
|
||
+ g_autoptr(GsAppList) addons = NULL;
|
||
+
|
||
+ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) {
|
||
+ g_warn_if_reached ();
|
||
+ continue;
|
||
+ }
|
||
+
|
||
+ if (is_install) {
|
||
+ gs_app_set_state (app, GS_APP_STATE_INSTALLING);
|
||
+ addons = gs_app_dup_addons (app);
|
||
+ }
|
||
+ for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) {
|
||
+ GsApp *addon = gs_app_list_index (addons, j);
|
||
+ if (gs_app_get_to_be_installed (addon)) {
|
||
+ const gchar *addon_source = gs_app_get_default_source (app);
|
||
+ if (addon_source != NULL) {
|
||
+ g_ptr_array_add (packages, g_strdup (addon_source));
|
||
+ if (is_install)
|
||
+ gs_app_set_state (addon, GS_APP_STATE_INSTALLING);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ success = rpm_proxy != NULL;
|
||
+ if (success) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ GsDnf5TransactionFlags flags = (interactive ? GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE) |
|
||
+ (!can_apply ? GS_DNF5_TRANSACTION_FLAG_DOWNLOAD_ONLY : GS_DNF5_TRANSACTION_FLAG_NONE);
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "interactive",
|
||
+ g_variant_new_boolean (interactive));
|
||
+
|
||
+ /* NULL-terminate the array, to be like strv */
|
||
+ g_ptr_array_add (packages, NULL);
|
||
+
|
||
+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, error);
|
||
+
|
||
+ if (success && is_install) {
|
||
+ success = gs_dnf5_rpm_rpm_call_install_sync (rpm_proxy,
|
||
+ (const gchar * const *) packages->pdata,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ cancellable,
|
||
+ error);
|
||
+ } else if (success) {
|
||
+ /* ignore package list for update, it's an offline action, which should update
|
||
+ everything possible, not only a specific package */
|
||
+ const gchar * const all_packages[2] = { NULL, NULL };
|
||
+ success = gs_dnf5_rpm_rpm_call_upgrade_sync (rpm_proxy,
|
||
+ all_packages,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ cancellable,
|
||
+ error);
|
||
+ }
|
||
+ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, progress_list,
|
||
+ flags | (is_install ? 0 : GS_DNF5_TRANSACTION_FLAG_OFFLINE),
|
||
+ NULL, cancellable, error);
|
||
+ if (success && !is_install && can_apply) {
|
||
+ /* ensure the update is finished with the 'reboot' action */
|
||
+ g_autoptr(GsDnf5Offline) offline_proxy = NULL;
|
||
+
|
||
+ offline_proxy = gs_dnf5_offline_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ success = offline_proxy != NULL;
|
||
+ if (success) {
|
||
+ g_autofree gchar *op_error_msg = NULL;
|
||
+ gboolean op_success = FALSE;
|
||
+
|
||
+ g_clear_pointer (&options_builder, g_variant_builder_unref);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "interactive",
|
||
+ g_variant_new_boolean (interactive));
|
||
+
|
||
+ success = gs_dnf5_offline_call_set_finish_action_with_options_sync (offline_proxy,
|
||
+ "reboot",
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &op_success,
|
||
+ &op_error_msg,
|
||
+ cancellable,
|
||
+ error);
|
||
+ if (success && !op_success) {
|
||
+ success = FALSE;
|
||
+ if (op_error_msg != NULL) {
|
||
+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, op_error_msg);
|
||
+ g_prefix_error_literal (error, "Failed to set finish action: ");
|
||
+ }
|
||
+ } else if (!success) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to set finish action: ");
|
||
+ } else {
|
||
+ g_autoptr(GSettings) settings = g_settings_new ("org.gnome.software");
|
||
+ /* to check anything installed/updated after today */
|
||
+ g_settings_set (settings, "packagekit-historical-updates-timestamp", "t", g_get_real_time () / G_USEC_PER_SEC);
|
||
+ }
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Offline proxy: ");
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Rpm proxy: ");
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+
|
||
+ if (success) {
|
||
+ gs_dnf5_mark_session_needs_reset (self);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (error);
|
||
+
|
||
+ if (error != NULL && *error != NULL) {
|
||
+ GsApp *app = NULL;
|
||
+ if (gs_app_list_length (list) == 1)
|
||
+ app = gs_app_list_index (list, 0);
|
||
+
|
||
+ gs_dnf5_report_error (self, event_callback, event_user_data, app, *error, interactive);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ for (guint i = 0; i < gs_app_list_length (used_apps); i++) {
|
||
+ GsApp *app = gs_app_list_index (used_apps, i);
|
||
+ g_autoptr(GsAppList) addons = NULL;
|
||
+
|
||
+ if (gs_app_get_state (app) == GS_APP_STATE_INSTALLING) {
|
||
+ if (success) {
|
||
+ gs_app_set_state (app, GS_APP_STATE_INSTALLED);
|
||
+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0);
|
||
+ } else {
|
||
+ gs_app_set_state_recover (app);
|
||
+ }
|
||
+ } else if (success) {
|
||
+ /* apps prepared for offline update are downloaded */
|
||
+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0);
|
||
+ }
|
||
+
|
||
+ if (is_install)
|
||
+ addons = gs_app_dup_addons (app);
|
||
+ for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) {
|
||
+ GsApp *addon = gs_app_list_index (addons, j);
|
||
+ if (gs_app_get_state (addon) == GS_APP_STATE_INSTALLING) {
|
||
+ if (success) {
|
||
+ gs_app_set_state (addon, GS_APP_STATE_INSTALLED);
|
||
+ gs_app_set_size_download (addon, GS_SIZE_TYPE_VALID, 0);
|
||
+ } else {
|
||
+ gs_app_set_state_recover (addon);
|
||
+ }
|
||
+ } else if (success) {
|
||
+ /* apps prepared for offline update are downloaded */
|
||
+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return success;
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_install_apps_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPlugin *plugin = GS_PLUGIN (self);
|
||
+ GsPluginInstallAppsData *data = task_data;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ gboolean success;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ g_atomic_int_inc (&self->calling_rpm);
|
||
+
|
||
+ success = gs_dnf5_install_update (plugin, data->apps, TRUE,
|
||
+ (data->flags & GS_PLUGIN_INSTALL_APPS_FLAGS_INTERACTIVE) != 0,
|
||
+ (data->flags & GS_PLUGIN_INSTALL_APPS_FLAGS_NO_APPLY) == 0,
|
||
+ data->event_callback, data->event_user_data,
|
||
+ cancellable, &local_error);
|
||
+
|
||
+ g_atomic_int_dec_and_test (&self->calling_rpm);
|
||
+
|
||
+ if (success)
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ else
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_install_apps_async (GsPlugin *plugin,
|
||
+ GsAppList *apps,
|
||
+ GsPluginInstallAppsFlags flags,
|
||
+ GsPluginProgressCallback progress_callback,
|
||
+ gpointer progress_user_data,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GsPluginAppNeedsUserActionCallback app_needs_user_action_callback,
|
||
+ gpointer app_needs_user_action_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ gboolean interactive = (flags & GS_PLUGIN_INSTALL_APPS_FLAGS_INTERACTIVE) != 0;
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+
|
||
+ task = gs_plugin_install_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data,
|
||
+ event_callback, event_user_data,
|
||
+ app_needs_user_action_callback, app_needs_user_action_data,
|
||
+ cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_install_apps_async);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_install_apps_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_install_apps_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_uninstall_apps_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPlugin *plugin = GS_PLUGIN (self);
|
||
+ GsPluginUninstallAppsData *data = task_data;
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GPtrArray) touched_apps = NULL;
|
||
+ g_autoptr(GPtrArray) packages = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL;
|
||
+ g_autoptr(GsApp) progress_app = NULL;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ gboolean success;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ touched_apps = g_ptr_array_new_with_free_func (g_object_unref);
|
||
+ packages = g_ptr_array_new_with_free_func (g_free);
|
||
+
|
||
+ for (guint i = 0; i < gs_app_list_length (data->apps); i++) {
|
||
+ g_autoptr(GsAppList) addons = NULL;
|
||
+ GsApp *app = gs_app_list_index (data->apps, i);
|
||
+ const gchar *source;
|
||
+ if (!gs_app_has_management_plugin (app, plugin))
|
||
+ continue;
|
||
+ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY) {
|
||
+ g_warn_if_reached ();
|
||
+ continue;
|
||
+ }
|
||
+ source = gs_app_get_default_source (app);
|
||
+ if (source == NULL) {
|
||
+ g_task_return_new_error (task, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NOT_SUPPORTED,
|
||
+ "remove not available");
|
||
+ return;
|
||
+ }
|
||
+ g_ptr_array_add (packages, g_strdup (source));
|
||
+ g_ptr_array_add (touched_apps, g_object_ref (app));
|
||
+ gs_app_set_state (app, GS_APP_STATE_REMOVING);
|
||
+
|
||
+ if (progress_app == NULL)
|
||
+ progress_app = g_object_ref (app);
|
||
+
|
||
+ addons = gs_app_dup_addons (app);
|
||
+ for (guint j = 0; addons != NULL && j < gs_app_list_length (addons); j++) {
|
||
+ GsApp *addon = gs_app_list_index (addons, j);
|
||
+ if (gs_app_get_state (addon) == GS_APP_STATE_INSTALLED) {
|
||
+ const gchar *addon_source = gs_app_get_default_source (addon);
|
||
+ if (addon_source != NULL) {
|
||
+ g_ptr_array_add (packages, g_strdup (addon_source));
|
||
+ g_ptr_array_add (touched_apps, g_object_ref (addon));
|
||
+ gs_app_set_state (addon, GS_APP_STATE_REMOVING);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (packages->len == 0) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error);
|
||
+ if (session_path == NULL) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ g_atomic_int_inc (&self->calling_rpm);
|
||
+
|
||
+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = rpm_proxy != NULL;
|
||
+ if (success) {
|
||
+ GsDnf5TransactionFlags flags = (data->flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0 ?
|
||
+ GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE;
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "strict",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+
|
||
+ /* NULL-terminate the array, to be like strv */
|
||
+ g_ptr_array_add (packages, NULL);
|
||
+
|
||
+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, &local_error);
|
||
+
|
||
+ success = success &&
|
||
+ gs_dnf5_rpm_rpm_call_remove_sync (rpm_proxy,
|
||
+ (const gchar * const *) packages->pdata,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = success && gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, flags, NULL, cancellable, &local_error);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to create Rpm proxy: ");
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+ g_atomic_int_dec_and_test (&self->calling_rpm);
|
||
+
|
||
+ if (success) {
|
||
+ gs_dnf5_mark_session_needs_reset (self);
|
||
+ } else {
|
||
+ GsApp *app = NULL;
|
||
+ if (gs_app_list_length (data->apps) == 1)
|
||
+ app = gs_app_list_index (data->apps, 0);
|
||
+
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, app,
|
||
+ local_error, (data->flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0);
|
||
+ }
|
||
+
|
||
+ for (guint i = 0; i < touched_apps->len; i++) {
|
||
+ GsApp *app = g_ptr_array_index (touched_apps, i);
|
||
+ if (gs_app_get_state (app) == GS_APP_STATE_REMOVING) {
|
||
+ if (success)
|
||
+ gs_app_set_state (app, GS_APP_STATE_UNKNOWN);
|
||
+ else
|
||
+ gs_app_set_state_recover (app);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (success)
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ else
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_uninstall_apps_async (GsPlugin *plugin,
|
||
+ GsAppList *apps,
|
||
+ GsPluginUninstallAppsFlags flags,
|
||
+ GsPluginProgressCallback progress_callback,
|
||
+ gpointer progress_user_data,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GsPluginAppNeedsUserActionCallback app_needs_user_action_callback,
|
||
+ gpointer app_needs_user_action_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ gboolean interactive = (flags & GS_PLUGIN_UNINSTALL_APPS_FLAGS_INTERACTIVE) != 0;
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+
|
||
+ task = gs_plugin_uninstall_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data,
|
||
+ event_callback, event_user_data,
|
||
+ app_needs_user_action_callback, app_needs_user_action_data,
|
||
+ cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_uninstall_apps_async);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_uninstall_apps_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_uninstall_apps_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_update_apps_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPlugin *plugin = GS_PLUGIN (self);
|
||
+ GsPluginUpdateAppsData *data = task_data;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ gboolean success;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ g_atomic_int_inc (&self->calling_rpm);
|
||
+
|
||
+ success = gs_dnf5_install_update (plugin, data->apps, FALSE,
|
||
+ (data->flags & GS_PLUGIN_UPDATE_APPS_FLAGS_INTERACTIVE) != 0,
|
||
+ (data->flags & GS_PLUGIN_UPDATE_APPS_FLAGS_NO_APPLY) == 0,
|
||
+ data->event_callback, data->event_user_data,
|
||
+ cancellable, &local_error);
|
||
+
|
||
+ g_atomic_int_dec_and_test (&self->calling_rpm);
|
||
+
|
||
+ if (success)
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ else
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_update_apps_async (GsPlugin *plugin,
|
||
+ GsAppList *apps,
|
||
+ GsPluginUpdateAppsFlags flags,
|
||
+ GsPluginProgressCallback progress_callback,
|
||
+ gpointer progress_user_data,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GsPluginAppNeedsUserActionCallback app_needs_user_action_callback,
|
||
+ gpointer app_needs_user_action_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ gboolean interactive = (flags & GS_PLUGIN_UPDATE_APPS_FLAGS_INTERACTIVE) != 0;
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+
|
||
+ task = gs_plugin_update_apps_data_new_task (plugin, apps, flags, progress_callback, progress_user_data,
|
||
+ event_callback, event_user_data,
|
||
+ app_needs_user_action_callback, app_needs_user_action_data,
|
||
+ cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_update_apps_async);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_update_apps_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_update_apps_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_manage_repository_sync (GsPluginDnf5 *self,
|
||
+ GsApp *repository,
|
||
+ gboolean enable,
|
||
+ gboolean interactive,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5RpmRepo) repo_proxy = NULL;
|
||
+ gboolean success;
|
||
+
|
||
+ if (!gs_app_has_management_plugin (repository, GS_PLUGIN (self)))
|
||
+ return TRUE;
|
||
+
|
||
+ if (gs_app_get_kind (repository) != AS_COMPONENT_KIND_REPOSITORY) {
|
||
+ g_set_error (error,
|
||
+ GS_PLUGIN_ERROR,
|
||
+ GS_PLUGIN_ERROR_NOT_SUPPORTED,
|
||
+ "wrong app kind (%s) passed to manage_repository",
|
||
+ as_component_kind_to_string (gs_app_get_kind (repository)));
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, error);
|
||
+ if (session_path == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ repo_proxy = gs_dnf5_rpm_repo_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ success = repo_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Rpm proxy: ");
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ const gchar *ids[2] = { NULL, NULL };
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (repo_proxy), G_MAXINT);
|
||
+
|
||
+ ids[0] = gs_app_get_id (repository);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "interactive",
|
||
+ g_variant_new_boolean (interactive));
|
||
+
|
||
+ if (enable)
|
||
+ success = gs_dnf5_rpm_repo_call_enable_with_options_sync (repo_proxy, ids, g_variant_builder_end (options_builder), cancellable, error);
|
||
+ else
|
||
+ success = gs_dnf5_rpm_repo_call_disable_with_options_sync (repo_proxy, ids, g_variant_builder_end (options_builder), cancellable, error);
|
||
+
|
||
+ if (success) {
|
||
+ if (enable)
|
||
+ gs_app_set_state (repository, GS_APP_STATE_INSTALLED);
|
||
+ else
|
||
+ gs_app_set_state (repository, GS_APP_STATE_AVAILABLE);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error (error, "Failed to call %s: ", enable ? "enable" : "disable");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+
|
||
+ return success;
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_enable_repository_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPluginManageRepositoryData *data = task_data;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ if (gs_dnf5_manage_repository_sync (self, data->repository, TRUE,
|
||
+ (data->flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0,
|
||
+ cancellable, &local_error)) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_enable_repository_async (GsPlugin *plugin,
|
||
+ GsApp *repository,
|
||
+ GsPluginManageRepositoryFlags flags,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ gboolean interactive = (flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0;
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+
|
||
+ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, event_callback, event_user_data,
|
||
+ cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_enable_repository_async);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_enable_repository_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_enable_repository_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_disable_repository_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPluginManageRepositoryData *data = task_data;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ if (gs_dnf5_manage_repository_sync (self, data->repository, FALSE,
|
||
+ (data->flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0,
|
||
+ cancellable, &local_error)) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_disable_repository_async (GsPlugin *plugin,
|
||
+ GsApp *repository,
|
||
+ GsPluginManageRepositoryFlags flags,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ gboolean interactive = (flags & GS_PLUGIN_MANAGE_REPOSITORY_FLAGS_INTERACTIVE) != 0;
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+
|
||
+ task = gs_plugin_manage_repository_data_new_task (plugin, repository, flags, event_callback, event_user_data,
|
||
+ cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_disable_repository_async);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_disable_repository_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_disable_repository_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_pick_rpm_desktop_file_cb (GsPlugin *plugin,
|
||
+ GsApp *app,
|
||
+ const gchar *filename,
|
||
+ GKeyFile *key_file,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ return strstr (filename, "/snapd/") == NULL &&
|
||
+ strstr (filename, "/snap/") == NULL &&
|
||
+ strstr (filename, "/flatpak/") == NULL &&
|
||
+ g_key_file_has_group (key_file, "Desktop Entry") &&
|
||
+ !g_key_file_has_key (key_file, "Desktop Entry", "X-Flatpak", NULL) &&
|
||
+ !g_key_file_has_key (key_file, "Desktop Entry", "X-SnapInstanceName", NULL);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_launch_async (GsPlugin *plugin,
|
||
+ GsApp *app,
|
||
+ GsPluginLaunchFlags flags,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ gs_plugin_app_launch_filtered_async (plugin, app, flags,
|
||
+ gs_dnf5_pick_rpm_desktop_file_cb, NULL,
|
||
+ cancellable,
|
||
+ callback, user_data);
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_launch_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return gs_plugin_app_launch_filtered_finish (plugin, result, error);
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_file_to_app_sync (GsPluginDnf5 *self,
|
||
+ GsAppList *list,
|
||
+ GFile *file,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autofree gchar *content_type = NULL;
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL;
|
||
+ gboolean success = TRUE;
|
||
+ const gchar *mimetypes[] = {
|
||
+ "application/x-app-package",
|
||
+ "application/x-deb",
|
||
+ "application/vnd.debian.binary-package",
|
||
+ "application/x-redhat-package-manager",
|
||
+ "application/x-rpm",
|
||
+ NULL };
|
||
+
|
||
+ /* does this match any of the mimetypes we support */
|
||
+ content_type = gs_utils_get_content_type (file, cancellable, error);
|
||
+ if (content_type == NULL)
|
||
+ return FALSE;
|
||
+ if (!g_strv_contains (mimetypes, content_type))
|
||
+ return TRUE;
|
||
+
|
||
+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, error);
|
||
+ if (session_path == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ success = rpm_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Rpm proxy: ");
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariantBuilder) patterns_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+ g_autofree gchar *file_uri = NULL;
|
||
+
|
||
+ file_uri = g_file_get_path (file);
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT);
|
||
+
|
||
+ patterns_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
||
+ g_variant_builder_add (patterns_builder, "s", file_uri);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "patterns",
|
||
+ g_variant_builder_end (patterns_builder));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "package_attrs",
|
||
+ gs_dnf5_dup_package_attrs ());
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_provides",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_filenames",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "with_src",
|
||
+ g_variant_new_boolean (FALSE));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "scope",
|
||
+ g_variant_new_string ("all"));
|
||
+
|
||
+ success = gs_dnf5_rpm_rpm_call_list_sync (rpm_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ &result,
|
||
+ cancellable,
|
||
+ error);
|
||
+
|
||
+ if (success) {
|
||
+ g_autofree gchar *basename = g_file_get_basename (file);
|
||
+ ReadPackageData rpd = { 0, };
|
||
+ rpd.list = list;
|
||
+ rpd.set_state = GS_APP_STATE_AVAILABLE_LOCAL;
|
||
+ rpd.nevra_to_app = NULL;
|
||
+ success = gs_dnf5_foreach_item (self, result, gs_dnf5_read_package_cb, &rpd, cancellable, error);
|
||
+
|
||
+ if (success) {
|
||
+ for (guint i = 0; i < gs_app_list_length (list); i++) {
|
||
+ GsApp *app = gs_app_list_index (list, i);
|
||
+ /* it's already downloaded, because it's a local file */
|
||
+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0);
|
||
+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE);
|
||
+ gs_app_set_local_file (app, file);
|
||
+ gs_app_set_metadata (app, "GnomeSoftware::packagename-value", basename);
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to call list: ");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+
|
||
+ return success;
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_dnf5_file_to_app_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ g_autoptr(GsAppList) list = gs_app_list_new ();
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPluginFileToAppData *data = task_data;
|
||
+
|
||
+ if (gs_dnf5_file_to_app_sync (self, list, data->file, cancellable, &local_error)) {
|
||
+ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref);
|
||
+ } else if (local_error != NULL) {
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ } else {
|
||
+ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref);
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_file_to_app_async (GsPlugin *plugin,
|
||
+ GFile *file,
|
||
+ GsPluginFileToAppFlags flags,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ gboolean interactive = (flags & GS_PLUGIN_FILE_TO_APP_FLAGS_INTERACTIVE) != 0;
|
||
+
|
||
+ task = gs_plugin_file_to_app_data_new_task (plugin, file, flags, event_callback, event_user_data, cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_file_to_app_async);
|
||
+
|
||
+ /* Queue a job to get the apps. */
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_file_to_app_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static GsAppList *
|
||
+gs_plugin_dnf5_file_to_app_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_pointer (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_dnf5_system_upgrade_sync (GsPluginDnf5 *self,
|
||
+ const gchar *releasever,
|
||
+ GsApp *progress_app,
|
||
+ gboolean download_only,
|
||
+ gboolean interactive,
|
||
+ GCancellable *cancellable,
|
||
+ GError **error)
|
||
+{
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GsAppList) list = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5Offline) offline_proxy = NULL;
|
||
+ g_autoptr(GsDnf5RpmRpm) rpm_proxy = NULL;
|
||
+ gboolean success;
|
||
+
|
||
+ session_path = gs_dnf5_open_session (self, releasever, &session_manager, cancellable, error);
|
||
+ if (session_path == NULL) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ return FALSE;
|
||
+ }
|
||
+
|
||
+ rpm_proxy = gs_dnf5_rpm_rpm_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ success = rpm_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Rpm proxy: ");
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ offline_proxy = gs_dnf5_offline_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ error);
|
||
+ success = offline_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to create Offline proxy: ");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ gboolean pending = FALSE;
|
||
+ g_autoptr(GVariant) transaction_status = NULL;
|
||
+
|
||
+ if (gs_dnf5_offline_call_get_status_sync (offline_proxy, &pending, &transaction_status, cancellable, error) &&
|
||
+ pending && transaction_status != NULL) {
|
||
+ const gchar *target_releasever = NULL;
|
||
+ gboolean found = g_variant_lookup (transaction_status, "target_releasever", "&s", &target_releasever);
|
||
+ if (found && g_strcmp0 (target_releasever, releasever) == 0) {
|
||
+ g_debug ("There is prepared an upgrade for version '%s' already, skipping preparation", releasever);
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+ return TRUE;
|
||
+ } else if (found && target_releasever != NULL) {
|
||
+ g_debug ("There is prepared an update for version '%s', but needs version '%s', redo preparation", target_releasever, releasever);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autoptr(GVariant) result = NULL;
|
||
+
|
||
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (rpm_proxy), G_MAXINT);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "interactive",
|
||
+ g_variant_new_boolean (interactive));
|
||
+
|
||
+ success = gs_dnf5_reset_transaction_sync (self, session_path, rpm_proxy, cancellable, error);
|
||
+
|
||
+ success = success &&
|
||
+ gs_dnf5_rpm_rpm_call_system_upgrade_sync (rpm_proxy,
|
||
+ g_variant_builder_end (options_builder),
|
||
+ cancellable,
|
||
+ error);
|
||
+
|
||
+ if (success) {
|
||
+ GsDnf5TransactionFlags flags = interactive ? GS_DNF5_TRANSACTION_FLAG_INTERACTIVE : GS_DNF5_TRANSACTION_FLAG_NONE;
|
||
+
|
||
+ success = gs_dnf5_run_transaction (self, session_path, rpm_proxy, progress_app, NULL, flags | GS_DNF5_TRANSACTION_FLAG_OFFLINE, NULL, cancellable, error);
|
||
+
|
||
+ if (success && !download_only) {
|
||
+ g_autofree gchar *op_error_msg = NULL;
|
||
+ gboolean op_success = FALSE;
|
||
+
|
||
+ g_clear_pointer (&options_builder, g_variant_builder_unref);
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "interactive",
|
||
+ g_variant_new_boolean (interactive));
|
||
+
|
||
+ /* ensure the upgrade is finished with the 'reboot' action */
|
||
+ success = gs_dnf5_offline_call_set_finish_action_with_options_sync (offline_proxy, "reboot", g_variant_builder_end (options_builder),
|
||
+ &op_success, &op_error_msg, cancellable, error);
|
||
+ if (success && !op_success) {
|
||
+ success = FALSE;
|
||
+ if (op_error_msg != NULL) {
|
||
+ g_set_error_literal (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, op_error_msg);
|
||
+ g_prefix_error_literal (error, "Failed to set finish action: ");
|
||
+ }
|
||
+ } else if (!success) {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to set finish action: ");
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (error);
|
||
+ g_prefix_error_literal (error, "Failed to call system_upgrade: ");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+
|
||
+ return success;
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_download_upgrade_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPluginDownloadUpgradeData *data = task_data;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ gs_app_set_state (data->app, GS_APP_STATE_DOWNLOADING);
|
||
+
|
||
+ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, TRUE,
|
||
+ (data->flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0,
|
||
+ cancellable, &local_error)) {
|
||
+ gs_app_set_state (data->app, GS_APP_STATE_UPDATABLE);
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ } else {
|
||
+ gs_app_set_state (data->app, GS_APP_STATE_AVAILABLE);
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ gs_dnf5_report_error (self, data->event_callback, data->event_user_data, data->app,
|
||
+ local_error, (data->flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_download_upgrade_async (GsPlugin *plugin,
|
||
+ GsApp *app,
|
||
+ GsPluginDownloadUpgradeFlags flags,
|
||
+ GsPluginEventCallback event_callback,
|
||
+ void *event_user_data,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+ gboolean interactive = (flags & GS_PLUGIN_DOWNLOAD_UPGRADE_FLAGS_INTERACTIVE) != 0;
|
||
+
|
||
+ task = gs_plugin_download_upgrade_data_new_task (plugin, app, flags, event_callback, event_user_data, cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_download_upgrade_async);
|
||
+
|
||
+ /* only process this app if was created by this plugin */
|
||
+ if (!gs_app_has_management_plugin (app, plugin)) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* check is distro-upgrade */
|
||
+ if (gs_app_get_kind (app) != AS_COMPONENT_KIND_OPERATING_SYSTEM) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_download_upgrade_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_download_upgrade_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_trigger_upgrade_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPluginTriggerUpgradeData *data = task_data;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ gs_app_set_state (data->app, GS_APP_STATE_PENDING_INSTALL);
|
||
+
|
||
+ if (gs_dnf5_system_upgrade_sync (self, gs_app_get_version (data->app), data->app, FALSE,
|
||
+ (data->flags & GS_PLUGIN_TRIGGER_UPGRADE_FLAGS_INTERACTIVE) != 0,
|
||
+ cancellable, &local_error)) {
|
||
+ gs_app_set_state (data->app, GS_APP_STATE_UPDATABLE);
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ } else {
|
||
+ gs_app_set_state (data->app, GS_APP_STATE_AVAILABLE);
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_trigger_upgrade_async (GsPlugin *plugin,
|
||
+ GsApp *app,
|
||
+ GsPluginTriggerUpgradeFlags flags,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+ gboolean interactive = (flags & GS_PLUGIN_TRIGGER_UPGRADE_FLAGS_INTERACTIVE) != 0;
|
||
+
|
||
+ task = gs_plugin_trigger_upgrade_data_new_task (plugin, app, flags, cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_trigger_upgrade_async);
|
||
+
|
||
+ /* only process this app if was created by this plugin */
|
||
+ if (!gs_app_has_management_plugin (app, plugin)) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* check is distro-upgrade */
|
||
+ if (gs_app_get_kind (app) != AS_COMPONENT_KIND_OPERATING_SYSTEM) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ gs_app_set_state (app, GS_APP_STATE_PENDING_INSTALL);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_trigger_upgrade_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_trigger_upgrade_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+/* Run in @worker. */
|
||
+static void
|
||
+gs_dnf5_cancel_offline_update_thread_cb (GTask *task,
|
||
+ gpointer source_object,
|
||
+ gpointer task_data,
|
||
+ GCancellable *cancellable)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (source_object);
|
||
+ GsPluginCancelOfflineUpdateData *data = task_data;
|
||
+ g_autofree gchar *session_path = NULL;
|
||
+ g_autoptr(GsAppList) list = NULL;
|
||
+ g_autoptr(GsDnf5SessionManager) session_manager = NULL;
|
||
+ g_autoptr(GsDnf5Offline) offline_proxy = NULL;
|
||
+ gboolean success;
|
||
+ g_autoptr(GError) local_error = NULL;
|
||
+
|
||
+ assert_in_worker (self);
|
||
+
|
||
+ session_path = gs_dnf5_open_session (self, GS_DNF5_RELEASEVER_DEFAULT, &session_manager, cancellable, &local_error);
|
||
+ if (session_path == NULL) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ offline_proxy = gs_dnf5_offline_proxy_new_sync (self->connection,
|
||
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
||
+ GS_DNF5_INTERFACE_RPM_DNF,
|
||
+ session_path,
|
||
+ cancellable,
|
||
+ &local_error);
|
||
+ success = offline_proxy != NULL;
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to create Offline proxy: ");
|
||
+ }
|
||
+
|
||
+ if (success) {
|
||
+ gboolean pending = FALSE;
|
||
+ g_autoptr(GVariant) transaction_status = NULL;
|
||
+
|
||
+ if (gs_dnf5_offline_call_get_status_sync (offline_proxy, &pending, &transaction_status, cancellable, &local_error) &&
|
||
+ pending && transaction_status != NULL) {
|
||
+ g_autoptr(GVariantBuilder) options_builder = NULL;
|
||
+ g_autofree gchar *op_error_msg = NULL;
|
||
+ gboolean op_success = FALSE;
|
||
+
|
||
+ options_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||
+ g_variant_builder_add (options_builder, "{sv}", "interactive",
|
||
+ g_variant_new_boolean ((data->flags & GS_PLUGIN_CANCEL_OFFLINE_UPDATE_FLAGS_INTERACTIVE) != 0));
|
||
+
|
||
+ success = gs_dnf5_offline_call_cancel_with_options_sync (offline_proxy, g_variant_builder_end (options_builder),
|
||
+ &op_success, &op_error_msg, cancellable, &local_error);
|
||
+ if (!success) {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call OfflineProxy::cancel: ");
|
||
+ } else if (!op_success) {
|
||
+ success = FALSE;
|
||
+ if (op_error_msg != NULL) {
|
||
+ g_set_error_literal (&local_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, op_error_msg);
|
||
+ g_prefix_error_literal (&local_error, "Failed to call OfflineProxy::cancel op: ");
|
||
+ } else {
|
||
+ g_set_error_literal (&local_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED, "Failed to call OfflineProxy::cancel op");
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ gs_dnf5_close_session (self, session_manager, session_path);
|
||
+
|
||
+ if (success) {
|
||
+ g_task_return_boolean (task, TRUE);
|
||
+ } else {
|
||
+ gs_dnf5_convert_error (&local_error);
|
||
+ g_task_return_error (task, g_steal_pointer (&local_error));
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_cancel_offline_update_async (GsPlugin *plugin,
|
||
+ GsPluginCancelOfflineUpdateFlags flags,
|
||
+ GCancellable *cancellable,
|
||
+ GAsyncReadyCallback callback,
|
||
+ gpointer user_data)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (plugin);
|
||
+ g_autoptr(GTask) task = NULL;
|
||
+ gboolean interactive = (flags & GS_PLUGIN_CANCEL_OFFLINE_UPDATE_FLAGS_INTERACTIVE) != 0;
|
||
+
|
||
+ task = gs_plugin_cancel_offline_update_data_new_task (plugin, flags, cancellable, callback, user_data);
|
||
+ g_task_set_source_tag (task, gs_plugin_dnf5_cancel_offline_update_async);
|
||
+
|
||
+ gs_worker_thread_queue (self->worker, gs_dnf5_get_priority_for_interactivity (interactive),
|
||
+ gs_dnf5_cancel_offline_update_thread_cb, g_steal_pointer (&task));
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+gs_plugin_dnf5_cancel_offline_update_finish (GsPlugin *plugin,
|
||
+ GAsyncResult *result,
|
||
+ GError **error)
|
||
+{
|
||
+ return g_task_propagate_boolean (G_TASK (result), error);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_dispose (GObject *object)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (object);
|
||
+
|
||
+ if (self->rpm_transaction_watch_id != 0) {
|
||
+ g_bus_unwatch_name (self->rpm_transaction_watch_id);
|
||
+ self->rpm_transaction_watch_id = 0;
|
||
+ }
|
||
+
|
||
+ g_clear_object (&self->worker);
|
||
+ g_clear_object (&self->connection);
|
||
+ g_clear_object (&self->rpm_transaction_proxy);
|
||
+
|
||
+ g_mutex_lock (&self->session_data.mutex);
|
||
+ if (self->session_data.autoclose_timer) {
|
||
+ g_source_remove (self->session_data.autoclose_timer);
|
||
+ self->session_data.autoclose_timer = 0;
|
||
+ }
|
||
+
|
||
+ if (self->session_data.proxy != NULL) {
|
||
+ gs_dnf5_close_session_real (self->session_data.proxy, self->session_data.object_path);
|
||
+
|
||
+ g_clear_object (&self->session_data.proxy);
|
||
+ g_clear_pointer (&self->session_data.object_path, g_free);
|
||
+ }
|
||
+ g_mutex_unlock (&self->session_data.mutex);
|
||
+
|
||
+ g_mutex_lock (&self->dependency_sizes_data.mutex);
|
||
+ g_clear_pointer (&self->dependency_sizes_data.apps, g_ptr_array_unref);
|
||
+ g_clear_object (&self->dependency_sizes_data.cancellable);
|
||
+ g_mutex_unlock (&self->dependency_sizes_data.mutex);
|
||
+
|
||
+ G_OBJECT_CLASS (gs_plugin_dnf5_parent_class)->dispose (object);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_finalize (GObject *object)
|
||
+{
|
||
+ GsPluginDnf5 *self = GS_PLUGIN_DNF5 (object);
|
||
+
|
||
+ g_mutex_clear (&self->session_data.mutex);
|
||
+ g_cond_clear (&self->session_data.cond);
|
||
+ g_mutex_clear (&self->dependency_sizes_data.mutex);
|
||
+
|
||
+ G_OBJECT_CLASS (gs_plugin_dnf5_parent_class)->finalize (object);
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_class_init (GsPluginDnf5Class *klass)
|
||
+{
|
||
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
+ GsPluginClass *plugin_class = GS_PLUGIN_CLASS (klass);
|
||
+
|
||
+ object_class->dispose = gs_plugin_dnf5_dispose;
|
||
+ object_class->finalize = gs_plugin_dnf5_finalize;
|
||
+
|
||
+ plugin_class->setup_async = gs_plugin_dnf5_setup_async;
|
||
+ plugin_class->setup_finish = gs_plugin_dnf5_setup_finish;
|
||
+ plugin_class->shutdown_async = gs_plugin_dnf5_shutdown_async;
|
||
+ plugin_class->shutdown_finish = gs_plugin_dnf5_shutdown_finish;
|
||
+ plugin_class->list_apps_async = gs_plugin_dnf5_list_apps_async;
|
||
+ plugin_class->list_apps_finish = gs_plugin_dnf5_list_apps_finish;
|
||
+ plugin_class->refine_async = gs_plugin_dnf5_refine_async;
|
||
+ plugin_class->refine_finish = gs_plugin_dnf5_refine_finish;
|
||
+ plugin_class->refresh_metadata_async = gs_plugin_dnf5_refresh_metadata_async;
|
||
+ plugin_class->refresh_metadata_finish = gs_plugin_dnf5_refresh_metadata_finish;
|
||
+ plugin_class->update_apps_async = gs_plugin_dnf5_update_apps_async;
|
||
+ plugin_class->update_apps_finish = gs_plugin_dnf5_update_apps_finish;
|
||
+ plugin_class->enable_repository_async = gs_plugin_dnf5_enable_repository_async;
|
||
+ plugin_class->enable_repository_finish = gs_plugin_dnf5_enable_repository_finish;
|
||
+ plugin_class->disable_repository_async = gs_plugin_dnf5_disable_repository_async;
|
||
+ plugin_class->disable_repository_finish = gs_plugin_dnf5_disable_repository_finish;
|
||
+ plugin_class->install_apps_async = gs_plugin_dnf5_install_apps_async;
|
||
+ plugin_class->install_apps_finish = gs_plugin_dnf5_install_apps_finish;
|
||
+ plugin_class->uninstall_apps_async = gs_plugin_dnf5_uninstall_apps_async;
|
||
+ plugin_class->uninstall_apps_finish = gs_plugin_dnf5_uninstall_apps_finish;
|
||
+ plugin_class->launch_async = gs_plugin_dnf5_launch_async;
|
||
+ plugin_class->launch_finish = gs_plugin_dnf5_launch_finish;
|
||
+ plugin_class->file_to_app_async = gs_plugin_dnf5_file_to_app_async;
|
||
+ plugin_class->file_to_app_finish = gs_plugin_dnf5_file_to_app_finish;
|
||
+ plugin_class->download_upgrade_async = gs_plugin_dnf5_download_upgrade_async;
|
||
+ plugin_class->download_upgrade_finish = gs_plugin_dnf5_download_upgrade_finish;
|
||
+ plugin_class->trigger_upgrade_async = gs_plugin_dnf5_trigger_upgrade_async;
|
||
+ plugin_class->trigger_upgrade_finish = gs_plugin_dnf5_trigger_upgrade_finish;
|
||
+ plugin_class->cancel_offline_update_async = gs_plugin_dnf5_cancel_offline_update_async;
|
||
+ plugin_class->cancel_offline_update_finish = gs_plugin_dnf5_cancel_offline_update_finish;
|
||
+}
|
||
+
|
||
+static void
|
||
+gs_plugin_dnf5_init (GsPluginDnf5 *self)
|
||
+{
|
||
+ GsPlugin *plugin = GS_PLUGIN (self);
|
||
+
|
||
+ g_mutex_init (&self->session_data.mutex);
|
||
+ g_cond_init (&self->session_data.cond);
|
||
+ g_mutex_init (&self->dependency_sizes_data.mutex);
|
||
+
|
||
+ /* getting app properties from appstream is quicker */
|
||
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "appstream");
|
||
+
|
||
+ /* like appstream, we need the icon plugin to load cached icons into pixbufs */
|
||
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_BEFORE, "icons");
|
||
+
|
||
+ /* prioritize over packagekit */
|
||
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "packagekit");
|
||
+}
|
||
+
|
||
+GType
|
||
+gs_plugin_query_type (void)
|
||
+{
|
||
+ return GS_TYPE_PLUGIN_DNF5;
|
||
+}
|
||
diff --git a/plugins/dnf5/gs-plugin-dnf5.h b/plugins/dnf5/gs-plugin-dnf5.h
|
||
new file mode 100644
|
||
index 000000000..319d5843c
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/gs-plugin-dnf5.h
|
||
@@ -0,0 +1,20 @@
|
||
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
|
||
+ *
|
||
+ * Copyright (C) 2024 Red Hat <www.redhat.com>
|
||
+ *
|
||
+ * SPDX-License-Identifier: GPL-2.0+
|
||
+ */
|
||
+
|
||
+#pragma once
|
||
+
|
||
+#include <glib.h>
|
||
+#include <glib-object.h>
|
||
+
|
||
+G_BEGIN_DECLS
|
||
+
|
||
+#define GS_TYPE_PLUGIN_DNF5 (gs_plugin_dnf5_get_type ())
|
||
+
|
||
+G_DECLARE_FINAL_TYPE (GsPluginDnf5, gs_plugin_dnf5, GS, PLUGIN_DNF5, GsPlugin)
|
||
+
|
||
+G_END_DECLS
|
||
diff --git a/plugins/dnf5/meson.build b/plugins/dnf5/meson.build
|
||
new file mode 100644
|
||
index 000000000..12b2137e1
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/meson.build
|
||
@@ -0,0 +1,42 @@
|
||
+cargs = ['-DG_LOG_DOMAIN="GsDnf5"']
|
||
+
|
||
+dnf5_generated = gnome.gdbus_codegen(
|
||
+ 'gs-dnf5-generated',
|
||
+ sources: [ 'org.rpm.dnf.v0.Advisory.xml',
|
||
+ 'org.rpm.dnf.v0.Base.xml',
|
||
+ 'org.rpm.dnf.v0.comps.Group.xml',
|
||
+ 'org.rpm.dnf.v0.Goal.xml',
|
||
+ 'org.rpm.dnf.v0.History.xml',
|
||
+ 'org.rpm.dnf.v0.Offline.xml',
|
||
+ 'org.rpm.dnf.v0.rpm.Repo.xml',
|
||
+ 'org.rpm.dnf.v0.rpm.Rpm.xml',
|
||
+ 'org.rpm.dnf.v0.SessionManager.xml'
|
||
+ ],
|
||
+ interface_prefix : 'org.rpm.dnf.v0',
|
||
+ namespace : 'GsDnf5'
|
||
+)
|
||
+
|
||
+dnf5_generated += gnome.gdbus_codegen(
|
||
+ 'gs-dnf5-rpm-generated',
|
||
+ sources: [ 'org.rpm.Transaction.xml' ],
|
||
+ interface_prefix : 'org.rpm',
|
||
+ namespace : 'GsDnf5Rpm'
|
||
+)
|
||
+
|
||
+shared_module(
|
||
+ 'gs_plugin_dnf5',
|
||
+ dnf5_generated,
|
||
+ sources : [
|
||
+ 'gs-dnf5-progress-helper.c',
|
||
+ 'gs-plugin-dnf5.c',
|
||
+ '../packagekit/gs-markdown.c',
|
||
+ ],
|
||
+ include_directories : [
|
||
+ include_directories('../..'),
|
||
+ include_directories('../../lib'),
|
||
+ ],
|
||
+ install : true,
|
||
+ install_dir: plugin_dir,
|
||
+ c_args : cargs,
|
||
+ dependencies : [ plugin_libs ],
|
||
+)
|
||
diff --git a/plugins/dnf5/org.rpm.Transaction.xml b/plugins/dnf5/org.rpm.Transaction.xml
|
||
new file mode 100644
|
||
index 000000000..7d0cbab48
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.Transaction.xml
|
||
@@ -0,0 +1,26 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright (C) 2025 Red Hat <www.redhat.com>
|
||
+SPDX-License-Identifier: GPL-2.0+
|
||
+-->
|
||
+
|
||
+<node>
|
||
+
|
||
+<!-- org.rpm.Transaction:
|
||
+ @short_description: Interface to rpm-plugin-dbus-announce
|
||
+-->
|
||
+<interface name="org.rpm.Transaction">
|
||
+ <signal name="StartTransaction">
|
||
+ <arg name="dbcookie" type="s" />
|
||
+ <arg name="tid" type="u" />
|
||
+ </signal>
|
||
+
|
||
+ <signal name="EndTransaction">
|
||
+ <arg name="dbcookie" type="s" />
|
||
+ <arg name="tid" type="u" />
|
||
+ </signal>
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/dnf5/org.rpm.dnf.v0.Advisory.xml b/plugins/dnf5/org.rpm.dnf.v0.Advisory.xml
|
||
new file mode 100644
|
||
index 000000000..6aecee9fa
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.dnf.v0.Advisory.xml
|
||
@@ -0,0 +1,74 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright Contributors to the libdnf project.
|
||
+
|
||
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
|
||
+
|
||
+Libdnf is free software: you can redistribute it and/or modify
|
||
+it under the terms of the GNU Lesser General Public License as published by
|
||
+the Free Software Foundation, either version 2.1 of the License, or
|
||
+(at your option) any later version.
|
||
+
|
||
+Libdnf 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 Lesser General Public License for more details.
|
||
+
|
||
+You should have received a copy of the GNU Lesser General Public License
|
||
+along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
||
+-->
|
||
+
|
||
+<node>
|
||
+<!-- org.rpm.dnf.v0.Advisory:
|
||
+ @short_description: Interface to security advisories
|
||
+-->
|
||
+<interface name="org.rpm.dnf.v0.Advisory">
|
||
+ <!--
|
||
+ list:
|
||
+ @options: an array of key/value pairs
|
||
+ @advisories: array of returned advisories with requested attributes
|
||
+
|
||
+ Get list of security advisories that match to given filters.
|
||
+
|
||
+ Following options and filters are supported:
|
||
+
|
||
+ - advisory_attrs: list of strings
|
||
+ List of advisory attributes that are returned in `advisories` array. Supported attributes are "advisoryid", "name", "title", "type", "severity", "status", "vendor", "description", "buildtime", "message", "rights", "collections", and "references".
|
||
+ - availability: string, default "available"
|
||
+ Limit returned advisories based on their relation to installed packages. Accepted values are
|
||
+
|
||
+ - "available" to return only advisories containing newer versions of installed packages
|
||
+
|
||
+ - "all" - to return all advisories
|
||
+
|
||
+ - "installed" - to return only advisories containing equal or older versions of installed packages
|
||
+
|
||
+ - "updates" - to return only advisories containing newer versions of installed packages for which an update is available
|
||
+ - names: list of strings
|
||
+ Consider only advisories with one of given names.
|
||
+ - types: list of strings
|
||
+ Consider only advisories of given types. Possible types are "security", "bugfix", "enhancement", and "newpackage".
|
||
+ - contains_pkgs: list of strings
|
||
+ Consider only advisories containing one of given packages.
|
||
+ - severities: list of strings
|
||
+ Consider only advisories of given severity. Possible values are "critical", "important", "moderate", "low", and "none".
|
||
+ - reference_bzs: list of strings
|
||
+ Consider only advisories referencing given Bugzilla ticket ID. Expected values are numeric IDs, e.g. 123456.
|
||
+ - reference_cves: list of strings
|
||
+ Consider only advisoried referencing given CVE ID. Expected values are strings IDs in CVE format, e.g. CVE-2201-0123.
|
||
+ - with_bz: boolean, default False
|
||
+ Consider only advisories referencing a Bugzilla ticket.
|
||
+ - with_cve: boolean, default False
|
||
+ Consider only advisories referencing a CVE ticket.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="list">
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ <arg name="advisories" type="aa{sv}" direction="out" />
|
||
+ </method>
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/dnf5/org.rpm.dnf.v0.Base.xml b/plugins/dnf5/org.rpm.dnf.v0.Base.xml
|
||
new file mode 100644
|
||
index 000000000..e596e60b9
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.dnf.v0.Base.xml
|
||
@@ -0,0 +1,176 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright Contributors to the libdnf project.
|
||
+
|
||
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
|
||
+
|
||
+Libdnf is free software: you can redistribute it and/or modify
|
||
+it under the terms of the GNU Lesser General Public License as published by
|
||
+the Free Software Foundation, either version 2.1 of the License, or
|
||
+(at your option) any later version.
|
||
+
|
||
+Libdnf 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 Lesser General Public License for more details.
|
||
+
|
||
+You should have received a copy of the GNU Lesser General Public License
|
||
+along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
||
+-->
|
||
+
|
||
+<node>
|
||
+<!-- org.rpm.dnf.v0.Base:
|
||
+ @short_description: Interface to Base class
|
||
+-->
|
||
+<interface name="org.rpm.dnf.v0.Base">
|
||
+ <!--
|
||
+ read_all_repos:
|
||
+ @success: `true` if repositories were successfully loaded, `false` otherwise.
|
||
+
|
||
+ Explicitly ask for loading repositories metadata.
|
||
+ -->
|
||
+ <method name="read_all_repos">
|
||
+ <arg name="success" type="b" direction="out"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ clean_with_options:
|
||
+ @cache_type: cache type to clean up. Supported types are "all", "packages", "metadata", "dbcache", and "expire-cache".
|
||
+ @options: an array of key/value pairs to modify the call behavior
|
||
+ @success: `true` if the cache was successfully cleaned, `false` otherwise.
|
||
+ @error_msg: string, contains errors encountered while cleaning the cache.
|
||
+
|
||
+ Remove or expire cached data.
|
||
+
|
||
+ Following @options are supported:
|
||
+ - interactive: boolean, default true
|
||
+ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="clean_with_options">
|
||
+ <arg name="cache_type" type="s" direction="in"/>
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ <arg name="success" type="b" direction="out"/>
|
||
+ <arg name="error_msg" type="s" direction="out"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ clean:
|
||
+ @cache_type: cache type to clean up. Supported types are "all", "packages", "metadata", "dbcache", and "expire-cache".
|
||
+ @success: `true` if the cache was successfully cleaned, `false` otherwise.
|
||
+ @error_msg: string, contains errors encountered while cleaning the cache.
|
||
+
|
||
+ Remove or expire cached data.
|
||
+
|
||
+ This is equivalent to call "clean_with_options()" with empty options.
|
||
+ -->
|
||
+ <method name="clean">
|
||
+ <arg name="cache_type" type="s" direction="in"/>
|
||
+ <arg name="success" type="b" direction="out"/>
|
||
+ <arg name="error_msg" type="s" direction="out"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ reset:
|
||
+ @success: `true` if the session was successfully reset, `false` otherwise.
|
||
+ @error_msg: string, contains errors encountered while resetting the session
|
||
+
|
||
+ Completely reset the session.
|
||
+ -->
|
||
+ <method name="reset">
|
||
+ <arg name="success" type="b" direction="out"/>
|
||
+ <arg name="error_msg" type="s" direction="out"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ download_add_new:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @download_id: unique id of downloaded object (repo or package)
|
||
+ @description: the description of the downloaded object
|
||
+ @total_to_download: total bytes to download
|
||
+
|
||
+ A new download has started.
|
||
+ -->
|
||
+ <signal name="download_add_new">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="download_id" type="s" />
|
||
+ <arg name="description" type="s" />
|
||
+ <arg name="total_to_download" type="x" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ download_progress:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @download_id: unique id of downloaded object (repo or package)
|
||
+ @total_to_download: total bytes to download
|
||
+ @downloaded: bytes already downloaded
|
||
+
|
||
+ Progress in downloading.
|
||
+ -->
|
||
+ <signal name="download_progress">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="download_id" type="s" />
|
||
+ <arg name="total_to_download" type="x" />
|
||
+ <arg name="downloaded" type="x" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ download_mirror_failure:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @download_id: unique id of downloaded object (repo or package)
|
||
+ @message: an error message
|
||
+ @url: URL being downloaded
|
||
+ @metadata: For repository metadata download contains metadata type
|
||
+
|
||
+ Mirror failure during the download.
|
||
+ -->
|
||
+ <signal name="download_mirror_failure">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="download_id" type="s" />
|
||
+ <arg name="message" type="s" />
|
||
+ <arg name="url" type="s" />
|
||
+ <arg name="metadata" type="s" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ download_end:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @download_id: unique id of downloaded object (repo or package)
|
||
+ @transfer_status: libdnf5::repo::DownloadCallbacks::TransferStatus (0 - successful, 1 - already exists, 2 - error)
|
||
+ @message: error message in case of failed download
|
||
+
|
||
+ Downloading has ended.
|
||
+ -->
|
||
+ <signal name="download_end">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="download_id" type="s" />
|
||
+ <arg name="transfer_status" type="u" />
|
||
+ <arg name="message" type="s" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ repo_key_import_request:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @key_id: OpenPGP key id
|
||
+ @user_ids: User id
|
||
+ @key_fingerprint: Fingerprint of the OpenPGP key
|
||
+ @key_url: URL of the OpenPGP key
|
||
+ @timestamp: timestamp when the key was created
|
||
+
|
||
+ Request for repository key import confirmation.
|
||
+ -->
|
||
+ <signal name="repo_key_import_request">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="key_id" type="s" />
|
||
+ <arg name="user_ids" type="as" />
|
||
+ <arg name="key_fingerprint" type="s" />
|
||
+ <arg name="key_url" type="s" />
|
||
+ <arg name="timestamp" type="x" />
|
||
+ </signal>
|
||
+
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/dnf5/org.rpm.dnf.v0.Goal.xml b/plugins/dnf5/org.rpm.dnf.v0.Goal.xml
|
||
new file mode 100644
|
||
index 000000000..097e9dc7e
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.dnf.v0.Goal.xml
|
||
@@ -0,0 +1,122 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright Contributors to the libdnf project.
|
||
+
|
||
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
|
||
+
|
||
+Libdnf is free software: you can redistribute it and/or modify
|
||
+it under the terms of the GNU Lesser General Public License as published by
|
||
+the Free Software Foundation, either version 2.1 of the License, or
|
||
+(at your option) any later version.
|
||
+
|
||
+Libdnf 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 Lesser General Public License for more details.
|
||
+
|
||
+You should have received a copy of the GNU Lesser General Public License
|
||
+along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
||
+-->
|
||
+
|
||
+<node>
|
||
+<!-- org.rpm.dnf.v0.Goal:
|
||
+ @short_description: Interface to Goal
|
||
+-->
|
||
+<interface name="org.rpm.dnf.v0.Goal">
|
||
+ <!--
|
||
+ resolve:
|
||
+ @options: an array of key/value pairs to modify dependency resolving
|
||
+ @transaction_items: array of (object_type, action, reason, {transaction_item_attributes}, {object}) tuples. object_type is one of libdnf5::transaction::TransactionItemType, action is one of libdnf5::transaction::TransactionItemAction, reason is one of libdnf5::transaction::TransactionItemReason enums. Each of these enums are represented as strings using corresponding libdnf5::transaction::\*_to_string() methods. transaction_item_attributes is a map {attribute: value}. Currently "reason_change_group_id" attribute is used for target group id in case the action is ReasonChange and the reason Group, and "replaces" containing a vector of packages ids being replaced by this item. Finally the object is a map {attribute: value} and represents (according to object_type) package, group, environment, or module. These package attributes are returned: "name", "epoch", "version", "release", "arch", "repo_id", "from_repo_id", "package_size", "install_size", "evr", and "reason". For groups the object contains "groupid" and "name" attributes.
|
||
+ @result: problems detected during transaction resolving. Possible values are 0 - no problem, 1 - no problem, but some info / warnings are present or 2 - resolving failed.
|
||
+
|
||
+ Resolve the transaction.
|
||
+
|
||
+ Following @options are supported:
|
||
+ - allow_erasing: boolean, default false
|
||
+ Whether removal of installed package is allowed to resolve the transaction.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="resolve">
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ <arg name="transaction_items" type="a(sssa{sv}a{sv})" direction="out" />
|
||
+ <arg name="result" type="u" direction="out" />
|
||
+ </method>
|
||
+
|
||
+
|
||
+ <!--
|
||
+ get_transaction_problems_string:
|
||
+ @problems: array of strings containing all problems found during the transaction resolution.
|
||
+
|
||
+ Return all problems found during the transaction resolution as human readable messages.
|
||
+ -->
|
||
+ <method name="get_transaction_problems_string">
|
||
+ <arg name="problems" type="as" direction="out" />
|
||
+ </method>
|
||
+
|
||
+
|
||
+ <!--
|
||
+ get_transaction_problems:
|
||
+ @problems: array of {key: value} maps
|
||
+
|
||
+ Return all problems found during the transaction resolution as structures. This is the list of problem keys:
|
||
+ always present:
|
||
+ action - see include/libdnf5/base/goal_elements.hpp: GoalAction
|
||
+ problem - see include/libdnf5/base/goal_elements.hpp: GoalProblem
|
||
+ optional:
|
||
+ goal_job_settings - {string: variant}, settings used to resolve the goal
|
||
+ spec - string, usually nevra of affected package
|
||
+ additional_data - array of strings, more details on the problem
|
||
+ solver_problems - problems reported by underlying libsolv
|
||
+ -->
|
||
+ <method name="get_transaction_problems">
|
||
+ <arg name="problems" type="aa{sv}" direction="out" />
|
||
+ </method>
|
||
+
|
||
+
|
||
+ <!--
|
||
+ do_transaction:
|
||
+ @options: an array of key/value pairs to modify transaction running
|
||
+
|
||
+ Perform the resolved transaction.
|
||
+
|
||
+ Following @options are supported:
|
||
+ - comment: string
|
||
+ Adds a comment to a transaction.
|
||
+ - offline: boolean, default false
|
||
+ If true, the transaction will be prepared to run during the next reboot. Otherwise, it will run immediately.
|
||
+ - interactive: boolean, default true
|
||
+ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
|
||
+ - downloadonly: boolean, default false, since 5.2.16
|
||
+ If true, only download the resolved package set without executing an RMP transaction. Along with the offline option, the offline transaction files are prepared, but the execution of the transaction is not initiated.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="do_transaction">
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ cancel:
|
||
+ @success: true if the cancellation was successfully requested
|
||
+ @error_msg: error message if the cancellation was refused
|
||
+
|
||
+ Cancel the transaction that was initiated by `do_transaction()`. The transaction can only be canceled during the package download phase. Once the RPM transaction has begun, cancellation is no longer permitted.
|
||
+ -->
|
||
+ <method name="cancel">
|
||
+ <arg name="success" type="b" direction="out" />
|
||
+ <arg name="error_msg" type="s" direction="out" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ reset:
|
||
+ Reset the prepared rpm transaction. After this call the session is ready to perform another rpm transaction.
|
||
+ -->
|
||
+ <method name="reset">
|
||
+ </method>
|
||
+
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/dnf5/org.rpm.dnf.v0.History.xml b/plugins/dnf5/org.rpm.dnf.v0.History.xml
|
||
new file mode 100644
|
||
index 000000000..5df5184b0
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.dnf.v0.History.xml
|
||
@@ -0,0 +1,84 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright Contributors to the libdnf project.
|
||
+
|
||
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
|
||
+
|
||
+Libdnf is free software: you can redistribute it and/or modify
|
||
+it under the terms of the GNU Lesser General Public License as published by
|
||
+the Free Software Foundation, either version 2.1 of the License, or
|
||
+(at your option) any later version.
|
||
+
|
||
+Libdnf 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 Lesser General Public License for more details.
|
||
+
|
||
+You should have received a copy of the GNU Lesser General Public License
|
||
+along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
||
+-->
|
||
+
|
||
+<node>
|
||
+<!-- org.rpm.dnf.v0.History:
|
||
+ @short_description: Interface to transactions history database
|
||
+-->
|
||
+<interface name="org.rpm.dnf.v0.History">
|
||
+ <!--
|
||
+ recent_changes:
|
||
+ @options: an array of key/value pairs
|
||
+ @changeset: a dictionary {"<type_of_change>":[<package>,<package>,...]}
|
||
+
|
||
+ Get a list of packages that have been recently changed. The
|
||
+ "type_of_change" key in the output dictionary can be one of the
|
||
+ following: "installed", "removed", "upgraded", or "downgraded". The
|
||
+ packages in the output are represented as dictionaries containing the
|
||
+ requested set of attributes for the changed packages.
|
||
+
|
||
+ For installed, upgraded, and downgraded packages, the currently
|
||
+ installed version is returned.
|
||
+
|
||
+ For upgraded and downgraded packages, the "original_evr" attribute
|
||
+ is also included, containing the package EVR before the change.
|
||
+
|
||
+ If requested, upgraded packages may also include an "advisories" list
|
||
+ field, indicating the name of the updates that contain the upgraded
|
||
+ (i.e., currently installed) version of the package. If the "all_advisories"
|
||
+ option is set, advisories for all versions between the original and currently
|
||
+ installed versions are also included.
|
||
+
|
||
+ For removed packages, only the name, arch, and evr of the removed
|
||
+ version are provided.
|
||
+
|
||
+ Following options and filters are supported:
|
||
+
|
||
+ - upgraded_packages: boolean, default "true"
|
||
+ Whether to include upgraded packages in the output.
|
||
+ - downgraded_packages: boolean, default "true"
|
||
+ Whether to include downgraded packages in the output.
|
||
+ - installed_packages: boolean, default "true"
|
||
+ Whether to include installed packages in the output.
|
||
+ - removed_packages: boolean, default "true"
|
||
+ Whether to include removed packages in the output.
|
||
+ - include_advisory: boolean, default "true"
|
||
+ Whether to include "advisories" to upgraded packages attributes.
|
||
+ - all_advisories: boolean, default "false"
|
||
+ Only make sense with include_advisory=true. Include also advisories
|
||
+ for all versions between the original and currently installed.
|
||
+ - package_attrs: list of strings, default ["name", "summary", "evr", "arch"]
|
||
+ List of package attributes that are returned for each changed
|
||
+ package. Supported attributes are the same as in list() method of
|
||
+ the Rpm interface.
|
||
+ - since: int64, unix timestamp
|
||
+ Get changes since a given point in time.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="recent_changes">
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ <arg name="changeset" type="a{saa{sv}}" direction="out" />
|
||
+ </method>
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/dnf5/org.rpm.dnf.v0.Offline.xml b/plugins/dnf5/org.rpm.dnf.v0.Offline.xml
|
||
new file mode 100644
|
||
index 000000000..8d84c22aa
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.dnf.v0.Offline.xml
|
||
@@ -0,0 +1,151 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright Contributors to the libdnf project.
|
||
+
|
||
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
|
||
+
|
||
+Libdnf is free software: you can redistribute it and/or modify
|
||
+it under the terms of the GNU Lesser General Public License as published by
|
||
+the Free Software Foundation, either version 2.1 of the License, or
|
||
+(at your option) any later version.
|
||
+
|
||
+Libdnf 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 Lesser General Public License for more details.
|
||
+
|
||
+You should have received a copy of the GNU Lesser General Public License
|
||
+along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
||
+-->
|
||
+
|
||
+<node>
|
||
+<!-- org.rpm.dnf.v0.Offline:
|
||
+ @short_description: Interface to offline transactions
|
||
+-->
|
||
+<interface name="org.rpm.dnf.v0.Offline">
|
||
+ <!--
|
||
+ get_status:
|
||
+ @pending: boolean, true if there is a pending offline transaction
|
||
+ @transaction_status: the status of the current offline dnf5 transaction
|
||
+
|
||
+ Check whether there is an offline transaction configured for the next reboot initiated by dnf5 and return its status.
|
||
+ -->
|
||
+ <method name="get_status">
|
||
+ <arg name="pending" type="b" direction="out" />
|
||
+ <arg name="transaction_status" type="a{sv}" direction="out" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ cancel_with_options:
|
||
+ @options: an array of key/value pairs to modify the call behavior
|
||
+ @success: boolean, returns `false` if there was an error during the transaction cancellation, or if the offline transaction was initiated by another tool than dnf5. Returns `true` if the offline transaction was successfully cancelled or if no offline transaction was configured.
|
||
+ @error_msg: string, contains error encountered while cancelling the transaction
|
||
+
|
||
+ Cancel the dnf5 offline transaction configured for the next reboot. Offline updates scheduled by another tool are not cancelled.
|
||
+
|
||
+ Following @options are supported:
|
||
+ - interactive: boolean, default true
|
||
+ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="cancel_with_options">
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ <arg name="success" type="b" direction="out" />
|
||
+ <arg name="error_msg" type="s" direction="out" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ cancel:
|
||
+ @success: boolean, returns `false` if there was an error during the transaction cancellation, or if the offline transaction was initiated by another tool than dnf5. Returns `true` if the offline transaction was successfully cancelled or if no offline transaction was configured.
|
||
+ @error_msg: string, contains error encountered while cancelling the transaction
|
||
+
|
||
+ Cancel the dnf5 offline transaction configured for the next reboot. Offline updates scheduled by another tool are not cancelled.
|
||
+
|
||
+ This is equivalent to call "cancel_with_options()" with empty options.
|
||
+ -->
|
||
+ <method name="cancel">
|
||
+ <arg name="success" type="b" direction="out" />
|
||
+ <arg name="error_msg" type="s" direction="out" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ clean_with_options:
|
||
+ @options: an array of key/value pairs to modify the call behavior
|
||
+ @success: boolean, returns `false` if there was an error during the transaction cleanup. Returns `true` if the offline transaction was successfully cleaned or if no offline transaction was configured.
|
||
+ @error_msg: string, contains error encountered while cleaning the transaction
|
||
+
|
||
+ Cancel the dnf5 offline transaction configured for the next reboot and remove all stored offline transaction data, including downloaded packages. Offline updates scheduled by another tool are not affected.
|
||
+
|
||
+ Following @options are supported:
|
||
+ - interactive: boolean, default true
|
||
+ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="clean_with_options">
|
||
+ <arg name="success" type="b" direction="out" />
|
||
+ <arg name="error_msg" type="s" direction="out" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ clean:
|
||
+ @success: boolean, returns `false` if there was an error during the transaction cleanup. Returns `true` if the offline transaction was successfully cleaned or if no offline transaction was configured.
|
||
+ @error_msg: string, contains error encountered while cleaning the transaction
|
||
+
|
||
+ Cancel the dnf5 offline transaction configured for the next reboot and remove all stored offline transaction data, including downloaded packages. Offline updates scheduled by another tool are not affected.
|
||
+
|
||
+ This is equivalent to call "clean_with_options()" with empty options.
|
||
+ -->
|
||
+ <method name="clean">
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ <arg name="success" type="b" direction="out" />
|
||
+ <arg name="error_msg" type="s" direction="out" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ set_finish_action_with_options:
|
||
+ @action: string, one of "poweroff", or "reboot". If set to "poweroff", the system will be powered off after applying the offline transaction. Otherwise the system will reboot.
|
||
+ @options: an array of key/value pairs to modify the call behavior
|
||
+ @success: boolean, true if the action was successfully set
|
||
+ @error_msg: string, contains error encountered while setting the action
|
||
+
|
||
+ Set the action that should be performed after the offline transaction is applied. If the `action` is "poweroff", the system will be powered off, otherwise it will be rebooted (which is default).
|
||
+ The call might fail in case there is no scheduled offline transaction, or the transaction was not scheduled using libdnf5.
|
||
+
|
||
+ Following @options are supported:
|
||
+ - interactive: boolean, default true
|
||
+ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="set_finish_action_with_options">
|
||
+ <arg name="action" type="s" direction="in" />
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ <arg name="success" type="b" direction="out" />
|
||
+ <arg name="error_msg" type="s" direction="out" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ set_finish_action:
|
||
+ @action: string, one of "poweroff", or "reboot". If set to "poweroff", the system will be powered off after applying the offline transaction. Otherwise the system will reboot.
|
||
+ @success: boolean, true if the action was successfully set
|
||
+ @error_msg: string, contains error encountered while setting the action
|
||
+
|
||
+ Set the action that should be performed after the offline transaction is applied. If the `action` is "poweroff", the system will be powered off, otherwise it will be rebooted (which is default).
|
||
+ The call might fail in case there is no scheduled offline transaction, or the transaction was not scheduled using libdnf5.
|
||
+
|
||
+
|
||
+ This is equivalent to call "set_finish_action_with_options()" with empty options.
|
||
+ -->
|
||
+ <method name="set_finish_action">
|
||
+ <arg name="action" type="s" direction="in" />
|
||
+ <arg name="success" type="b" direction="out" />
|
||
+ <arg name="error_msg" type="s" direction="out" />
|
||
+ </method>
|
||
+
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/dnf5/org.rpm.dnf.v0.SessionManager.xml b/plugins/dnf5/org.rpm.dnf.v0.SessionManager.xml
|
||
new file mode 100644
|
||
index 000000000..a0facd692
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.dnf.v0.SessionManager.xml
|
||
@@ -0,0 +1,69 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright Contributors to the libdnf project.
|
||
+
|
||
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
|
||
+
|
||
+Libdnf is free software: you can redistribute it and/or modify
|
||
+it under the terms of the GNU Lesser General Public License as published by
|
||
+the Free Software Foundation, either version 2.1 of the License, or
|
||
+(at your option) any later version.
|
||
+
|
||
+Libdnf 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 Lesser General Public License for more details.
|
||
+
|
||
+You should have received a copy of the GNU Lesser General Public License
|
||
+along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
||
+-->
|
||
+
|
||
+<node>
|
||
+<!-- org.rpm.dnf.v0.SessionManager:
|
||
+ @short_description: Interface to dnf5daemon sessions management
|
||
+-->
|
||
+<interface name="org.rpm.dnf.v0.SessionManager">
|
||
+ <!--
|
||
+ open_session:
|
||
+ @options: session configuration - an array of key/value pairs
|
||
+ @session_object_path: object path to created Session object
|
||
+
|
||
+ Opens a new session.
|
||
+
|
||
+ Following configuration options are supported:
|
||
+
|
||
+ - load_system_repo: bool, default true
|
||
+ If true information about currently installed packages is loaded.
|
||
+ - load_available_repos: bool, default true
|
||
+ If true information about packages available in enabled repositories is loaded.
|
||
+ - config: map {string: string}
|
||
+ Override configuration options.
|
||
+ - releasever: string
|
||
+ Override releasever variable used for substitutions in repository configurations.
|
||
+ - locale: string
|
||
+ Override server locale for this session. Affects language used in various error messages.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="open_session">
|
||
+ <arg name="options" type="a{sv}" direction="in"/>
|
||
+ <arg name="session_object_path" type="o" direction="out"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ close_session:
|
||
+ @session_object_path: object path of session to close
|
||
+ @success: bool whether the requested session was successfully closed
|
||
+
|
||
+ Close session on given object path.
|
||
+ -->
|
||
+ <method name="close_session">
|
||
+ <arg name="session_object_path" type="o" direction="in" />
|
||
+ <arg name="success" type="b" direction="out" />
|
||
+ </method>
|
||
+
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/dnf5/org.rpm.dnf.v0.comps.Group.xml b/plugins/dnf5/org.rpm.dnf.v0.comps.Group.xml
|
||
new file mode 100644
|
||
index 000000000..b12e0ac6d
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.dnf.v0.comps.Group.xml
|
||
@@ -0,0 +1,61 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright Contributors to the libdnf project.
|
||
+
|
||
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
|
||
+
|
||
+Libdnf is free software: you can redistribute it and/or modify
|
||
+it under the terms of the GNU Lesser General Public License as published by
|
||
+the Free Software Foundation, either version 2.1 of the License, or
|
||
+(at your option) any later version.
|
||
+
|
||
+Libdnf 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 Lesser General Public License for more details.
|
||
+
|
||
+You should have received a copy of the GNU Lesser General Public License
|
||
+along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
||
+-->
|
||
+
|
||
+<node>
|
||
+<!-- org.rpm.dnf.v0.comps.Group:
|
||
+ @short_description: Interface to comps groups
|
||
+-->
|
||
+<interface name="org.rpm.dnf.v0.comps.Group">
|
||
+ <!--
|
||
+ list:
|
||
+ @options: an array of key/value pairs
|
||
+ @groups: array of returned groups with requested attributes
|
||
+
|
||
+ Get list of groups that match to given filters.
|
||
+
|
||
+ Following options and filters are supported:
|
||
+
|
||
+ - attributes: list of strings
|
||
+ list of group attributes that are returned
|
||
+ - match_group_id: bool (default true)
|
||
+ match patterns against ids of the groups
|
||
+ - match_group_name: bool (default false)
|
||
+ match patterns against names of the groups
|
||
+ - scope: string (default "all")
|
||
+ limit groups to one of "all", "installed", "available"
|
||
+ - with_hidden: bool (default false)
|
||
+ include hidden groups into the result
|
||
+ - patterns: list of strings
|
||
+ any group matching to any of patterns is returned
|
||
+ - contains_pkgs: list of strings
|
||
+ include only groups containing given packages
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="list">
|
||
+ <arg name="options" type="a{sv}" direction="in"/>
|
||
+ <arg name="groups" type="aa{sv}" direction="out"/>
|
||
+ </method>
|
||
+
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml b/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml
|
||
new file mode 100644
|
||
index 000000000..50b2ba3bf
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.dnf.v0.rpm.Repo.xml
|
||
@@ -0,0 +1,147 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright Contributors to the libdnf project.
|
||
+
|
||
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
|
||
+
|
||
+Libdnf is free software: you can redistribute it and/or modify
|
||
+it under the terms of the GNU Lesser General Public License as published by
|
||
+the Free Software Foundation, either version 2.1 of the License, or
|
||
+(at your option) any later version.
|
||
+
|
||
+Libdnf 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 Lesser General Public License for more details.
|
||
+
|
||
+You should have received a copy of the GNU Lesser General Public License
|
||
+along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
||
+-->
|
||
+
|
||
+<node>
|
||
+<!-- org.rpm.dnf.v0.rpm.Repo:
|
||
+ @short_description: Interface to RPM repositories
|
||
+-->
|
||
+<interface name="org.rpm.dnf.v0.rpm.Repo">
|
||
+ <!--
|
||
+ list:
|
||
+ @options: an array of key/value pairs
|
||
+ @repositories: array of returned repositories with requested attributes
|
||
+
|
||
+ Get list of repositories that match to given filters.
|
||
+
|
||
+ Following options are supported:
|
||
+
|
||
+ - patterns: list of strings
|
||
+ any repository with id matching to any of patterns is returned
|
||
+ - repo_attrs: list of strings
|
||
+ list of repository attributes that are returned
|
||
+ - enable_disable: string (default "enabled")
|
||
+ When set to "enabled" or "disabled", only enabled / disabled repositories are listed. Any other value means all repositories are returned.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="list">
|
||
+ <arg name="options" type="a{sv}" direction="in"/>
|
||
+ <arg name="repositories" type="aa{sv}" direction="out"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ confirm_key_with_options:
|
||
+ @key_id: id of the key in question
|
||
+ @confirmed: whether the key import is confirmed by user
|
||
+ @options: an array of key/value pairs to modify the call behavior
|
||
+
|
||
+ Confirm repository OpenPGP key import.
|
||
+
|
||
+ Following @options are supported:
|
||
+ - interactive: boolean, default true
|
||
+ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="confirm_key_with_options">
|
||
+ <arg name="key_id" type="s" direction="in"/>
|
||
+ <arg name="confirmed" type="b" direction="in"/>
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ confirm_key:
|
||
+ @key_id: id of the key in question
|
||
+ @confirmed: whether the key import is confirmed by user
|
||
+
|
||
+ Confirm repository OpenPGP key import.
|
||
+
|
||
+ This is equivalent to call "confirm_key_with_options()" with empty options.
|
||
+ -->
|
||
+ <method name="confirm_key">
|
||
+ <arg name="key_id" type="s" direction="in"/>
|
||
+ <arg name="confirmed" type="b" direction="in"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ enable_with_options:
|
||
+ @repo_ids: array of strings containing all repo ids to be enabled
|
||
+ @options: an array of key/value pairs to modify the call behavior
|
||
+
|
||
+ Enable repositories based on the list of given ids.
|
||
+
|
||
+ Following @options are supported:
|
||
+ - interactive: boolean, default true
|
||
+ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="enable_with_options">
|
||
+ <arg name="repo_ids" type="as" direction="in"/>
|
||
+ <arg name="options" type="a{sv}" direction="in"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ enable:
|
||
+ @repo_ids: array of strings containing all repo ids to be enabled
|
||
+
|
||
+ Enable repositories based on the list of given ids.
|
||
+
|
||
+ This is equivalent to call "enable_with_options()" with empty options.
|
||
+ -->
|
||
+ <method name="enable">
|
||
+ <arg name="repo_ids" type="as" direction="in"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ disable_with_options:
|
||
+ @repo_ids: array of strings containing all repo ids to be disabled
|
||
+ @options: an array of key/value pairs to modify the call behavior
|
||
+
|
||
+ Disable repositories based on the list of given ids.
|
||
+
|
||
+ Following @options are supported:
|
||
+ - interactive: boolean, default true
|
||
+ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="disable_with_options">
|
||
+ <arg name="repo_ids" type="as" direction="in"/>
|
||
+ <arg name="options" type="a{sv}" direction="in"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ disable:
|
||
+ @repo_ids: array of strings containing all repo ids to be disabled
|
||
+
|
||
+ Disable repositories based on the list of given ids.
|
||
+
|
||
+ This is equivalent to call "disable_with_options()" with empty options.
|
||
+ -->
|
||
+ <method name="disable">
|
||
+ <arg name="repo_ids" type="as" direction="in"/>
|
||
+ </method>
|
||
+
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml b/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml
|
||
new file mode 100644
|
||
index 000000000..f24b14d39
|
||
--- /dev/null
|
||
+++ b/plugins/dnf5/org.rpm.dnf.v0.rpm.Rpm.xml
|
||
@@ -0,0 +1,470 @@
|
||
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||
+
|
||
+<!--
|
||
+Copyright Contributors to the libdnf project.
|
||
+
|
||
+This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
|
||
+
|
||
+Libdnf is free software: you can redistribute it and/or modify
|
||
+it under the terms of the GNU Lesser General Public License as published by
|
||
+the Free Software Foundation, either version 2.1 of the License, or
|
||
+(at your option) any later version.
|
||
+
|
||
+Libdnf 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 Lesser General Public License for more details.
|
||
+
|
||
+You should have received a copy of the GNU Lesser General Public License
|
||
+along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
||
+-->
|
||
+
|
||
+<node>
|
||
+<!-- org.rpm.dnf.v0.rpm.Rpm:
|
||
+ @short_description: Interface to RPM packages
|
||
+-->
|
||
+<interface name="org.rpm.dnf.v0.rpm.Rpm">
|
||
+ <!--
|
||
+ list:
|
||
+ @options: an array of key/value pairs
|
||
+ @packages: array of returned packages with requested attributes
|
||
+
|
||
+ Get list of packages that match to given filters.
|
||
+ Since the method returns packages directly in the D-Bus method reply
|
||
+ message, which has a limited size, there is a possibility that the
|
||
+ requested package set size would exceed this limit. If you request a
|
||
+ large number of packages and/or a great number of package attributes,
|
||
+ please consider using the list_fd() method.
|
||
+
|
||
+ Following options and filters are supported:
|
||
+
|
||
+ - package_attrs: list of strings
|
||
+ list of package attributes that are returned. Supported attributes are name, epoch, version, release, arch, repo_id, from_repo_id, is_installed, install_size, download_size, buildtime, sourcerpm, summary, url, license, description, files, changelogs, provides, requires, requires_pre, conflicts, obsoletes, recommends, suggests, enhances, supplements, evr, nevra, full_nevra, reason, vendor, group.
|
||
+ - with_nevra: bool (default true)
|
||
+ match patterns against available packages NEVRAs
|
||
+ - with_provides: bool (default true)
|
||
+ match patterns against available packages provides
|
||
+ - with_filenames: bool (default true)
|
||
+ match patterns against names of the files in available packages
|
||
+ - with_binaries: bool (default true)
|
||
+ match patterns against names of the binaries in `/usr/(s)bin` in available packages
|
||
+ - with_src: bool (default true)
|
||
+ include source rpms into the results
|
||
+ - icase: bool (default true)
|
||
+ ignore case while matching patterns
|
||
+
|
||
+ - patterns: list of strings
|
||
+ any package matching to any of patterns is returned
|
||
+ - scope: string (default "all")
|
||
+ limit packages to one of "all", "installed", "available", "upgrades", "upgradable"
|
||
+ - arch: list of strings
|
||
+ limit the resulting set only to packages of given architectures
|
||
+ - repo: list of strings
|
||
+ limit the resulting set only to packages from given repositories
|
||
+ - latest-limit: int
|
||
+ limit the resulting set to only <limit> of latest packages for every name and architecture
|
||
+ - whatprovides: list of strings
|
||
+ limit the resulting set only to packages that provide any of given capabilities
|
||
+ - whatdepends: list of strings
|
||
+ limit the resulting set only to packages that require, enhance, recommend, suggest or supplement any of given capabilities
|
||
+ - whatrequires: list of strings
|
||
+ limit the resulting set only to packages that require any of given capabilities
|
||
+ - whatrecommends: list of strings
|
||
+ limit the resulting set only to packages that recommend any of given capabilities
|
||
+ - whatenhances: list of strings
|
||
+ limit the resulting set only to packages that enhance any of given capabilities
|
||
+ - whatsuggests: list of strings
|
||
+ limit the resulting set only to packages that suggest any of given capabilities
|
||
+ - whatsupplements: list of strings
|
||
+ limit the resulting set only to packages that supplement any of given capabilities
|
||
+ - whatobsoletes: list of strings
|
||
+ limit the resulting set only to packages that obsolete any of given capabilities
|
||
+ - whatconflicts: list of strings
|
||
+ limit the resulting set only to packages that conflict with any of given capabilities
|
||
+
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="list">
|
||
+ <arg name="options" type="a{sv}" direction="in"/>
|
||
+ <arg name="packages" type="aa{sv}" direction="out"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ list_fd:
|
||
+ @options: an array of key/value pairs
|
||
+ @file_descriptor: File descriptor opened for writing used to transfer data
|
||
+ @transfer_id: Identifier of this transfer. Used for signaling the end of transfer.
|
||
+
|
||
+ Retrieve a list of packages that match the provided filters.
|
||
+ Packages are transmitted as a stream of JSON objects, with each JSON object representing one package as a dictionary. This stream is then written into the given file_descriptor.
|
||
+ Unlike the list() method, this approach does not encounter issues with large output data.
|
||
+ The server has a 30-second timeout during which it waits for the client to read data from the pipe if the pipe is full.
|
||
+
|
||
+ The same options as in list() method are supported.
|
||
+ -->
|
||
+ <method name="list_fd">
|
||
+ <arg name="options" type="a{sv}" direction="in"/>
|
||
+ <arg name="file_descriptor" type="h" direction="in"/>
|
||
+ <arg name="transfer_id" type="s" direction="out"/>
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ install:
|
||
+ @pkg_specs: an array of package specifications to be installed on the system
|
||
+ @options: an array of key/value pairs to modify install behavior
|
||
+
|
||
+ Mark packages specified by @pkg_specs for installation.
|
||
+
|
||
+ Following @options are supported:
|
||
+
|
||
+ - repo_ids: list of strings
|
||
+ Identifiers of the repos from which the packages could be installed.
|
||
+ - skip_broken: boolean, default false
|
||
+ Whether solver can skip packages with broken dependencies to resolve transaction
|
||
+ - skip_unavailable: boolean, default false
|
||
+ Whether nonexisting packages can be skipped.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="install">
|
||
+ <arg name="pkg_specs" type="as" direction="in" />
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ upgrade:
|
||
+ @pkg_specs: an array of package specifications to be upgraded on the system
|
||
+ @options: an array of key/value pairs to modify upgrade behavior
|
||
+
|
||
+ Mark packages specified by @pkg_specs for upgrade.
|
||
+
|
||
+ Following @options are supported:
|
||
+
|
||
+ - repo_ids: list of strings
|
||
+ Identifiers of the repos from which the packages could be upgraded.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="upgrade">
|
||
+ <arg name="pkg_specs" type="as" direction="in" />
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ remove:
|
||
+ @pkg_specs: an array of package specifications to be removed on the system
|
||
+ @options: an array of key/value pairs to modify remove behavior
|
||
+
|
||
+ Mark packages specified by @pkg_specs for removal.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="remove">
|
||
+ <arg name="pkg_specs" type="as" direction="in" />
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ distro_sync:
|
||
+ @pkg_specs: array of package specifications to synchronize to the latest available versions
|
||
+ @options: an array of key/value pairs to modify distro_sync behavior
|
||
+
|
||
+ Following @options are supported:
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="distro_sync">
|
||
+ <arg name="pkg_specs" type="as" direction="in" />
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ downgrade:
|
||
+ @pkg_specs: an array of package specifications to be downgraded on the system
|
||
+ @options: an array of key/value pairs to modify downgrade behavior
|
||
+
|
||
+ Mark packages specified by @pkg_specs for downgrade.
|
||
+
|
||
+ Following @options are supported:
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="downgrade">
|
||
+ <arg name="pkg_specs" type="as" direction="in" />
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ reinstall:
|
||
+ @pkg_specs: an array of package specifications to be reinstalled on the system
|
||
+ @options: an array of key/value pairs to modify reinstall behavior
|
||
+
|
||
+ Mark packages specified by @pkg_specs for reinstall.
|
||
+
|
||
+ Following @options are supported:
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="reinstall">
|
||
+ <arg name="pkg_specs" type="as" direction="in" />
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ system_upgrade:
|
||
+ @options: an array of key/value pairs to modify system_upgrade behavior
|
||
+
|
||
+ Prepare a transaction for upgrade to the new distribution release. The prepared transaction should be executed during reboot (see `offline` option of the `do_transaction` method of the `Goal` interface).
|
||
+ The method relies on the `releasever` option being correctly set to the new distribution release during the `open_session()` call.
|
||
+
|
||
+ Following @options are supported:
|
||
+
|
||
+ - mode: string (one of "distrosync", "upgrade", default is "distrosync")
|
||
+ By default the system_upgrade behaves like `dnf distro-sync`, always installing packages from the new release, even if they are older than the currently installed version. If set to "upgrade", packages from the new release are not installed if they are older than what is currently installed (behave like `dnf upgrade`).
|
||
+ - interactive: boolean, default true
|
||
+ Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
|
||
+
|
||
+ Unknown options are ignored.
|
||
+ -->
|
||
+ <method name="system_upgrade">
|
||
+ <arg name="options" type="a{sv}" direction="in" />
|
||
+ </method>
|
||
+
|
||
+ <!--
|
||
+ transaction_elem_progress:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @nevra: full NEVRA of the package
|
||
+ @processed: amount already processed
|
||
+ @total: total to process
|
||
+
|
||
+ Overall progress in transaction item processing. Called right before an item is processed.
|
||
+ -->
|
||
+ <signal name="transaction_elem_progress">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="nevra" type="s" />
|
||
+ <arg name="processed" type="t" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_before_begin:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @total: number of elements in the rpm transaction
|
||
+
|
||
+ Send right before the rpm transaction is run.
|
||
+ -->
|
||
+ <signal name="transaction_before_begin">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_after_complete:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @success: true if the rpm transaction was completed successfully
|
||
+
|
||
+ Send right after the rpm transaction run finished.
|
||
+ -->
|
||
+ <signal name="transaction_after_complete">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="success" type="b" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_action_start:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @nevra: full NEVRA of the package
|
||
+ @action: one of the dnfdaemon::RpmTransactionItem::Actions enum
|
||
+ @total: total to process
|
||
+
|
||
+ Processing (installation or removal) of the package has started.
|
||
+ -->
|
||
+ <signal name="transaction_action_start">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="nevra" type="s" />
|
||
+ <arg name="action" type="u" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_action_progress:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @nevra: full NEVRA of the package
|
||
+ @processed: amount already processed
|
||
+ @total: total to process
|
||
+
|
||
+ Progress in processing of the package.
|
||
+ -->
|
||
+ <signal name="transaction_action_progress">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="nevra" type="s" />
|
||
+ <arg name="processed" type="t" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_action_stop:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @nevra: full NEVRA of the package
|
||
+ @total: total processed
|
||
+
|
||
+ Processing of the package has finished.
|
||
+ -->
|
||
+ <signal name="transaction_action_stop">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="nevra" type="s" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_script_start:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @nevra: full NEVRA of the package script belongs to
|
||
+ @scriptlet_type: scriptlet type that started (pre, post,...)
|
||
+
|
||
+ The scriptlet has started.
|
||
+ -->
|
||
+ <signal name="transaction_script_start">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="nevra" type="s" />
|
||
+ <arg name="scriptlet_type" type="u" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_script_stop:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @nevra: full NEVRA of the package script belongs to
|
||
+ @scriptlet_type: scriptlet type that started (pre, post,...)
|
||
+ @return_code: return value of the script
|
||
+
|
||
+ The scriptlet has successfully finished.
|
||
+ -->
|
||
+ <signal name="transaction_script_stop">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="nevra" type="s" />
|
||
+ <arg name="scriptlet_type" type="u" />
|
||
+ <arg name="return_code" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_script_error:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @nevra: full NEVRA of the package script belongs to
|
||
+ @scriptlet_type: scriptlet type that started (pre, post,...)
|
||
+ @return_code: return value of the script
|
||
+
|
||
+ The scriptlet has finished with an error.
|
||
+ -->
|
||
+ <signal name="transaction_script_error">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="nevra" type="s" />
|
||
+ <arg name="scriptlet_type" type="u" />
|
||
+ <arg name="return_code" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_verify_start:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @total: total to process
|
||
+
|
||
+ Package files verification has started.
|
||
+ -->
|
||
+ <signal name="transaction_verify_start">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_verify_progress:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @processed: amount already processed
|
||
+ @total: total to process
|
||
+
|
||
+ Progress in processing of the package.
|
||
+ -->
|
||
+ <signal name="transaction_verify_progress">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="processed" type="t" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_verify_stop:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @total: total to process
|
||
+
|
||
+ Package files verification has finished.
|
||
+ -->
|
||
+ <signal name="transaction_verify_stop">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_transaction_start:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @total: total to process
|
||
+
|
||
+ Preparation of transaction packages has started.
|
||
+ -->
|
||
+ <signal name="transaction_transaction_start">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_transaction_progress:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @processed: amount already processed
|
||
+ @total: total to process
|
||
+
|
||
+ Progress in preparation of transaction packages.
|
||
+ -->
|
||
+ <signal name="transaction_transaction_progress">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="processed" type="t" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_transaction_stop:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @total: total to process
|
||
+
|
||
+ Preparation of transaction packages has finished.
|
||
+ -->
|
||
+ <signal name="transaction_transaction_stop">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="total" type="t" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ transaction_unpack_error:
|
||
+ @session_object_path: object path of the dnf5daemon session
|
||
+ @nevra: full NEVRA of the package
|
||
+
|
||
+ Error while unpacking the package.
|
||
+ -->
|
||
+ <signal name="transaction_unpack_error">
|
||
+ <arg name="session_object_path" type="o" />
|
||
+ <arg name="nevra" type="s" />
|
||
+ </signal>
|
||
+
|
||
+ <!--
|
||
+ write_to_fd_finished:
|
||
+ @success: true if the transfer finished successfully
|
||
+ @transfer_id: id of the finished transfer
|
||
+ @error_msg: error message if the transfer was not successful.
|
||
+
|
||
+ Notify the user that writing to the file descriptor with given transfer_id has finished and the descriptor has been closed.
|
||
+ -->
|
||
+ <signal name="write_to_fd_finished">
|
||
+ <arg name="success" type="b" />
|
||
+ <arg name="transfer_id" type="s" />
|
||
+ <arg name="error_msg" type="s" />
|
||
+ </signal>
|
||
+</interface>
|
||
+
|
||
+</node>
|
||
diff --git a/plugins/meson.build b/plugins/meson.build
|
||
index a108e47e5..a131a5cd3 100644
|
||
--- a/plugins/meson.build
|
||
+++ b/plugins/meson.build
|
||
@@ -16,6 +16,9 @@ subdir('fedora-pkgdb-collections')
|
||
if get_option('dkms')
|
||
subdir('dkms')
|
||
endif
|
||
+if get_option('dnf5')
|
||
+ subdir('dnf5')
|
||
+endif
|
||
if get_option('eos_updater')
|
||
subdir('eos-updater')
|
||
endif
|
||
diff --git a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c
|
||
index 627a5143e..64451bccc 100644
|
||
--- a/plugins/rpm-ostree/gs-plugin-rpm-ostree.c
|
||
+++ b/plugins/rpm-ostree/gs-plugin-rpm-ostree.c
|
||
@@ -168,6 +168,7 @@ gs_plugin_rpm_ostree_init (GsPluginRpmOstree *self)
|
||
* more sense to use a custom plugin instead of using PackageKit.
|
||
*/
|
||
gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_CONFLICTS, "packagekit");
|
||
+ gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_CONFLICTS, "dnf5");
|
||
|
||
/* need pkgname */
|
||
gs_plugin_add_rule (GS_PLUGIN (self), GS_PLUGIN_RULE_RUN_AFTER, "appstream");
|
||
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
||
index 71aa486e2..d3e909dee 100644
|
||
--- a/po/POTFILES.in
|
||
+++ b/po/POTFILES.in
|
||
@@ -112,6 +112,7 @@ lib/gs-utils.c
|
||
src/org.gnome.Software.desktop.in
|
||
plugins/dkms/gs-plugin-dkms.c
|
||
plugins/core/gs-plugin-generic-updates.c
|
||
+plugins/dnf5/gs-plugin-dnf5.c
|
||
plugins/eos-updater/gs-plugin-eos-updater.c
|
||
plugins/epiphany/gs-plugin-epiphany.c
|
||
plugins/epiphany/org.gnome.Software.Plugin.Epiphany.metainfo.xml.in
|
||
diff --git a/src/meson.build b/src/meson.build
|
||
index f8b918539..7df81d130 100644
|
||
--- a/src/meson.build
|
||
+++ b/src/meson.build
|
||
@@ -245,7 +245,7 @@ if get_option('fwupd')
|
||
)
|
||
endif
|
||
|
||
-if get_option('packagekit') or get_option('rpm_ostree')
|
||
+if get_option('packagekit') or get_option('rpm_ostree') or get_option('dnf5')
|
||
i18n.merge_file(
|
||
input:
|
||
configure_file(
|