diff options
Diffstat (limited to 'plugins/packagekit/gs-plugin-systemd-updates.c')
-rw-r--r-- | plugins/packagekit/gs-plugin-systemd-updates.c | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/plugins/packagekit/gs-plugin-systemd-updates.c b/plugins/packagekit/gs-plugin-systemd-updates.c new file mode 100644 index 0000000..ea8ff1a --- /dev/null +++ b/plugins/packagekit/gs-plugin-systemd-updates.c @@ -0,0 +1,330 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2013 Richard Hughes <richard@hughsie.com> + * Copyright (C) 2015-2016 Kalev Lember <klember@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> + +#include <packagekit-glib2/packagekit.h> + +#include "packagekit-common.h" + +#include <gnome-software.h> + +/* + * Mark previously downloaded packages as zero size, and also allow + * scheduling the offline update. + */ + +struct GsPluginData { + GFileMonitor *monitor; + GFileMonitor *monitor_trigger; + GPermission *permission; + gboolean is_triggered; + GHashTable *hash_prepared; + GMutex hash_prepared_mutex; +}; + +void +gs_plugin_initialize (GsPlugin *plugin) +{ + GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData)); + gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "packagekit-refresh"); + gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "packagekit-refine"); + gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_BEFORE, "generic-updates"); + g_mutex_init (&priv->hash_prepared_mutex); + priv->hash_prepared = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); +} + +void +gs_plugin_destroy (GsPlugin *plugin) +{ + GsPluginData *priv = gs_plugin_get_data (plugin); + g_hash_table_unref (priv->hash_prepared); + g_mutex_clear (&priv->hash_prepared_mutex); + if (priv->monitor != NULL) + g_object_unref (priv->monitor); + if (priv->monitor_trigger != NULL) + g_object_unref (priv->monitor_trigger); +} + +static void +gs_plugin_systemd_updates_permission_cb (GPermission *permission, + GParamSpec *pspec, + gpointer data) +{ + GsPlugin *plugin = GS_PLUGIN (data); + gboolean ret = g_permission_get_allowed (permission) || + g_permission_get_can_acquire (permission); + gs_plugin_set_allow_updates (plugin, ret); +} + +static gboolean +gs_plugin_systemd_update_cache (GsPlugin *plugin, GError **error) +{ + GsPluginData *priv = gs_plugin_get_data (plugin); + g_autoptr(GError) error_local = NULL; + g_auto(GStrv) package_ids = NULL; + g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->hash_prepared_mutex); + + /* invalidate */ + g_hash_table_remove_all (priv->hash_prepared); + + /* get new list of package-ids */ + package_ids = pk_offline_get_prepared_ids (&error_local); + if (package_ids == NULL) { + if (g_error_matches (error_local, + PK_OFFLINE_ERROR, + PK_OFFLINE_ERROR_NO_DATA)) { + return TRUE; + } + g_set_error (error, + GS_PLUGIN_ERROR, + GS_PLUGIN_ERROR_INVALID_FORMAT, + "Failed to get prepared IDs: %s", + error_local->message); + return FALSE; + } + for (guint i = 0; package_ids[i] != NULL; i++) { + g_hash_table_insert (priv->hash_prepared, + g_strdup (package_ids[i]), + GUINT_TO_POINTER (1)); + } + return TRUE; +} + +static void +gs_plugin_systemd_updates_changed_cb (GFileMonitor *monitor, + GFile *file, GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + GsPlugin *plugin = GS_PLUGIN (user_data); + + /* update UI */ + gs_plugin_systemd_update_cache (plugin, NULL); + gs_plugin_updates_changed (plugin); +} + +static void +gs_plugin_systemd_updates_refresh_is_triggered (GsPlugin *plugin, GCancellable *cancellable) +{ + GsPluginData *priv = gs_plugin_get_data (plugin); + g_autoptr(GFile) file_trigger = NULL; + file_trigger = g_file_new_for_path ("/system-update"); + priv->is_triggered = g_file_query_exists (file_trigger, NULL); + g_debug ("offline trigger is now %s", + priv->is_triggered ? "enabled" : "disabled"); +} + +static void +gs_plugin_systemd_trigger_changed_cb (GFileMonitor *monitor, + GFile *file, GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + GsPlugin *plugin = GS_PLUGIN (user_data); + gs_plugin_systemd_updates_refresh_is_triggered (plugin, NULL); +} + +static void +gs_plugin_systemd_refine_app (GsPlugin *plugin, GsApp *app) +{ + GsPluginData *priv = gs_plugin_get_data (plugin); + const gchar *package_id; + g_autoptr(GMutexLocker) locker = NULL; + + /* only process this app if was created by this plugin */ + if (g_strcmp0 (gs_app_get_management_plugin (app), "packagekit") != 0) + return; + + /* the package is already downloaded */ + package_id = gs_app_get_source_id_default (app); + if (package_id == NULL) + return; + locker = g_mutex_locker_new (&priv->hash_prepared_mutex); + if (g_hash_table_lookup (priv->hash_prepared, package_id) != NULL) + gs_app_set_size_download (app, 0); +} + +gboolean +gs_plugin_refine (GsPlugin *plugin, + GsAppList *list, + GsPluginRefineFlags flags, + GCancellable *cancellable, + GError **error) +{ + /* not now */ + if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE) == 0) + return TRUE; + + /* re-read /var/lib/PackageKit/prepared-update */ + if (!gs_plugin_systemd_update_cache (plugin, error)) + return FALSE; + + for (guint i = 0; i < gs_app_list_length (list); i++) { + GsApp *app = gs_app_list_index (list, i); + GsAppList *related = gs_app_get_related (app); + /* refine the app itself */ + gs_plugin_systemd_refine_app (plugin, app); + /* and anything related for proxy apps */ + for (guint j = 0; j < gs_app_list_length (related); j++) { + GsApp *app_related = gs_app_list_index (related, j); + gs_plugin_systemd_refine_app (plugin, app_related); + } + } + + return TRUE; +} + +gboolean +gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error) +{ + GsPluginData *priv = gs_plugin_get_data (plugin); + g_autoptr(GFile) file_trigger = NULL; + + /* watch the prepared file */ + priv->monitor = pk_offline_get_prepared_monitor (cancellable, error); + if (priv->monitor == NULL) { + gs_utils_error_convert_gio (error); + return FALSE; + } + g_signal_connect (priv->monitor, "changed", + G_CALLBACK (gs_plugin_systemd_updates_changed_cb), + plugin); + + /* watch the trigger file */ + file_trigger = g_file_new_for_path ("/system-update"); + priv->monitor_trigger = g_file_monitor_file (file_trigger, + G_FILE_MONITOR_NONE, + NULL, + error); + if (priv->monitor_trigger == NULL) { + gs_utils_error_convert_gio (error); + return FALSE; + } + g_signal_connect (priv->monitor_trigger, "changed", + G_CALLBACK (gs_plugin_systemd_trigger_changed_cb), + plugin); + + /* check if we have permission to trigger the update */ + priv->permission = gs_utils_get_permission ( + "org.freedesktop.packagekit.trigger-offline-update", + NULL, NULL); + if (priv->permission != NULL) { + g_signal_connect (priv->permission, "notify", + G_CALLBACK (gs_plugin_systemd_updates_permission_cb), + plugin); + } + + /* get the list of currently downloaded packages */ + return gs_plugin_systemd_update_cache (plugin, error); +} + +static gboolean +_systemd_trigger_app (GsPlugin *plugin, + GsApp *app, + GCancellable *cancellable, + GError **error) +{ + GsPluginData *priv = gs_plugin_get_data (plugin); + + /* if we can process this online do not require a trigger */ + if (gs_app_get_state (app) != AS_APP_STATE_UPDATABLE) + return TRUE; + + /* only process this app if was created by this plugin */ + if (g_strcmp0 (gs_app_get_management_plugin (app), "packagekit") != 0) + return TRUE; + + /* already in correct state */ + if (priv->is_triggered) + return TRUE; + + /* trigger offline update */ + if (!pk_offline_trigger (PK_OFFLINE_ACTION_REBOOT, + cancellable, error)) { + gs_plugin_packagekit_error_convert (error); + return FALSE; + } + + /* don't rely on the file monitor */ + gs_plugin_systemd_updates_refresh_is_triggered (plugin, cancellable); + + /* success */ + return TRUE; +} + +gboolean +gs_plugin_update (GsPlugin *plugin, + GsAppList *list, + GCancellable *cancellable, + GError **error) +{ + /* any are us? */ + for (guint i = 0; i < gs_app_list_length (list); i++) { + GsApp *app = gs_app_list_index (list, i); + GsAppList *related = gs_app_get_related (app); + + /* try to trigger this app */ + if (!gs_app_has_quirk (app, GS_APP_QUIRK_IS_PROXY)) { + if (!_systemd_trigger_app (plugin, app, cancellable, error)) + return FALSE; + continue; + } + + /* try to trigger each related app */ + for (guint j = 0; j < gs_app_list_length (related); j++) { + GsApp *app_tmp = gs_app_list_index (related, j); + if (!_systemd_trigger_app (plugin, app_tmp, cancellable, error)) + return FALSE; + } + } + + /* success */ + return TRUE; +} + +gboolean +gs_plugin_update_cancel (GsPlugin *plugin, + GsApp *app, + GCancellable *cancellable, + GError **error) +{ + GsPluginData *priv = gs_plugin_get_data (plugin); + + /* only process this app if was created by this plugin */ + if (g_strcmp0 (gs_app_get_management_plugin (app), "packagekit") != 0) + return TRUE; + + /* already in correct state */ + if (!priv->is_triggered) + return TRUE; + + /* cancel offline update */ + if (!pk_offline_cancel (NULL, error)) + return FALSE; + + /* don't rely on the file monitor */ + gs_plugin_systemd_updates_refresh_is_triggered (plugin, cancellable); + + /* success! */ + return TRUE; +} + +gboolean +gs_plugin_app_upgrade_trigger (GsPlugin *plugin, + GsApp *app, + GCancellable *cancellable, + GError **error) +{ + /* only process this app if was created by this plugin */ + if (g_strcmp0 (gs_app_get_management_plugin (app), "packagekit") != 0) + return TRUE; + return pk_offline_trigger_upgrade (PK_OFFLINE_ACTION_REBOOT, cancellable, error); +} |