[gnome-software/wip/mcrha/fedora-third-party-followup-half] lib: Extract fedora-third-party code into a single file
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/mcrha/fedora-third-party-followup-half] lib: Extract fedora-third-party code into a single file
- Date: Mon, 27 Sep 2021 14:01:11 +0000 (UTC)
commit fed72335ce070ecf8a8e23b383968797e1ed8d0d
Author: Milan Crha <mcrha redhat com>
Date: Wed Sep 22 08:25:20 2021 +0200
lib: Extract fedora-third-party code into a single file
Helps to avoid code duplication.
lib/gnome-software-private.h | 1 +
lib/gs-fedora-third-party.c | 491 +++++++++++++++++++++++++++++++++++++++++++
lib/gs-fedora-third-party.h | 93 ++++++++
lib/meson.build | 1 +
4 files changed, 586 insertions(+)
---
diff --git a/lib/gnome-software-private.h b/lib/gnome-software-private.h
index 4cff5c7f6..1e3addf25 100644
--- a/lib/gnome-software-private.h
+++ b/lib/gnome-software-private.h
@@ -17,6 +17,7 @@
#include <gs-app-list-private.h>
#include <gs-app-private.h>
#include <gs-category-private.h>
+#include <gs-fedora-third-party.h>
#include <gs-os-release.h>
#include <gs-plugin-loader.h>
#include <gs-plugin-loader-sync.h>
diff --git a/lib/gs-fedora-third-party.c b/lib/gs-fedora-third-party.c
new file mode 100644
index 000000000..98f16e70c
--- /dev/null
+++ b/lib/gs-fedora-third-party.c
@@ -0,0 +1,491 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2021 Red Hat <www.redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "gs-fedora-third-party.h"
+
+struct _GsFedoraThirdParty
+{
+ GObject parent_instance;
+ GMutex lock;
+ gchar *executable;
+ GHashTable *repos; /* gchar *name ~> gchar *packaging format */
+ gint64 last_update;
+};
+
+G_DEFINE_TYPE (GsFedoraThirdParty, gs_fedora_third_party, G_TYPE_OBJECT)
+
+static GObject *
+gs_fedora_third_party_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ static GWeakRef singleton;
+ GObject *result;
+
+ result = g_weak_ref_get (&singleton);
+ if (result == NULL) {
+ result = G_OBJECT_CLASS (gs_fedora_third_party_parent_class)->constructor (type,
n_construct_properties, construct_properties);
+
+ if (result)
+ g_weak_ref_set (&singleton, result);
+ }
+
+ return result;
+}
+
+static void
+gs_fedora_third_party_finalize (GObject *object)
+{
+ GsFedoraThirdParty *self = GS_FEDORA_THIRD_PARTY (object);
+
+ g_clear_pointer (&self->executable, g_free);
+ g_clear_pointer (&self->repos, g_hash_table_unref);
+ g_mutex_clear (&self->lock);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (gs_fedora_third_party_parent_class)->finalize (object);
+}
+
+static void
+gs_fedora_third_party_class_init (GsFedoraThirdPartyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->constructor = gs_fedora_third_party_constructor;
+ object_class->finalize = gs_fedora_third_party_finalize;
+}
+
+static void
+gs_fedora_third_party_init (GsFedoraThirdParty *self)
+{
+ g_mutex_init (&self->lock);
+}
+
+GsFedoraThirdParty *
+gs_fedora_third_party_new (void)
+{
+ return g_object_new (GS_TYPE_FEDORA_THIRD_PARTY, NULL);
+}
+
+static gboolean
+gs_fedora_third_party_ensure_executable_locked (GsFedoraThirdParty *self,
+ GError **error)
+{
+ if (self->executable == NULL)
+ self->executable = g_find_program_in_path ("fedora-third-party");
+
+ if (self->executable == NULL) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "File 'fedora-third-party' not found");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_fedora_third_party_is_available (GsFedoraThirdParty *self)
+{
+ gboolean res;
+
+ g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE);
+
+ g_mutex_lock (&self->lock);
+ res = gs_fedora_third_party_ensure_executable_locked (self, NULL);
+ g_mutex_unlock (&self->lock);
+
+ return res;
+}
+
+void
+gs_fedora_third_party_invalidate (GsFedoraThirdParty *self)
+{
+ g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self));
+
+ g_mutex_lock (&self->lock);
+ g_clear_pointer (&self->executable, g_free);
+ g_clear_pointer (&self->repos, g_hash_table_unref);
+ self->last_update = 0;
+ g_mutex_unlock (&self->lock);
+}
+
+typedef struct _AsyncData
+{
+ gboolean enable;
+ gboolean config_only;
+} AsyncData;
+
+static AsyncData *
+async_data_new (gboolean enable,
+ gboolean config_only)
+{
+ AsyncData *async_data = g_slice_new0 (AsyncData);
+ async_data->enable = enable;
+ async_data->config_only = config_only;
+ return async_data;
+}
+
+static void
+async_data_free (gpointer ptr)
+{
+ AsyncData *async_data = ptr;
+ if (async_data != NULL)
+ g_slice_free (AsyncData, async_data);
+}
+
+static void
+gs_fedora_third_party_query_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ g_autoptr(GError) error = NULL;
+ GsFedoraThirdPartyState state;
+ if (gs_fedora_third_party_query_sync (GS_FEDORA_THIRD_PARTY (source_object), &state, cancellable,
&error))
+ g_task_return_int (task, state);
+ else
+ g_task_return_error (task, g_steal_pointer (&error));
+}
+
+void
+gs_fedora_third_party_query (GsFedoraThirdParty *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_fedora_third_party_query);
+ g_task_run_in_thread (task, gs_fedora_third_party_query_thread);
+}
+
+gboolean
+gs_fedora_third_party_query_finish (GsFedoraThirdParty *self,
+ GAsyncResult *result,
+ GsFedoraThirdPartyState *out_state,
+ GError **error)
+{
+ GError *local_error = NULL;
+ GsFedoraThirdPartyState state = GS_FEDORA_THIRD_PARTY_STATE_UNKNOWN;
+
+ g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE);
+
+ state = g_task_propagate_int (G_TASK (result), &local_error);
+ if (local_error) {
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ if (out_state)
+ *out_state = state;
+
+ return TRUE;
+}
+
+gboolean
+gs_fedora_third_party_query_sync (GsFedoraThirdParty *self,
+ GsFedoraThirdPartyState *out_state,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *args[] = {
+ "", /* executable */
+ "query",
+ "--quiet",
+ NULL
+ };
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE);
+
+ g_mutex_lock (&self->lock);
+ if (gs_fedora_third_party_ensure_executable_locked (self, error)) {
+ gint exit_status = -1;
+ args[0] = self->executable;
+ success = g_spawn_sync (NULL, (gchar **) args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL,
NULL, &exit_status, error);
+ if (success) {
+ GsFedoraThirdPartyState state = GS_FEDORA_THIRD_PARTY_STATE_UNKNOWN;
+ /* See https://pagure.io/fedora-third-party/blob/main/f/doc/fedora-third-party.1.md */
+ switch (WEXITSTATUS (exit_status)) {
+ case 0:
+ state = GS_FEDORA_THIRD_PARTY_STATE_ENABLED;
+ break;
+ case 1:
+ state = GS_FEDORA_THIRD_PARTY_STATE_DISABLED;
+ break;
+ case 2:
+ state = GS_FEDORA_THIRD_PARTY_STATE_ASK;
+ break;
+ default:
+ break;
+ }
+ if (out_state)
+ *out_state = state;
+ }
+ }
+ g_mutex_unlock (&self->lock);
+
+ return success;
+}
+
+static void
+gs_fedora_third_party_switch_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ g_autoptr(GError) error = NULL;
+ AsyncData *async_data = task_data;
+ if (gs_fedora_third_party_switch_sync (GS_FEDORA_THIRD_PARTY (source_object), async_data->enable,
async_data->config_only, cancellable, &error))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, g_steal_pointer (&error));
+}
+
+void
+gs_fedora_third_party_switch (GsFedoraThirdParty *self,
+ gboolean enable,
+ gboolean config_only,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_fedora_third_party_switch);
+ g_task_set_task_data (task, async_data_new (enable, config_only), async_data_free);
+ g_task_run_in_thread (task, gs_fedora_third_party_switch_thread);
+}
+
+gboolean
+gs_fedora_third_party_switch_finish (GsFedoraThirdParty *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE);
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+gs_fedora_third_party_switch_sync (GsFedoraThirdParty *self,
+ gboolean enable,
+ gboolean config_only,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *args[] = {
+ "pkexec",
+ "", /* executable */
+ "", /* command */
+ "", /* config-only */
+ NULL
+ };
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE);
+
+ g_mutex_lock (&self->lock);
+ if (gs_fedora_third_party_ensure_executable_locked (self, error)) {
+ args[1] = self->executable;
+ args[2] = enable ? "enable" : "disable";
+ args[3] = config_only ? "--config-only" : NULL;
+ success = g_spawn_sync (NULL, (gchar **) args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL,
NULL, NULL, error);
+ }
+ g_mutex_unlock (&self->lock);
+
+ return success;
+}
+
+static void
+gs_fedora_third_party_opt_out_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ g_autoptr(GError) error = NULL;
+ if (gs_fedora_third_party_opt_out_sync (GS_FEDORA_THIRD_PARTY (source_object), cancellable, &error))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, g_steal_pointer (&error));
+}
+
+void
+gs_fedora_third_party_opt_out (GsFedoraThirdParty *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_fedora_third_party_opt_out);
+ g_task_run_in_thread (task, gs_fedora_third_party_opt_out_thread);
+}
+
+gboolean
+gs_fedora_third_party_opt_out_finish (GsFedoraThirdParty *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE);
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+gs_fedora_third_party_opt_out_sync (GsFedoraThirdParty *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* fedora-third-party-opt-out is a single-purpose script that changes
+ * the third-party status from unset => disabled. It exists to allow
+ * a different pkexec configuration for opting-out and thus avoid
+ * admin users needing to authenticate to opt-out.
+ */
+ const gchar *args[] = {
+ "pkexec",
+ "/usr/lib/fedora-third-party/fedora-third-party-opt-out",
+ NULL
+ };
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE);
+
+ g_mutex_lock (&self->lock);
+ if (gs_fedora_third_party_ensure_executable_locked (self, error)) {
+ success = g_spawn_sync (NULL, (gchar **) args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL,
NULL, NULL, error);
+ }
+ g_mutex_unlock (&self->lock);
+
+ return success;
+}
+
+static void
+gs_fedora_third_party_list_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GHashTable) repos = NULL;
+ if (gs_fedora_third_party_list_sync (GS_FEDORA_THIRD_PARTY (source_object), &repos, cancellable,
&error))
+ g_task_return_pointer (task, g_steal_pointer (&repos), (GDestroyNotify) g_hash_table_unref);
+ else
+ g_task_return_error (task, g_steal_pointer (&error));
+}
+
+void
+gs_fedora_third_party_list (GsFedoraThirdParty *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GS_IS_FEDORA_THIRD_PARTY (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_fedora_third_party_list);
+ g_task_run_in_thread (task, gs_fedora_third_party_list_thread);
+}
+
+gboolean
+gs_fedora_third_party_list_finish (GsFedoraThirdParty *self,
+ GAsyncResult *result,
+ GHashTable **out_repos, /* gchar *name ~> gchar *management_plugin */
+ GError **error)
+{
+ g_autoptr(GHashTable) repos = NULL;
+ g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE);
+ repos = g_task_propagate_pointer (G_TASK (result), error);
+ if (repos == NULL)
+ return FALSE;
+ if (out_repos)
+ *out_repos = g_steal_pointer (&repos);
+ return TRUE;
+}
+
+gboolean
+gs_fedora_third_party_list_sync (GsFedoraThirdParty *self,
+ GHashTable **out_repos, /* gchar *name ~> gchar *management_plugin */
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *args[] = {
+ "", /* executable */
+ "list",
+ "--csv",
+ "--columns=type,name",
+ NULL
+ };
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (GS_IS_FEDORA_THIRD_PARTY (self), FALSE);
+
+ g_mutex_lock (&self->lock);
+ /* Auto-recheck only twice a day */
+ if (self->repos == NULL || (g_get_real_time () / G_USEC_PER_SEC) - self->last_update > 12 * 60 * 60) {
+ g_clear_pointer (&self->repos, g_hash_table_unref);
+ if (gs_fedora_third_party_ensure_executable_locked (self, error)) {
+ g_autofree gchar *stdoutput = NULL;
+ args[0] = self->executable;
+ if (g_spawn_sync (NULL, (gchar **) args, NULL, G_SPAWN_DEFAULT, NULL, NULL,
&stdoutput, NULL, NULL, error)) {
+ GHashTable *repos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
g_free);
+ g_auto(GStrv) lines = NULL;
+
+ lines = g_strsplit (stdoutput != NULL ? stdoutput : "", "\n", -1);
+
+ for (gsize ii = 0; lines != NULL && lines[ii]; ii++) {
+ g_auto(GStrv) tokens = g_strsplit (lines[ii], ",", 2);
+ if (tokens != NULL && tokens[0] != NULL && tokens[1] != NULL) {
+ const gchar *repo_type = tokens[0];
+ /* The 'dnf' means 'packagekit' here */
+ if (g_str_equal (repo_type, "dnf"))
+ repo_type = "packagekit";
+ /* Hash them by name, which cannot clash between types */
+ g_hash_table_insert (repos, g_strdup (tokens[1]), g_strdup
(repo_type));
+ }
+ }
+
+ self->repos = repos;
+ }
+ }
+ self->last_update = g_get_real_time () / G_USEC_PER_SEC;
+ }
+ success = self->repos != NULL && g_hash_table_size (self->repos) != 0;
+ if (success && out_repos)
+ *out_repos = g_hash_table_ref (self->repos);
+ g_mutex_unlock (&self->lock);
+
+ return success;
+}
+
+gboolean
+gs_fedora_third_party_util_is_third_party_repo (GHashTable *third_party_repos,
+ const gchar *origin,
+ const gchar *management_plugin)
+{
+ const gchar *expected_management_plugin;
+
+ if (third_party_repos == NULL || origin == NULL)
+ return FALSE;
+
+ expected_management_plugin = g_hash_table_lookup (third_party_repos, origin);
+ if (expected_management_plugin == NULL)
+ return FALSE;
+
+ return g_strcmp0 (management_plugin, expected_management_plugin) == 0;
+}
diff --git a/lib/gs-fedora-third-party.h b/lib/gs-fedora-third-party.h
new file mode 100644
index 000000000..11a0144cf
--- /dev/null
+++ b/lib/gs-fedora-third-party.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2021 Red Hat <www.redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_FEDORA_THIRD_PARTY (gs_fedora_third_party_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsFedoraThirdParty, gs_fedora_third_party, GS, FEDORA_THIRD_PARTY, GObject)
+
+typedef enum _GsFedoraThirdPartyState {
+ GS_FEDORA_THIRD_PARTY_STATE_UNKNOWN,
+ GS_FEDORA_THIRD_PARTY_STATE_ENABLED,
+ GS_FEDORA_THIRD_PARTY_STATE_DISABLED,
+ GS_FEDORA_THIRD_PARTY_STATE_ASK
+} GsFedoraThirdPartyState;
+
+GsFedoraThirdParty *
+ gs_fedora_third_party_new (void);
+gboolean gs_fedora_third_party_is_available
+ (GsFedoraThirdParty *self);
+void gs_fedora_third_party_invalidate(GsFedoraThirdParty *self);
+void gs_fedora_third_party_query (GsFedoraThirdParty *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean gs_fedora_third_party_query_finish
+ (GsFedoraThirdParty *self,
+ GAsyncResult *result,
+ GsFedoraThirdPartyState *out_state,
+ GError **error);
+gboolean gs_fedora_third_party_query_sync(GsFedoraThirdParty *self,
+ GsFedoraThirdPartyState *out_state,
+ GCancellable *cancellable,
+ GError **error);
+void gs_fedora_third_party_switch (GsFedoraThirdParty *self,
+ gboolean enable,
+ gboolean config_only,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean gs_fedora_third_party_switch_finish
+ (GsFedoraThirdParty *self,
+ GAsyncResult *result,
+ GError **error);
+gboolean gs_fedora_third_party_switch_sync
+ (GsFedoraThirdParty *self,
+ gboolean enable,
+ gboolean config_only,
+ GCancellable *cancellable,
+ GError **error);
+void gs_fedora_third_party_opt_out (GsFedoraThirdParty *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean gs_fedora_third_party_opt_out_finish
+ (GsFedoraThirdParty *self,
+ GAsyncResult *result,
+ GError **error);
+gboolean gs_fedora_third_party_opt_out_sync
+ (GsFedoraThirdParty *self,
+ GCancellable *cancellable,
+ GError **error);
+void gs_fedora_third_party_list (GsFedoraThirdParty *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean gs_fedora_third_party_list_finish
+ (GsFedoraThirdParty *self,
+ GAsyncResult *result,
+ GHashTable **out_repos,
+ GError **error);
+gboolean gs_fedora_third_party_list_sync (GsFedoraThirdParty *self,
+ GHashTable **out_repos,
+ GCancellable *cancellable,
+ GError **error);
+
+/* Utility functions */
+gboolean gs_fedora_third_party_util_is_third_party_repo
+ (GHashTable *third_party_repos,
+ const gchar *origin,
+ const gchar *management_plugin);
+
+G_END_DECLS
diff --git a/lib/meson.build b/lib/meson.build
index f089e2d67..3b8c92339 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -82,6 +82,7 @@ libgnomesoftware = library(
'gs-debug.c',
'gs-desktop-data.c',
'gs-external-appstream-utils.c',
+ 'gs-fedora-third-party.c',
'gs-icon.c',
'gs-ioprio.c',
'gs-ioprio.h',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]