[gnome-settings-daemon] updates: migrate the UDev firmware installing functionality from gnome-packagekit



commit 8bd31ca2474e43c971aa76ccb5f46c66bb23e71b
Author: Richard Hughes <richard hughsie com>
Date:   Wed Feb 16 22:41:42 2011 +0000

    updates: migrate the UDev firmware installing functionality from gnome-packagekit

 configure.ac                                       |   18 +
 ...ttings-daemon.plugins.updates.gschema.xml.in.in |   15 +
 plugins/updates/Makefile.am                        |    2 +
 plugins/updates/gsd-updates-common.h               |   48 +
 plugins/updates/gsd-updates-firmware.c             | 1012 ++++++++++++++++++++
 plugins/updates/gsd-updates-firmware.h             |   52 +
 plugins/updates/gsd-updates-manager.c              |   10 +
 plugins/updates/gsd-updates-refresh.c              |    1 +
 plugins/updates/gsd-updates-refresh.h              |   16 -
 po/POTFILES.in                                     |    1 +
 10 files changed, 1159 insertions(+), 16 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 48c016a..88ee3d8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -291,6 +291,24 @@ AM_CONDITIONAL(HAVE_PACKAGEKIT, test "x$have_packagekit" = "xtrue")
 AC_SUBST(PACKAGEKIT_CFLAGS)
 AC_SUBST(PACKAGEKIT_LIBS)
 
+dnl ---------------------------------------------------------------------------
+dnl - GUdev integration (default enabled)
+dnl ---------------------------------------------------------------------------
+AC_ARG_ENABLE(gudev, AS_HELP_STRING([--disable-gudev],[Disable GUdev support]), enable_gudev=$enableval)
+if test x$enable_gudev != xno; then
+	PKG_CHECK_MODULES(GUDEV, gudev-1.0, HAVE_GUDEV="yes", HAVE_GUDEV="no")
+	if test "x$HAVE_GUDEV" = "xyes"; then
+		AC_DEFINE(HAVE_GUDEV, 1, [define if GUdev is available])
+	else
+		if test x$enable_gudev = xyes; then
+			AC_MSG_ERROR([GUdev enabled but not found])
+		fi
+	fi
+else
+	HAVE_GUDEV=no
+fi
+AM_CONDITIONAL(HAVE_GUDEV, test x$HAVE_GUDEV = xyes)
+
 
 dnl ==============================================
 dnl smartcard section
diff --git a/data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in.in b/data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in.in
index 208f6cb..7354472 100644
--- a/data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in.in
+++ b/data/org.gnome.settings-daemon.plugins.updates.gschema.xml.in.in
@@ -90,5 +90,20 @@
       <_summary>Notify the user when the update type is available</_summary>
       <_description>Notify the user when updates are available of a certain type and not auto-installed.</_description>
     </key>
+    <key name="enable-check-firmware" type="b">
+      <default>true</default>
+      <_summary>Ask the user if additional firmware should be installed</_summary>
+      <_description>Ask the user if additional firmware should be installed if it is available.</_description>
+    </key>
+    <key name="banned-firmware" type="s">
+      <default>'*/intel-ucode/*'</default>
+      <_summary>Firmware files that should not be searched for</_summary>
+      <_description>Firmware files that should not be searched for, separated by commas. These can include '*' and '?' characters.</_description>
+    </key>
+    <key name="ignored-devices" type="s">
+      <default>''</default>
+      <_summary>Devices that should be ignored</_summary>
+      <_description>Devices that should be ignored, separated by commas. These can include '*' and '?' characters.</_description>
+    </key>
   </schema>
 </schemalist>
diff --git a/plugins/updates/Makefile.am b/plugins/updates/Makefile.am
index b06469a..cc20d57 100644
--- a/plugins/updates/Makefile.am
+++ b/plugins/updates/Makefile.am
@@ -6,6 +6,8 @@ libupdates_la_SOURCES = \
 	gsd-updates-plugin.c \
 	gsd-updates-refresh.h \
 	gsd-updates-refresh.c \
+	gsd-updates-firmware.h \
+	gsd-updates-firmware.c \
 	gsd-updates-manager.h \
 	gsd-updates-manager.c
 
diff --git a/plugins/updates/gsd-updates-common.h b/plugins/updates/gsd-updates-common.h
new file mode 100644
index 0000000..8a576da
--- /dev/null
+++ b/plugins/updates/gsd-updates-common.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GSD_UPDATES_COMMON_H
+#define __GSD_UPDATES_COMMON_H
+
+G_BEGIN_DECLS
+
+#define GSD_SETTINGS_AUTO_UPDATE_TYPE                   "auto-update-type"
+#define GSD_SETTINGS_BANNED_FIRMWARE                    "banned-firmware"
+#define GSD_SETTINGS_CONNECTION_USE_MOBILE              "connection-use-mobile"
+#define GSD_SETTINGS_CONNECTION_USE_WIFI                "connection-use-wifi"
+#define GSD_SETTINGS_ENABLE_CHECK_FIRMWARE              "enable-check-firmware"
+#define GSD_SETTINGS_FORCE_GET_UPDATES_LOGIN            "force-get-updates-login"
+#define GSD_SETTINGS_FREQUENCY_GET_UPDATES              "frequency-get-updates"
+#define GSD_SETTINGS_FREQUENCY_GET_UPGRADES             "frequency-get-upgrades"
+#define GSD_SETTINGS_FREQUENCY_REFRESH_CACHE            "frequency-refresh-cache"
+#define GSD_SETTINGS_IGNORED_DEVICES                    "ignored-devices"
+#define GSD_SETTINGS_NOTIFY_DISTRO_UPGRADES             "notify-distro-upgrades"
+#define GSD_SETTINGS_NOTIFY_UPDATE_COMPLETE             "notify-update-complete"
+#define GSD_SETTINGS_NOTIFY_UPDATE_COMPLETE_RESTART     "notify-update-complete-restart"
+#define GSD_SETTINGS_NOTIFY_UPDATE_NOT_BATTERY          "notify-update-not-battery"
+#define GSD_SETTINGS_NOTIFY_UPDATE_TYPE                 "notify-update-type"
+#define GSD_SETTINGS_SCHEMA                             "org.gnome.settings-daemon.plugins.updates"
+#define GSD_SETTINGS_SESSION_STARTUP_TIMEOUT            "session-startup-timeout"
+#define GSD_SETTINGS_UPDATE_BATTERY                     "update-battery"
+
+G_END_DECLS
+
+#endif /* __GSD_UPDATES_COMMON_H */
diff --git a/plugins/updates/gsd-updates-firmware.c b/plugins/updates/gsd-updates-firmware.c
new file mode 100644
index 0000000..f972a17
--- /dev/null
+++ b/plugins/updates/gsd-updates-firmware.c
@@ -0,0 +1,1012 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2011 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <libnotify/notify.h>
+#include <packagekit-glib2/packagekit.h>
+#ifdef HAVE_GUDEV
+#include <gudev/gudev.h>
+#endif
+
+#include "gsd-updates-common.h"
+#include "gsd-updates-firmware.h"
+
+static void     gsd_updates_firmware_finalize   (GObject          *object);
+
+#define GSD_UPDATES_FIRMWARE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_UPDATES_TYPE_FIRMWARE, GsdUpdatesFirmwarePrivate))
+#define GSD_UPDATES_FIRMWARE_MISSING_DIR                "/dev/.udev/firmware-missing"
+#define GSD_UPDATES_FIRMWARE_LOADING_DIR                "/lib/firmware"
+#define GSD_UPDATES_FIRMWARE_LOGIN_DELAY                10 /* seconds */
+#define GSD_UPDATES_FIRMWARE_PROCESS_DELAY              2 /* seconds */
+#define GSD_UPDATES_FIRMWARE_INSERT_DELAY               2 /* seconds */
+#define GSD_UPDATES_FIRMWARE_DEVICE_REBIND_PROGRAM      "/usr/sbin/pk-device-rebind"
+
+struct GsdUpdatesFirmwarePrivate
+{
+        GSettings               *settings;
+        GFileMonitor            *monitor;
+        GPtrArray               *array_requested;
+        PkTask                  *task;
+        GPtrArray               *packages_found;
+        guint                    timeout_id;
+};
+
+typedef enum {
+        FIRMWARE_SUBSYSTEM_USB,
+        FIRMWARE_SUBSYSTEM_PCI,
+        FIRMWARE_SUBSYSTEM_UNKNOWN
+} FirmwareSubsystem;
+
+typedef struct {
+        gchar                   *filename;
+        gchar                   *sysfs_path;
+        gchar                   *model;
+        gchar                   *id;
+        FirmwareSubsystem        subsystem;
+} GsdUpdatesFirmwareRequest;
+
+G_DEFINE_TYPE (GsdUpdatesFirmware, gsd_updates_firmware, G_TYPE_OBJECT)
+
+static void install_package_ids (GsdUpdatesFirmware *firmware);
+static void ignore_devices (GsdUpdatesFirmware *firmware);
+
+static gboolean
+subsystem_can_replug (FirmwareSubsystem subsystem)
+{
+        if (subsystem == FIRMWARE_SUBSYSTEM_USB)
+                return TRUE;
+        return FALSE;
+}
+
+static GsdUpdatesFirmwareRequest *
+request_new (const gchar *filename, const gchar *sysfs_path)
+{
+        GsdUpdatesFirmwareRequest *req;
+#ifdef HAVE_GUDEV
+        GUdevDevice *device;
+        GUdevClient *client;
+        const gchar *subsystem;
+        const gchar *model;
+        const gchar *id_vendor;
+        const gchar *id_product;
+#endif
+
+        req = g_new0 (GsdUpdatesFirmwareRequest, 1);
+        req->filename = g_strdup (filename);
+        req->sysfs_path = g_strdup (sysfs_path);
+        req->subsystem = FIRMWARE_SUBSYSTEM_UNKNOWN;
+#ifdef HAVE_GUDEV
+
+        /* get all subsystems */
+        client = g_udev_client_new (NULL);
+        device = g_udev_client_query_by_sysfs_path (client, sysfs_path);
+        if (device == NULL)
+                goto out;
+
+        /* find subsystem, which will affect if we have to replug, or reboot */
+        subsystem = g_udev_device_get_subsystem (device);
+        if (g_strcmp0 (subsystem, "usb") == 0) {
+                req->subsystem = FIRMWARE_SUBSYSTEM_USB;
+        } else if (g_strcmp0 (subsystem, "pci") == 0) {
+                req->subsystem = FIRMWARE_SUBSYSTEM_PCI;
+        } else {
+                g_warning ("subsystem unrecognised: %s", subsystem);
+        }
+
+        /* get model, so we can show something sensible */
+        model = g_udev_device_get_property (device, "ID_MODEL");
+        if (model != NULL && model[0] != '\0') {
+                req->model = g_strdup (model);
+                /* replace invalid chars */
+                g_strdelimit (req->model, "_", ' ');
+        }
+
+        /* create ID so we can ignore the specific device */
+        id_vendor = g_udev_device_get_property (device, "ID_VENDOR");
+        id_product = g_udev_device_get_property (device, "ID_MODEL_ID");
+        req->id = g_strdup_printf ("%s_%s", id_vendor, id_product);
+out:
+        g_object_unref (device);
+        g_object_unref (client);
+#endif
+        return req;
+}
+
+static void
+request_free (GsdUpdatesFirmwareRequest *req)
+{
+        g_free (req->filename);
+        g_free (req->model);
+        g_free (req->sysfs_path);
+        g_free (req->id);
+        g_free (req);
+}
+
+static gboolean
+device_rebind (GsdUpdatesFirmware *firmware)
+{
+        gboolean ret;
+        gchar *command;
+        gchar *rebind_stderr = NULL;
+        gchar *rebind_stdout = NULL;
+        GError *error = NULL;
+        gint exit_status = 0;
+        guint i;
+        GPtrArray *array;
+        const GsdUpdatesFirmwareRequest *req;
+        GString *string;
+
+        string = g_string_new ("");
+
+        /* make a string array of all the devices to replug */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                g_string_append_printf (string, "%s ", req->sysfs_path);
+        }
+
+        /* remove trailing space */
+        if (string->len > 0)
+                g_string_set_size (string, string->len-1);
+
+        /* use PolicyKit to do this as root */
+        command = g_strdup_printf ("pkexec %s %s",
+                                   GSD_UPDATES_FIRMWARE_DEVICE_REBIND_PROGRAM,
+                                   string->str);
+        ret = g_spawn_command_line_sync (command,
+                                         &rebind_stdout,
+                                         &rebind_stderr,
+                                         &exit_status,
+                                         &error);
+        if (!ret) {
+                g_warning ("failed to spawn '%s': %s",
+                           command, error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* if we failed to rebind the device */
+        if (exit_status != 0) {
+                g_warning ("failed to rebind: %s, %s",
+                           rebind_stdout, rebind_stderr);
+                ret = FALSE;
+                goto out;
+        }
+out:
+        g_free (rebind_stdout);
+        g_free (rebind_stderr);
+        g_free (command);
+        g_string_free (string, TRUE);
+        return ret;
+}
+
+static void
+libnotify_cb (NotifyNotification *notification, gchar *action, gpointer data)
+{
+        GsdUpdatesFirmware *firmware = GSD_UPDATES_FIRMWARE (data);
+
+        if (g_strcmp0 (action, "install-firmware") == 0) {
+                install_package_ids (firmware);
+        } else if (g_strcmp0 (action, "ignore-devices") == 0) {
+                ignore_devices (firmware);
+        } else {
+                g_warning ("unknown action id: %s", action);
+        }
+}
+
+static void
+require_restart (GsdUpdatesFirmware *firmware)
+{
+        const gchar *message;
+        gboolean ret;
+        GError *error = NULL;
+        NotifyNotification *notification;
+
+        /* TRANSLATORS: we need to restart so the new hardware can re-request the firmware */
+        message = _("You will need to restart this computer before the hardware will work correctly.");
+
+        /* TRANSLATORS: title of libnotify bubble */
+        notification = notify_notification_new (_("Additional software was installed"), message, NULL);
+        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
+
+        /* show the bubble */
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+require_replug (GsdUpdatesFirmware *firmware)
+{
+        const gchar *message;
+        gboolean ret;
+        GError *error = NULL;
+        NotifyNotification *notification;
+
+        /* TRANSLATORS: we need to remove an replug so the new hardware can re-request the firmware */
+        message = _("You will need to remove and then reinsert the hardware before it will work correctly.");
+
+        /* TRANSLATORS: title of libnotify bubble */
+        notification = notify_notification_new (_("Additional software was installed"), message, NULL);
+        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
+
+        /* show the bubble */
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+require_nothing (GsdUpdatesFirmware *firmware)
+{
+        const gchar *message;
+        gboolean ret;
+        GError *error = NULL;
+        NotifyNotification *notification;
+
+        /* TRANSLATORS: we need to remove an replug so the new hardware can re-request the firmware */
+        message = _("Your hardware has been set up and is now ready to use.");
+
+        /* TRANSLATORS: title of libnotify bubble */
+        notification = notify_notification_new (_("Additional software was installed"), message, NULL);
+        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
+
+        /* show the bubble */
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+install_packages_cb (GObject *object,
+                     GAsyncResult *res,
+                     GsdUpdatesFirmware *firmware)
+{
+        PkClient *client = PK_CLIENT (object);
+        GError *error = NULL;
+        PkResults *results = NULL;
+        GPtrArray *array = NULL;
+        gboolean restart = FALSE;
+        const GsdUpdatesFirmwareRequest *req;
+        gboolean ret;
+        guint i;
+        PkError *error_code = NULL;
+
+        /* get the results */
+        results = pk_client_generic_finish (client, res, &error);
+        if (results == NULL) {
+                g_warning ("failed to install file: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* check error code */
+        error_code = pk_results_get_error_code (results);
+        if (error_code != NULL) {
+                g_warning ("failed to install file: %s, %s",
+                           pk_error_enum_to_text (pk_error_get_code (error_code)),
+                           pk_error_get_details (error_code));
+                goto out;
+        }
+
+        /* go through all the requests, and find the worst type */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                ret = subsystem_can_replug (req->subsystem);
+                if (!ret) {
+                        restart = TRUE;
+                        break;
+                }
+        }
+
+        /* can we just rebind the device */
+        ret = g_file_test (GSD_UPDATES_FIRMWARE_DEVICE_REBIND_PROGRAM, G_FILE_TEST_EXISTS);
+        if (ret) {
+                ret = device_rebind (firmware);
+                if (ret) {
+                        require_nothing (firmware);
+                        goto out;
+                }
+        } else {
+                /* give the user the correct message */
+                if (restart)
+                        require_restart (firmware);
+                else
+                        require_replug (firmware);
+        }
+
+        /* clear array */
+        g_ptr_array_set_size (firmware->priv->array_requested, 0);
+out:
+        if (error_code != NULL)
+                g_object_unref (error_code);
+        if (array != NULL)
+                g_ptr_array_unref (array);
+        if (results != NULL)
+                g_object_unref (results);
+}
+
+static gchar **
+package_array_to_strv (GPtrArray *array)
+{
+	PkPackage *item;
+	gchar **results;
+	guint i;
+
+	results = g_new0 (gchar *, array->len+1);
+	for (i=0; i<array->len; i++) {
+		item = g_ptr_array_index (array, i);
+		results[i] = g_strdup (pk_package_get_id (item));
+	}
+	return results;
+}
+
+static void
+install_package_ids (GsdUpdatesFirmware *firmware)
+{
+        gchar **package_ids;
+
+        /* install all of the firmware files */
+        package_ids = package_array_to_strv (firmware->priv->packages_found);
+        pk_client_install_packages_async (PK_CLIENT(firmware->priv->task),
+                                          TRUE, package_ids,
+                                          NULL,
+                                          NULL, NULL,
+                                          (GAsyncReadyCallback) install_packages_cb,
+                                          firmware);
+        g_strfreev (package_ids);
+}
+
+static void
+ignore_devices (GsdUpdatesFirmware *firmware)
+{
+        gchar *existing = NULL;
+        GsdUpdatesFirmwareRequest *req;
+        GPtrArray *array;
+        GString *string = NULL;
+        guint i;
+
+        /* get from settings */
+        existing = g_settings_get_string (firmware->priv->settings,
+                                          GSD_SETTINGS_IGNORED_DEVICES);
+
+        /* get existing string */
+        string = g_string_new (existing);
+        if (string->len > 0)
+                g_string_append (string, ",");
+
+        /* add all listed devices */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                g_string_append_printf (string, "%s,", req->id);
+        }
+
+        /* remove final ',' */
+        if (string->len > 2)
+                g_string_set_size (string, string->len - 1);
+
+        /* set new string */
+        g_settings_set_string (firmware->priv->settings,
+                               GSD_SETTINGS_IGNORED_DEVICES,
+                               string->str);
+
+        g_free (existing);
+        if (string != NULL)
+                g_string_free (string, TRUE);
+}
+
+static PkPackage *
+check_available (GsdUpdatesFirmware *firmware, const gchar *filename)
+{
+        guint length = 0;
+        GPtrArray *array = NULL;
+        GError *error = NULL;
+        PkPackage *item = NULL;
+        PkBitfield filter;
+        PkResults *results;
+        gchar **values = NULL;
+        PkError *error_code = NULL;
+
+        /* search for newest not installed package */
+        filter = pk_bitfield_from_enums (PK_FILTER_ENUM_NOT_INSTALLED,
+                                         PK_FILTER_ENUM_NEWEST, -1);
+        values = g_strsplit (filename, "&", -1);
+        results = pk_client_search_files (PK_CLIENT(firmware->priv->task),
+                                          filter,
+                                          values,
+                                          NULL,
+                                          NULL, NULL,
+                                          &error);
+        if (results == NULL) {
+                g_warning ("failed to search file %s: %s",
+                           filename, error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* check error code */
+        error_code = pk_results_get_error_code (results);
+        if (error_code != NULL) {
+                g_warning ("failed to search file: %s, %s",
+                           pk_error_enum_to_text (pk_error_get_code (error_code)),
+                           pk_error_get_details (error_code));
+                goto out;
+        }
+
+        /* make sure we have one package */
+        array = pk_results_get_package_array (results);
+        if (array->len == 0)
+                g_debug ("no package providing %s found", filename);
+        else if (array->len != 1)
+                g_warning ("not one package providing %s found (%i)", filename, length);
+        else
+                item = g_object_ref (g_ptr_array_index (array, 0));
+out:
+        g_strfreev (values);
+        if (error_code != NULL)
+                g_object_unref (error_code);
+        if (array != NULL)
+                g_object_unref (array);
+        if (results != NULL)
+                g_object_unref (results);
+        return item;
+}
+
+static void
+remove_duplicate (GPtrArray *array)
+{
+        guint i, j;
+        const gchar *val;
+        const gchar *val_tmp;
+
+        /* remove each duplicate entry */
+        for (i=0; i<array->len; i++) {
+                val = g_ptr_array_index (array, i);
+                for (j=i+1; j<array->len; j++) {
+                        val_tmp = g_ptr_array_index (array, j);
+                        if (g_strcmp0 (val_tmp, val) == 0)
+                                g_ptr_array_remove_index_fast (array, j);
+                }
+        }
+}
+
+static gboolean
+delay_timeout_cb (gpointer data)
+{
+        guint i;
+        gboolean ret;
+        GString *string;
+        GsdUpdatesFirmware *firmware = GSD_UPDATES_FIRMWARE (data);
+        NotifyNotification *notification;
+        GPtrArray *array;
+        GError *error = NULL;
+        PkPackage *item = NULL;
+        const GsdUpdatesFirmwareRequest *req;
+        gboolean has_data = FALSE;
+
+        /* message string */
+        string = g_string_new ("");
+
+        /* try to find each firmware file in an available package */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                /* save to new array if we found one package for this file */
+                item = check_available (firmware, req->filename);
+                if (item != NULL) {
+                        g_ptr_array_add (firmware->priv->packages_found, item);
+                        g_object_unref (item);
+                }
+        }
+
+        /* nothing to do */
+        if (firmware->priv->packages_found->len == 0) {
+                g_debug ("no packages providing any of the missing firmware");
+                goto out;
+        }
+
+        /* check we don't want the same package more than once */
+        remove_duplicate (firmware->priv->packages_found);
+
+        /* have we got any models to array */
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                if (req->model != NULL) {
+                        has_data = TRUE;
+                        break;
+                }
+        }
+
+        /* TRANSLATORS: we need another package to keep udev quiet */
+        g_string_append (string, _("Additional firmware is required to make hardware in this computer function correctly."));
+
+        /* sdd what information we have */
+        if (has_data) {
+                g_string_append (string, "\n");
+                for (i=0; i<array->len; i++) {
+                        req = g_ptr_array_index (array, i);
+                        if (req->model != NULL)
+                                g_string_append_printf (string, "\nâ?¢ %s", req->model);
+                }
+                g_string_append (string, "\n");
+        }
+
+        /* TRANSLATORS: title of libnotify bubble */
+        notification = notify_notification_new (_("Additional firmware required"), string->str, NULL);
+        notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER);
+        notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW);
+        notify_notification_add_action (notification, "install-firmware",
+                                        /* TRANSLATORS: button label */
+                                        _("Install firmware"), libnotify_cb, firmware, NULL);
+        notify_notification_add_action (notification, "ignore-devices",
+                                        /* TRANSLATORS: we should ignore this device and not ask anymore */
+                                        _("Ignore devices"), libnotify_cb, firmware, NULL);
+        ret = notify_notification_show (notification, &error);
+        if (!ret) {
+                g_warning ("error: %s", error->message);
+                g_error_free (error);
+        }
+
+out:
+        g_string_free (string, TRUE);
+        /* never repeat */
+        return FALSE;
+}
+
+static void
+remove_banned (GsdUpdatesFirmware *firmware, GPtrArray *array)
+{
+        gboolean ret;
+        gchar **banned = NULL;
+        gchar *banned_str;
+        GsdUpdatesFirmwareRequest *req;
+        guint i, j;
+
+        /* get from settings */
+        banned_str = g_settings_get_string (firmware->priv->settings,
+                                            GSD_SETTINGS_BANNED_FIRMWARE);
+        if (banned_str == NULL) {
+                g_warning ("could not read banned list");
+                goto out;
+        }
+
+        /* nothing in list, common case */
+        if (banned_str[0] == '\0') {
+                g_debug ("nothing in banned list");
+                goto out;
+        }
+
+        /* split using "," */
+        banned = g_strsplit (banned_str, ",", 0);
+
+        /* remove any banned pattern matches */
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                for (j=0; banned[j] != NULL; j++) {
+                        ret = g_pattern_match_simple (banned[j], req->filename);
+                        if (ret) {
+                                g_debug ("match %s for %s, removing",
+                                         banned[j], req->filename);
+                                request_free (req);
+                                g_ptr_array_remove_index_fast (array, i);
+                                break;
+                        }
+                }
+        }
+out:
+        g_free (banned_str);
+        g_strfreev (banned);
+}
+
+static void
+remove_ignored (GsdUpdatesFirmware *firmware, GPtrArray *array)
+{
+        gboolean ret;
+        gchar **ignored = NULL;
+        gchar *ignored_str;
+        GsdUpdatesFirmwareRequest *req;
+        guint i, j;
+
+        /* get from settings */
+        ignored_str = g_settings_get_string (firmware->priv->settings,
+                                             GSD_SETTINGS_IGNORED_DEVICES);
+        if (ignored_str == NULL) {
+                g_warning ("could not read ignored list");
+                goto out;
+        }
+
+        /* nothing in list, common case */
+        if (ignored_str[0] == '\0') {
+                g_debug ("nothing in ignored list");
+                goto out;
+        }
+
+        /* split using "," */
+        ignored = g_strsplit (ignored_str, ",", 0);
+
+        /* remove any ignored pattern matches */
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                for (j=0; ignored[j] != NULL; j++) {
+                        ret = g_pattern_match_simple (ignored[j], req->id);
+                        if (ret) {
+                                g_debug ("match %s for %s, removing", ignored[j], req->id);
+                                request_free (req);
+                                g_ptr_array_remove_index_fast (array, i);
+                                break;
+                        }
+                }
+        }
+out:
+        g_free (ignored_str);
+        g_strfreev (ignored);
+}
+
+static gchar *
+udev_text_decode (const gchar *data)
+{
+        guint i;
+        guint j;
+        gchar *decode;
+
+        decode = g_strdup (data);
+        for (i = 0, j = 0; data[i] != '\0'; j++) {
+                if (memcmp (&data[i], "\\x2f", 4) == 0) {
+                        decode[j] = '/';
+                        i += 4;
+                } else if (memcmp (&data[i], "\\x5c", 4) == 0) {
+                        decode[j] = '\\';
+                        i += 4;
+                } else {
+                        decode[j] = data[i];
+                        i++;
+                }
+        }
+        decode[j] = '\0';
+        return decode;
+}
+
+static gchar *
+get_device (GsdUpdatesFirmware *firmware, const gchar *filename)
+{
+        GFile *file;
+        GFileInfo *info;
+        const gchar *symlink_path;
+        guint len;
+        gchar *syspath = NULL;
+        gchar **split = NULL;
+        GError *error = NULL;
+        gchar *target = NULL;
+        guint i;
+
+        /* get the file data */
+        file = g_file_new_for_path (filename);
+        info = g_file_query_info (file,
+                                  G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
+                                  G_FILE_QUERY_INFO_NONE,
+                                  NULL,
+                                  &error);
+        if (info == NULL) {
+                g_warning ("Failed to get symlink: %s",
+                           error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* /devices/pci0000:00/0000:00:1d.0/usb5/5-2/firmware/5-2 */
+        symlink_path = g_file_info_get_symlink_target (info);
+        if (symlink_path == NULL) {
+                g_warning ("failed to get symlink target");
+                goto out;
+        }
+
+        /* prepend sys to make '/sys/devices/pci0000:00/0000:00:1d.0/usb5/5-2/firmware/5-2' */
+        syspath = g_strjoin (NULL, "/sys", symlink_path, NULL);
+
+        /* now find device without the junk */
+        split = g_strsplit (syspath, "/", -1);
+        len = g_strv_length (split);
+
+        /* start with the longest, and try to find a path that exists */
+        for (i=len; i>1; i--) {
+                split[i] = NULL;
+                target = g_strjoinv ("/", split);
+                g_debug ("testing %s", target);
+                if (g_file_test (target, G_FILE_TEST_EXISTS))
+                        goto out;
+                g_free (target);
+        }
+
+        /* ensure we return error if nothing found */
+        target = NULL;
+out:
+        if (info != NULL)
+                g_object_unref (info);
+        g_object_unref (file);
+        g_free (syspath);
+        g_strfreev (split);
+        return target;
+}
+
+static void
+add_filename (GsdUpdatesFirmware *firmware, const gchar *filename_no_path)
+{
+        gboolean ret;
+        gchar *filename_path = NULL;
+        gchar *missing_path = NULL;
+        gchar *sysfs_path = NULL;
+        GsdUpdatesFirmwareRequest *req;
+        GPtrArray *array;
+        guint i;
+
+        /* this is the file we want to load */
+        filename_path = g_build_filename (GSD_UPDATES_FIRMWARE_LOADING_DIR,
+                                          filename_no_path, NULL);
+
+        /* file already exists */
+        ret = g_file_test (filename_path, G_FILE_TEST_EXISTS);
+        if (ret)
+                goto out;
+
+        /* this is the file that udev created for us */
+        missing_path = g_build_filename (GSD_UPDATES_FIRMWARE_MISSING_DIR,
+                                         filename_no_path, NULL);
+        g_debug ("filename=%s -> %s", missing_path, filename_path);
+
+        /* get symlink target */
+        sysfs_path = get_device (firmware, missing_path);
+        if (sysfs_path == NULL)
+                goto out;
+
+        /* find any previous requests with this path or firmware */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                if (g_strcmp0 (sysfs_path, req->sysfs_path) == 0) {
+                        g_debug ("ignoring previous sysfs request for %s",
+                                 sysfs_path);
+                        goto out;
+                }
+                if (g_strcmp0 (filename_path, req->filename) == 0) {
+                        g_debug ("ignoring previous filename request for %s",
+                                 filename_path);
+                        goto out;
+                }
+        }
+
+        /* create new request object */
+        req = request_new (filename_path, sysfs_path);
+        g_ptr_array_add (firmware->priv->array_requested, req);
+out:
+        g_free (missing_path);
+        g_free (filename_path);
+        g_free (sysfs_path);
+}
+
+static void
+scan_directory (GsdUpdatesFirmware *firmware)
+{
+        gboolean ret;
+        GError *error = NULL;
+        GDir *dir;
+        const gchar *filename;
+        gchar *filename_decoded;
+        guint i;
+        GPtrArray *array;
+        const GsdUpdatesFirmwareRequest *req;
+        guint scan_id = 0;
+
+        /* should we check and show the user */
+        ret = g_settings_get_boolean (firmware->priv->settings,
+                                      GSD_SETTINGS_ENABLE_CHECK_FIRMWARE);
+        if (!ret) {
+                g_debug ("not showing thanks to GSettings");
+                return;
+        }
+
+        /* open the directory of requests */
+        dir = g_dir_open (GSD_UPDATES_FIRMWARE_MISSING_DIR, 0, &error);
+        if (dir == NULL) {
+                g_warning ("failed to open directory: %s",
+                           error->message);
+                g_error_free (error);
+                return;
+        }
+
+        /* find all the firmware requests */
+        filename = g_dir_read_name (dir);
+        while (filename != NULL) {
+
+                filename_decoded = udev_text_decode (filename);
+                add_filename (firmware, filename_decoded);
+                g_free (filename_decoded);
+
+                /* next file */
+                filename = g_dir_read_name (dir);
+        }
+        g_dir_close (dir);
+
+        /* debugging */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                g_debug ("requested: %s", req->filename);
+        }
+
+        /* remove banned files */
+        remove_banned (firmware, array);
+
+        /* remove ignored devices */
+        remove_ignored (firmware, array);
+
+        /* debugging */
+        array = firmware->priv->array_requested;
+        for (i=0; i<array->len; i++) {
+                req = g_ptr_array_index (array, i);
+                g_debug ("searching for: %s", req->filename);
+        }
+
+        /* don't spam the user at startup, so wait a little delay */
+        if (array->len > 0) {
+                scan_id = g_timeout_add_seconds (GSD_UPDATES_FIRMWARE_PROCESS_DELAY,
+                                                 delay_timeout_cb,
+                                                 firmware);
+                g_source_set_name_by_id (scan_id, "[GsdUpdatesFirmware] process");
+        }
+}
+
+static gboolean
+scan_directory_cb (GsdUpdatesFirmware *firmware)
+{
+        scan_directory (firmware);
+        firmware->priv->timeout_id = 0;
+        return FALSE;
+}
+
+static void
+monitor_changed_cb (GFileMonitor *monitor,
+                    GFile *file,
+                    GFile *other_file,
+                    GFileMonitorEvent event_type,
+                    GsdUpdatesFirmware *firmware)
+{
+        if (firmware->priv->timeout_id > 0) {
+                g_debug ("clearing timeout as device changed");
+                g_source_remove (firmware->priv->timeout_id);
+        }
+
+        /* wait for the device to settle */
+        firmware->priv->timeout_id =
+                g_timeout_add_seconds (GSD_UPDATES_FIRMWARE_INSERT_DELAY,
+                                       (GSourceFunc) scan_directory_cb,
+                                       firmware);
+        g_source_set_name_by_id (firmware->priv->timeout_id,
+                                 "[GsdUpdatesFirmware] changed");
+}
+
+static void
+gsd_updates_firmware_class_init (GsdUpdatesFirmwareClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+        object_class->finalize = gsd_updates_firmware_finalize;
+        g_type_class_add_private (klass, sizeof (GsdUpdatesFirmwarePrivate));
+}
+
+static void
+gsd_updates_firmware_init (GsdUpdatesFirmware *firmware)
+{
+        GFile *file;
+        GError *error = NULL;
+
+        firmware->priv = GSD_UPDATES_FIRMWARE_GET_PRIVATE (firmware);
+        firmware->priv->timeout_id = 0;
+        firmware->priv->packages_found = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+        firmware->priv->array_requested = g_ptr_array_new_with_free_func ((GDestroyNotify) request_free);
+        firmware->priv->settings = g_settings_new (GSD_SETTINGS_SCHEMA);
+        firmware->priv->task = pk_task_new ();
+        g_object_set (firmware->priv->task,
+                      "background", TRUE,
+                      NULL);
+
+        /* setup watch for new hardware */
+        file = g_file_new_for_path (GSD_UPDATES_FIRMWARE_MISSING_DIR);
+        firmware->priv->monitor = g_file_monitor (file,
+                                                  G_FILE_MONITOR_NONE,
+                                                  NULL,
+                                                  &error);
+        if (firmware->priv->monitor == NULL) {
+                g_warning ("failed to setup monitor: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* limit to one per second */
+        g_file_monitor_set_rate_limit (firmware->priv->monitor, 1000);
+
+        /* get notified of changes */
+        g_signal_connect (firmware->priv->monitor, "changed",
+                          G_CALLBACK (monitor_changed_cb), firmware);
+out:
+        g_object_unref (file);
+        firmware->priv->timeout_id =
+                g_timeout_add_seconds (GSD_UPDATES_FIRMWARE_LOGIN_DELAY,
+                                       (GSourceFunc) scan_directory_cb,
+                                       firmware);
+        g_source_set_name_by_id (firmware->priv->timeout_id,
+                                 "[GsdUpdatesFirmware] login coldplug");
+}
+
+static void
+gsd_updates_firmware_finalize (GObject *object)
+{
+        GsdUpdatesFirmware *firmware;
+
+        g_return_if_fail (GSD_UPDATES_IS_FIRMWARE (object));
+
+        firmware = GSD_UPDATES_FIRMWARE (object);
+
+        g_return_if_fail (firmware->priv != NULL);
+        g_ptr_array_unref (firmware->priv->array_requested);
+        g_ptr_array_unref (firmware->priv->packages_found);
+        g_object_unref (PK_CLIENT(firmware->priv->task));
+        g_object_unref (firmware->priv->settings);
+        if (firmware->priv->monitor != NULL)
+                g_object_unref (firmware->priv->monitor);
+        if (firmware->priv->timeout_id > 0)
+                g_source_remove (firmware->priv->timeout_id);
+
+        G_OBJECT_CLASS (gsd_updates_firmware_parent_class)->finalize (object);
+}
+
+GsdUpdatesFirmware *
+gsd_updates_firmware_new (void)
+{
+        GsdUpdatesFirmware *firmware;
+        firmware = g_object_new (GSD_UPDATES_TYPE_FIRMWARE, NULL);
+        return GSD_UPDATES_FIRMWARE (firmware);
+}
diff --git a/plugins/updates/gsd-updates-firmware.h b/plugins/updates/gsd-updates-firmware.h
new file mode 100644
index 0000000..07092f4
--- /dev/null
+++ b/plugins/updates/gsd-updates-firmware.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2011 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GSD_UPDATES_FIRMWARE_H
+#define __GSD_UPDATES_FIRMWARE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GSD_UPDATES_TYPE_FIRMWARE               (gsd_updates_firmware_get_type ())
+#define GSD_UPDATES_FIRMWARE(o)                 (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_UPDATES_TYPE_FIRMWARE, GsdUpdatesFirmware))
+#define GSD_UPDATES_FIRMWARE_CLASS(k)           (G_TYPE_CHECK_CLASS_CAST((k), GSD_UPDATES_TYPE_FIRMWARE, GsdUpdatesFirmwareClass))
+#define GSD_UPDATES_IS_FIRMWARE(o)              (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_UPDATES_TYPE_FIRMWARE))
+
+typedef struct GsdUpdatesFirmwarePrivate GsdUpdatesFirmwarePrivate;
+
+typedef struct
+{
+         GObject                         parent;
+         GsdUpdatesFirmwarePrivate      *priv;
+} GsdUpdatesFirmware;
+
+typedef struct
+{
+        GObjectClass    parent_class;
+} GsdUpdatesFirmwareClass;
+
+GType                    gsd_updates_firmware_get_type          (void);
+GsdUpdatesFirmware      *gsd_updates_firmware_new               (void);
+
+G_END_DECLS
+
+#endif /* __GSD_UPDATES_FIRMWARE_H */
diff --git a/plugins/updates/gsd-updates-manager.c b/plugins/updates/gsd-updates-manager.c
index 038c3ea..3531126 100644
--- a/plugins/updates/gsd-updates-manager.c
+++ b/plugins/updates/gsd-updates-manager.c
@@ -30,7 +30,9 @@
 
 #include "gsd-enums.h"
 #include "gsd-updates-manager.h"
+#include "gsd-updates-firmware.h"
 #include "gsd-updates-refresh.h"
+#include "gsd-updates-common.h"
 #include "gnome-settings-profile.h"
 
 #define GSD_UPDATES_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_UPDATES_MANAGER, GsdUpdatesManagerPrivate))
@@ -39,6 +41,7 @@ struct GsdUpdatesManagerPrivate
 {
         GCancellable            *cancellable;
         GsdUpdatesRefresh       *refresh;
+        GsdUpdatesFirmware      *firmware;
         GSettings               *settings_ftp;
         GSettings               *settings_gsd;
         GSettings               *settings_http;
@@ -911,6 +914,9 @@ gsd_updates_manager_start (GsdUpdatesManager *manager,
                       "interactive", FALSE,
                       NULL);
 
+        /* watch UDev for missing firmware */
+        manager->priv->firmware = gsd_updates_firmware_new ();
+
         /* get automatic callbacks about when we should check for
          * updates, refresh-caches and upgrades */
         manager->priv->refresh = gsd_updates_refresh_new ();
@@ -974,6 +980,10 @@ gsd_updates_manager_stop (GsdUpdatesManager *manager)
                 g_object_unref (manager->priv->refresh);
                 manager->priv->refresh = NULL;
         }
+        if (manager->priv->firmware != NULL) {
+                g_object_unref (manager->priv->firmware);
+                manager->priv->firmware = NULL;
+        }
         if (manager->priv->cancellable != NULL) {
                 g_object_unref (manager->priv->cancellable);
                 manager->priv->cancellable = NULL;
diff --git a/plugins/updates/gsd-updates-refresh.c b/plugins/updates/gsd-updates-refresh.c
index 9571df5..9193358 100644
--- a/plugins/updates/gsd-updates-refresh.c
+++ b/plugins/updates/gsd-updates-refresh.c
@@ -25,6 +25,7 @@
 #include <packagekit-glib2/packagekit.h>
 #include <libupower-glib/upower.h>
 
+#include "gsd-updates-common.h"
 #include "gsd-updates-refresh.h"
 
 static void     gsd_updates_refresh_finalize    (GObject            *object);
diff --git a/plugins/updates/gsd-updates-refresh.h b/plugins/updates/gsd-updates-refresh.h
index ad45dfe..f5e7158 100644
--- a/plugins/updates/gsd-updates-refresh.h
+++ b/plugins/updates/gsd-updates-refresh.h
@@ -44,22 +44,6 @@ typedef struct
         GObjectClass    parent_class;
 } GsdUpdatesRefreshClass;
 
-#define GSD_SETTINGS_SCHEMA                             "org.gnome.settings-daemon.plugins.updates"
-#define GSD_SETTINGS_AUTO_UPDATE_TYPE                   "auto-update-type"
-#define GSD_SETTINGS_CONNECTION_USE_MOBILE              "connection-use-mobile"
-#define GSD_SETTINGS_CONNECTION_USE_WIFI                "connection-use-wifi"
-#define GSD_SETTINGS_FORCE_GET_UPDATES_LOGIN            "force-get-updates-login"
-#define GSD_SETTINGS_FREQUENCY_GET_UPDATES              "frequency-get-updates"
-#define GSD_SETTINGS_FREQUENCY_GET_UPGRADES             "frequency-get-upgrades"
-#define GSD_SETTINGS_FREQUENCY_REFRESH_CACHE            "frequency-refresh-cache"
-#define GSD_SETTINGS_NOTIFY_UPDATE_TYPE                 "notify-update-type"
-#define GSD_SETTINGS_NOTIFY_DISTRO_UPGRADES             "notify-distro-upgrades"
-#define GSD_SETTINGS_NOTIFY_UPDATE_COMPLETE             "notify-update-complete"
-#define GSD_SETTINGS_NOTIFY_UPDATE_COMPLETE_RESTART     "notify-update-complete-restart"
-#define GSD_SETTINGS_NOTIFY_UPDATE_NOT_BATTERY          "notify-update-not-battery"
-#define GSD_SETTINGS_SESSION_STARTUP_TIMEOUT            "session-startup-timeout"
-#define GSD_SETTINGS_UPDATE_BATTERY                     "update-battery"
-
 GType                    gsd_updates_refresh_get_type           (void);
 GsdUpdatesRefresh       *gsd_updates_refresh_new                (void);
 gboolean                 gsd_updates_refresh_get_on_battery     (GsdUpdatesRefresh *refresh);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4d18a83..f213214 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -47,3 +47,4 @@ plugins/smartcard/gsd-smartcard-manager.c
 plugins/smartcard/gsd-smartcard.c
 plugins/datetime/org.gnome.settingsdaemon.datetimemechanism.policy.in
 plugins/updates/gsd-updates-manager.c
+plugins/updates/gsd-updates-firmware.c



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]