[gnome-software] Download updates
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Download updates
- Date: Sat, 2 Nov 2013 04:22:41 +0000 (UTC)
commit 774a67b55ca21901d4dca58717015503fce8db1b
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Oct 26 19:12:16 2013 -0400
Download updates
This moves more functionality from the gsd updates plugin
here:
* Notify about successful or unsuccessful offline updates
15 seconds after start.
* Monitor for available offline updates and notify about
them, once per hour.
* Refresh the cache once per day, after 6am.
* If important updates are available, or updates have not
been installed for at least 7 days, download all
available updates. We rely on PackageKit to prepare
an offline update when updates have been downloaded.
* Avoid mobile networks for cache refresh and downloads.
https://bugzilla.gnome.org/show_bug.cgi?id=709121
src/gs-offline-updates.c | 6 +
src/gs-update-monitor.c | 420 +++++++++++++++++++++++++++++++++++++++++-----
src/gs-utils.c | 54 ++++++
src/gs-utils.h | 3 +
4 files changed, 440 insertions(+), 43 deletions(-)
---
diff --git a/src/gs-offline-updates.c b/src/gs-offline-updates.c
index d427c58..bfbc763 100644
--- a/src/gs-offline-updates.c
+++ b/src/gs-offline-updates.c
@@ -25,6 +25,7 @@
#include <packagekit-glib2/packagekit.h>
#include "gs-offline-updates.h"
+#include "gs-utils.h"
static void
child_exit_cb (GPid pid, gint status, gpointer user_data)
@@ -61,6 +62,7 @@ gs_offline_updates_trigger (void)
gboolean ret;
GError *error = NULL;
const gchar *argv[3];
+ GDateTime *now;
argv[0] = "pkexec";
argv[1] = LIBEXECDIR "/pk-trigger-offline-update";
@@ -76,6 +78,10 @@ gs_offline_updates_trigger (void)
error->message);
g_error_free (error);
}
+
+ now = g_date_time_new_now_local ();
+ gs_save_timestamp_to_file ("install-timestamp", now);
+ g_date_time_unref (now);
}
void
diff --git a/src/gs-update-monitor.c b/src/gs-update-monitor.c
index 450c88a..dad1cc0 100644
--- a/src/gs-update-monitor.c
+++ b/src/gs-update-monitor.c
@@ -24,25 +24,33 @@
#include <string.h>
#include <glib/gi18n.h>
+#include <packagekit-glib2/packagekit.h>
+#include <gsettings-desktop-schemas/gdesktop-enums.h>
#include "gs-update-monitor.h"
#include "gs-utils.h"
#include "gs-offline-updates.h"
-#define GS_UPDATES_CHECK_OFFLINE_TIMEOUT 30 /* seconds */
-#define GS_REENABLE_OFFLINE_UPDATE_TIMEOUT 300 /* seconds */
-
-#define GS_UPDATES_ICON_NORMAL "software-update-available-symbolic"
-#define GS_UPDATES_ICON_URGENT "software-update-urgent-symbolic"
-
struct _GsUpdateMonitor {
GObject parent;
- GsApplication *application;
+ GApplication *application;
+ GCancellable *cancellable;
+
+ guint check_hourly_id;
+ GDateTime *check_timestamp;
+ GDateTime *install_timestamp;
+ gboolean refresh_cache_due;
+ gboolean get_updates_due;
+ gboolean network_available;
+ gchar **pending_downloads;
+ PkTask *task;
+ PkControl *control;
+
GFile *offline_update_file;
GFileMonitor *offline_update_monitor;
gboolean offline_update_notified;
-
+ guint reenable_offline_update_id;
guint check_offline_update_id;
};
@@ -52,20 +60,25 @@ struct _GsUpdateMonitorClass {
G_DEFINE_TYPE (GsUpdateMonitor, gs_update_monitor, G_TYPE_OBJECT)
+static void notify_offline_update_available (GsUpdateMonitor *monitor);
+
static gboolean
-reenable_offline_update (gpointer data)
+reenable_offline_update_notification (gpointer data)
{
GsUpdateMonitor *monitor = data;
monitor->offline_update_notified = FALSE;
+ monitor->reenable_offline_update_id = 0;
+
+ notify_offline_update_available (monitor);
+
return G_SOURCE_REMOVE;
}
static void
notify_offline_update_available (GsUpdateMonitor *monitor)
{
- guint id;
GNotification *n;
const gchar *title;
const gchar *body;
@@ -78,9 +91,9 @@ notify_offline_update_available (GsUpdateMonitor *monitor)
monitor->offline_update_notified = TRUE;
- /* don't notify more often than every 5 minutes */
- id = g_timeout_add_seconds (GS_REENABLE_OFFLINE_UPDATE_TIMEOUT, reenable_offline_update, monitor);
- g_source_set_name_by_id (id, "[gnome-software] reenable_offline_update");
+ /* don't notify more often than once every hour */
+ monitor->reenable_offline_update_id = g_timeout_add_seconds (3600,
reenable_offline_update_notification, monitor);
+ g_source_set_name_by_id (monitor->reenable_offline_update_id, "[gnome-software]
reenable_offline_update_notification");
title = _("Software Updates Available");
body = _("Important OS and application updates are ready to be installed");
@@ -89,28 +102,29 @@ notify_offline_update_available (GsUpdateMonitor *monitor)
g_notification_add_button_with_target (n, _("View"), "app.set-mode", "s", "updates");
g_notification_add_button (n, _("Not Now"), "app.nop");
g_notification_set_default_action_and_target (n, "app.set-mode", "s", "updates");
- g_application_send_notification (g_application_get_default (), "updates-available", n);
+ g_application_send_notification (monitor->application, "updates-available", n);
g_object_unref (n);
}
static void
-offline_update_cb (GFileMonitor *file_monitor,
- GFile *file,
- GFile *other_file,
- GFileMonitorEvent event_type,
- GsUpdateMonitor *monitor)
+offline_update_monitor_cb (GFileMonitor *file_monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GsUpdateMonitor *monitor)
{
notify_offline_update_available (monitor);
}
-static gboolean
-initial_offline_update_check (gpointer data)
+static void
+start_monitoring_offline_updates (GsUpdateMonitor *monitor)
{
- GsUpdateMonitor *monitor = data;
+ monitor->offline_update_file = g_file_new_for_path ("/var/lib/PackageKit/prepared-update");
+ monitor->offline_update_monitor = g_file_monitor_file (monitor->offline_update_file, 0, NULL, NULL);
+ g_signal_connect (monitor->offline_update_monitor, "changed",
+ G_CALLBACK (offline_update_monitor_cb), monitor);
notify_offline_update_available (monitor);
-
- return G_SOURCE_REMOVE;
}
static gboolean
@@ -147,7 +161,7 @@ check_offline_update_cb (gpointer user_data)
notification = g_notification_new (title);
g_notification_set_body (notification, message);
- icon = g_themed_icon_new (GS_UPDATES_ICON_URGENT);
+ icon = g_themed_icon_new ("software-update-urgent-symbolic");
g_notification_set_icon (notification, icon);
g_object_unref (icon);
if (success)
@@ -156,37 +170,341 @@ check_offline_update_cb (gpointer user_data)
g_notification_add_button (notification, _("Show Details"), "app.show-offline-update-error");
g_notification_add_button (notification, _("OK"), "app.clear-offline-updates");
- g_application_send_notification (g_application_get_default (), "offline-updates", notification);
+ g_application_send_notification (monitor->application, "offline-updates", notification);
g_object_unref (notification);
out:
+ start_monitoring_offline_updates (monitor);
+
monitor->check_offline_update_id = 0;
return G_SOURCE_REMOVE;
}
+static gboolean
+has_important_updates (GPtrArray *packages)
+{
+ guint i;
+ PkPackage *pkg;
+
+ for (i = 0; i < packages->len; i++) {
+ pkg = g_ptr_array_index (packages, i);
+ if (pk_package_get_info (pkg) == PK_INFO_ENUM_SECURITY ||
+ pk_package_get_info (pkg) == PK_INFO_ENUM_IMPORTANT)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+no_updates_for_a_week (GsUpdateMonitor *monitor)
+{
+ GDateTime *last_update;
+ GDateTime *now;
+ GTimeSpan d;
+
+ last_update = gs_read_timestamp_from_file ("install-timestamp");
+ if (!last_update)
+ return TRUE;
+
+ now = g_date_time_new_now_local ();
+ d = g_date_time_difference (now, last_update);
+ g_date_time_unref (last_update);
+ g_date_time_unref (now);
+
+ if (d >= 7 * G_TIME_SPAN_DAY)
+ return TRUE;
+
+ return FALSE;
+}
+
static void
-gs_update_monitor_init (GsUpdateMonitor *monitor)
+package_download_finished_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer data)
{
- guint id;
+ GsUpdateMonitor *monitor = data;
+ PkResults *results;
+ GError *error = NULL;
+ PkError *error_code;
+
+ results = pk_client_generic_finish (PK_CLIENT (object), res, &error);
+ if (results == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("failed to download: %s", error->message);
+ }
+ g_error_free (error);
+ return;
+ }
- monitor->offline_update_file = g_file_new_for_path ("/var/lib/PackageKit/prepared-update");
- monitor->offline_update_monitor = g_file_monitor_file (monitor->offline_update_file, 0, NULL, NULL);
+ error_code = pk_results_get_error_code (results);
+ if (error_code != NULL) {
+ g_warning ("failed to download: %s, %s",
+ pk_error_enum_to_string (pk_error_get_code (error_code)),
+ pk_error_get_details (error_code));
+ g_object_unref (error_code);
+ g_object_unref (results);
+ return;
+ }
- g_signal_connect (monitor->offline_update_monitor, "changed",
- G_CALLBACK (offline_update_cb), monitor);
+ g_debug ("Downloaded updates");
+
+ g_clear_pointer (&monitor->pending_downloads, g_strfreev);
+ g_object_unref (results);
+}
+
+static void
+download_updates (GsUpdateMonitor *monitor)
+{
+ if (monitor->pending_downloads == NULL)
+ return;
+
+ if (!monitor->network_available)
+ return;
+
+ g_debug ("Downloading updates");
+
+ pk_task_update_packages_async (monitor->task,
+ monitor->pending_downloads,
+ monitor->cancellable,
+ NULL, NULL,
+ package_download_finished_cb,
+ monitor);
+}
+
+static void
+get_updates_finished_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GsUpdateMonitor *monitor = data;
+ PkResults *results;
+ PkError *error_code;
+ GError *error = NULL;
+ GPtrArray *packages;
+ guint i;
+ PkPackage *pkg;
+
+ results = pk_client_generic_finish (PK_CLIENT (object), res, &error);
+ if (results == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("failed to get updates: %s", error->message);
+ }
+ g_error_free (error);
+ return;
+ }
+
+ error_code = pk_results_get_error_code (results);
+ if (error_code != NULL) {
+ g_warning ("failed to get updates: %s, %s",
+ pk_error_enum_to_string (pk_error_get_code (error_code)),
+ pk_error_get_details (error_code));
+ g_object_unref (error_code);
+ g_object_unref (results);
+ return;
+ }
+
+ /* we succeeded */
+ monitor->get_updates_due = FALSE;
+
+ packages = pk_results_get_package_array (results);
+
+ g_debug ("Got %d updates", packages->len);
+
+ if (has_important_updates (packages) ||
+ no_updates_for_a_week (monitor)) {
+
+ monitor->pending_downloads = g_new0 (gchar *, packages->len + 1);
+ for (i = 0; i < packages->len; i++) {
+ pkg = (PkPackage *)g_ptr_array_index (packages, i);
+ monitor->pending_downloads[i] = g_strdup (pk_package_get_id (pkg));
+ }
+ monitor->pending_downloads[packages->len] = NULL;
+
+ download_updates (monitor);
+ }
+
+ g_ptr_array_unref (packages);
+ g_object_unref (results);
+}
+
+static void
+get_updates (GsUpdateMonitor *monitor)
+{
+ if (monitor->refresh_cache_due)
+ return;
+
+ if (!monitor->get_updates_due)
+ return;
+
+ g_debug ("Getting updates");
+
+ pk_client_get_updates_async (PK_CLIENT (monitor->task),
+ pk_bitfield_value (PK_FILTER_ENUM_NONE),
+ monitor->cancellable,
+ NULL, NULL,
+ get_updates_finished_cb,
+ monitor);
+}
+
+static void
+refresh_cache_finished_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GsUpdateMonitor *monitor = data;
+ PkResults *results;
+ PkError *error_code;
+ GError *error = NULL;
+
+ results = pk_client_generic_finish (PK_CLIENT (object), res, &error);
+ if (results == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("failed to refresh the cache: %s", error->message);
+ }
+ g_error_free (error);
+ return;
+ }
+
+ error_code = pk_results_get_error_code (results);
+ if (error_code != NULL) {
+ g_warning ("failed to refresh the cache: %s, %s",
+ pk_error_enum_to_string (pk_error_get_code (error_code)),
+ pk_error_get_details (error_code));
+ g_object_unref (error_code);
+ g_object_unref (results);
+ return;
+ }
+
+ monitor->refresh_cache_due = FALSE;
- id = g_timeout_add_seconds (GS_REENABLE_OFFLINE_UPDATE_TIMEOUT,
- initial_offline_update_check,
- monitor);
- g_source_set_name_by_id (id, "[gnome-software] initial_offline_update_check");
+ g_object_unref (results);
+ get_updates (monitor);
+}
+
+static void
+refresh_cache (GsUpdateMonitor *monitor)
+{
+ if (!monitor->refresh_cache_due)
+ return;
+
+ if (!monitor->network_available)
+ return;
+
+ g_debug ("Refreshing cache");
+
+ pk_client_refresh_cache_async (PK_CLIENT (monitor->task),
+ TRUE,
+ monitor->cancellable,
+ NULL, NULL,
+ refresh_cache_finished_cb,
+ monitor);
+}
+
+static gboolean
+check_hourly_cb (gpointer data)
+{
+ GsUpdateMonitor *monitor = data;
+
+ g_debug ("Hourly updates check");
+
+ /* no need to check again */
+ if (monitor->refresh_cache_due)
+ return G_SOURCE_CONTINUE;
+
+ if (monitor->check_timestamp != NULL) {
+ GDateTime *now;
+ gint now_year, now_month, now_day, now_hour;
+ gint year, month, day;
+
+ now = g_date_time_new_now_local ();
+
+ g_date_time_get_ymd (now, &now_year, &now_month, &now_day);
+ now_hour = g_date_time_get_hour (now);
+ g_date_time_unref (now);
+
+ g_date_time_get_ymd (monitor->check_timestamp, &year, &month, &day);
+
+ /* check that it is the next day */
+ if (!((now_year > year) ||
+ (now_year == year && now_month > month) ||
+ (now_year == year && now_month == month && now_day > day)))
+ return G_SOURCE_CONTINUE;
+
+ /* ...and past 6am */
+ if (!(now_hour >= 6))
+ return G_SOURCE_CONTINUE;
+
+ g_clear_pointer (&monitor->check_timestamp, g_date_time_unref);
+ }
+
+ g_debug ("Daily update check due");
+
+ monitor->check_timestamp = g_date_time_new_now_local ();
+ gs_save_timestamp_to_file ("check-timestamp", monitor->check_timestamp);
+
+ monitor->refresh_cache_due = TRUE;
+ monitor->get_updates_due = TRUE;
+
+ refresh_cache (monitor);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+notify_network_state_cb (PkControl *control,
+ GParamSpec *pspec,
+ GsUpdateMonitor *monitor)
+{
+ PkNetworkEnum network_state;
+ gboolean available;
+
+ g_object_get (control, "network-state", &network_state, NULL);
+
+ if (network_state == PK_NETWORK_ENUM_OFFLINE ||
+ network_state == PK_NETWORK_ENUM_MOBILE)
+ available = FALSE;
+ else
+ available = TRUE;
+
+ if (monitor->network_available != available) {
+ monitor->network_available = available;
+
+ refresh_cache (monitor);
+ get_updates (monitor);
+ download_updates (monitor);
+ }
+}
+
+static void
+gs_update_monitor_init (GsUpdateMonitor *monitor)
+{
monitor->check_offline_update_id =
- g_timeout_add_seconds (GS_UPDATES_CHECK_OFFLINE_TIMEOUT,
- check_offline_update_cb,
- monitor);
+ g_timeout_add_seconds (15, check_offline_update_cb, monitor);
g_source_set_name_by_id (monitor->check_offline_update_id,
- "[gnpome-software] check_offline_update_cb");
+ "[gnome-software] check_offline_update_cb");
+
+ monitor->check_timestamp = gs_read_timestamp_from_file ("check-timestamp");
+
+ monitor->check_hourly_id =
+ g_timeout_add_seconds (3600, check_hourly_cb, monitor);
+ g_source_set_name_by_id (monitor->check_hourly_id,
+ "[gnome-software] check_hourly_cb");
+
+ monitor->cancellable = g_cancellable_new ();
+ monitor->task = pk_task_new ();
+ g_object_set (monitor->task,
+ "background", TRUE,
+ "interactive", FALSE,
+ "only-download", TRUE,
+ NULL);
+
+ monitor->network_available = FALSE;
+ monitor->control = pk_control_new ();
+ g_signal_connect (monitor->control, "notify::network-state",
+ G_CALLBACK (notify_network_state_cb), monitor);
}
static void
@@ -194,13 +512,29 @@ gs_update_monitor_finalize (GObject *object)
{
GsUpdateMonitor *monitor = GS_UPDATE_MONITOR (object);
+ if (monitor->cancellable) {
+ g_cancellable_cancel (monitor->cancellable);
+ g_clear_object (&monitor->cancellable);
+ }
+ if (monitor->check_hourly_id != 0) {
+ g_source_remove (monitor->check_hourly_id);
+ monitor->check_hourly_id = 0;
+ }
if (monitor->check_offline_update_id != 0) {
g_source_remove (monitor->check_offline_update_id);
monitor->check_offline_update_id = 0;
}
+ if (monitor->reenable_offline_update_id != 0) {
+ g_source_remove (monitor->reenable_offline_update_id);
+ monitor->reenable_offline_update_id = 0;
+ }
+ g_clear_pointer (&monitor->pending_downloads, g_strfreev);
+ g_clear_pointer (&monitor->check_timestamp, g_date_time_unref);
+ g_clear_object (&monitor->task);
+ g_clear_object (&monitor->control);
g_clear_object (&monitor->offline_update_file);
g_clear_object (&monitor->offline_update_monitor);
- g_application_release (G_APPLICATION (monitor->application));
+ g_application_release (monitor->application);
G_OBJECT_CLASS (gs_update_monitor_parent_class)->finalize (object);
}
@@ -218,8 +552,8 @@ gs_update_monitor_new (GsApplication *application)
GsUpdateMonitor *monitor;
monitor = GS_UPDATE_MONITOR (g_object_new (GS_TYPE_UPDATE_MONITOR, NULL));
- monitor->application = application;
- g_application_hold (G_APPLICATION (application));
+ monitor->application = G_APPLICATION (application);
+ g_application_hold (monitor->application);
return monitor;
}
diff --git a/src/gs-utils.c b/src/gs-utils.c
index 4b1af7e..6b9dc60 100644
--- a/src/gs-utils.c
+++ b/src/gs-utils.c
@@ -340,4 +340,58 @@ gs_reboot (GCallback reboot_failed)
g_object_unref (bus);
}
+GDateTime *
+gs_read_timestamp_from_file (const gchar *name)
+{
+ gchar *file;
+ gchar *contents;
+ GDateTime *result;
+
+ result = NULL;
+ file = g_build_filename (g_get_user_data_dir (),
+ "gnome-software", name, NULL);
+ if (g_file_get_contents (file, &contents, NULL, NULL)) {
+ gint64 timestamp;
+ gchar *endptr = NULL;
+
+ timestamp = g_ascii_strtoll (contents, &endptr, 0);
+ if (endptr) {
+ g_warning ("Could not read %s timestamp: %s", name, contents);
+ } else {
+ result = g_date_time_new_from_unix_local (timestamp);
+ }
+ g_free (contents);
+ }
+ g_free (file);
+
+ return result;
+}
+
+gboolean
+gs_save_timestamp_to_file (const gchar *name,
+ GDateTime *date)
+{
+ gchar *file;
+ gchar *contents;
+ gint64 timestamp;
+ gboolean result;
+ GError *error = NULL;
+
+ result = TRUE;
+ timestamp = g_date_time_to_unix (date);
+ contents = g_strdup_printf ("%" G_GINT64_FORMAT, timestamp);
+ file = g_build_filename (g_get_user_data_dir (),
+ "gnome-software", name, NULL);
+ if (!g_file_set_contents (file, contents, -1, &error)) {
+ g_warning ("Could not save %s timestamp: %s",
+ name, error->message);
+ g_error_free (error);
+ result = FALSE;
+ }
+ g_free (contents);
+ g_free (file);
+
+ return result;
+}
+
/* vim: set noexpandtab: */
diff --git a/src/gs-utils.h b/src/gs-utils.h
index ecfdd84..9fc36b8 100644
--- a/src/gs-utils.h
+++ b/src/gs-utils.h
@@ -49,6 +49,9 @@ GdkPixbuf *gs_pixbuf_load (const gchar *icon_name,
guint icon_size,
GError **error);
void gs_reboot (GCallback reboot_failed);
+GDateTime *gs_read_timestamp_from_file (const gchar *name);
+gboolean gs_save_timestamp_to_file (const gchar *name,
+ GDateTime *date);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]