[gnome-software] Add a plugin to match a hardware modalias



commit 0c72785a5c039b8afbe2846245787001c7f720b1
Author: Richard Hughes <richard hughsie com>
Date:   Fri Sep 2 18:21:21 2016 +0100

    Add a plugin to match a hardware modalias
    
    This allows us to show AppStream components that match installed hardware.

 configure.ac                      |    1 +
 contrib/gnome-software.spec.in    |    2 +
 src/gs-app.h                      |    4 +
 src/gs-self-test.c                |   38 +++++++++
 src/plugins/Makefile.am           |    7 ++
 src/plugins/gs-plugin-appstream.c |    9 ++
 src/plugins/gs-plugin-dummy.c     |    2 +
 src/plugins/gs-plugin-modalias.c  |  151 +++++++++++++++++++++++++++++++++++++
 8 files changed, 214 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 9222ed9..ccedf3f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -91,6 +91,7 @@ PKG_CHECK_MODULES(SQLITE, sqlite3)
 PKG_CHECK_MODULES(SOUP, libsoup-2.4 >= 2.51.92)
 PKG_CHECK_MODULES(GSETTINGS_DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.11.5)
 PKG_CHECK_MODULES(LIBSECRET, libsecret-1)
+PKG_CHECK_MODULES(GUDEV, gudev-1.0)
 AC_PATH_PROG(APPSTREAM_UTIL, [appstream-util], [unfound])
 
 # use -lm
diff --git a/contrib/gnome-software.spec.in b/contrib/gnome-software.spec.in
index 468f70f..d9c61b9 100644
--- a/contrib/gnome-software.spec.in
+++ b/contrib/gnome-software.spec.in
@@ -46,6 +46,7 @@ BuildRequires: libsecret-devel
 BuildRequires: flatpak-devel >= %{flatpak_version}
 BuildRequires: ostree-devel
 BuildRequires: rpm-devel
+BuildRequires: libgudev1-devel
 
 Requires: appstream-data
 %if 0%{?fedora}
@@ -163,6 +164,7 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
 %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-popular.so
 %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_icons.so
 %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_key-colors.so
+%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_modalias.so
 %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_odrs.so
 %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_ostree.so
 %if %{with packagekit}
diff --git a/src/gs-app.h b/src/gs-app.h
index a19d766..cff257e 100644
--- a/src/gs-app.h
+++ b/src/gs-app.h
@@ -101,6 +101,10 @@ typedef enum {
 #define AS_APP_QUIRK_NEEDS_USER_ACTION         (1 << 8)
 #endif
 
+#if !AS_CHECK_VERSION(0,6,3)
+#define AS_APP_KIND_DRIVER                     (AS_APP_KIND_LOCALIZATION+1)
+#endif
+
 GsApp          *gs_app_new                     (const gchar    *id);
 gchar          *gs_app_to_string               (GsApp          *app);
 
diff --git a/src/gs-self-test.c b/src/gs-self-test.c
index 26f9740..0929650 100644
--- a/src/gs-self-test.c
+++ b/src/gs-self-test.c
@@ -584,6 +584,32 @@ gs_plugin_loader_search_func (GsPluginLoader *plugin_loader)
 }
 
 static void
+gs_plugin_loader_modalias_func (GsPluginLoader *plugin_loader)
+{
+       GsApp *app;
+       g_autofree gchar *menu_path = NULL;
+       g_autoptr(GError) error = NULL;
+       g_autoptr(GsAppList) list = NULL;
+
+       /* get search result based on addon keyword */
+       list = gs_plugin_loader_search (plugin_loader,
+                                       "colorhug2",
+                                       GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+                                       NULL,
+                                       &error);
+       g_assert_no_error (error);
+       g_assert (list != NULL);
+
+       /* make sure there is one entry, the parent app */
+       g_assert_cmpint (gs_app_list_length (list), ==, 1);
+       app = gs_app_list_index (list, 0);
+       g_assert_cmpstr (gs_app_get_id (app), ==, "com.hughski.ColorHug2.driver");
+       g_assert_cmpint (gs_app_get_kind (app), ==, AS_APP_KIND_DRIVER);
+       g_assert (gs_app_has_category (app, "Addons"));
+       g_assert (gs_app_has_category (app, "Drivers"));
+}
+
+static void
 gs_plugin_loader_webapps_func (GsPluginLoader *plugin_loader)
 {
        gboolean ret;
@@ -1175,6 +1201,7 @@ main (int argc, char **argv)
                "desktop-menu-path",
                "icons",
                "key-colors",
+               "modalias",
                "provenance",
                "provenance-license",
                "packagekit-local",
@@ -1210,6 +1237,14 @@ main (int argc, char **argv)
        g_assert (fn != NULL);
        xml = g_strdup_printf ("<?xml version=\"1.0\"?>\n"
                "<components version=\"0.9\">\n"
+               "  <component type=\"driver\">\n"
+               "    <id>com.hughski.ColorHug2.driver</id>\n"
+               "    <name>ColorHug2</name>\n"
+               "    <summary>ColorHug2 Colorimeter Driver</summary>\n"
+               "    <provides>\n"
+               "      <modalias>pci:*</modalias>\n"
+               "    </provides>\n"
+               "  </component>\n"
                "  <component type=\"desktop\">\n"
                "    <id>chiron.desktop</id>\n"
                "    <pkgname>chiron</pkgname>\n"
@@ -1336,6 +1371,9 @@ main (int argc, char **argv)
        g_test_add_data_func ("/gnome-software/plugin-loader{webapps}",
                              plugin_loader,
                              (GTestDataFunc) gs_plugin_loader_webapps_func);
+       g_test_add_data_func ("/gnome-software/plugin-loader{modalias}",
+                             plugin_loader,
+                             (GTestDataFunc) gs_plugin_loader_modalias_func);
        g_test_add_data_func ("/gnome-software/plugin-loader{search}",
                              plugin_loader,
                              (GTestDataFunc) gs_plugin_loader_search_func);
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index f021cae..e2c3c44 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -12,6 +12,7 @@ AM_CPPFLAGS =                                         \
        $(FWUPD_CFLAGS)                                 \
        $(JSON_GLIB_CFLAGS)                             \
        $(LIMBA_CFLAGS)                                 \
+       $(GUDEV_CFLAGS)                                 \
        $(OSTREE_CFLAGS)                                \
        $(FLATPAK_CFLAGS)                               \
        $(RPM_CFLAGS)                                   \
@@ -39,6 +40,7 @@ plugin_LTLIBRARIES =                                  \
        libgs_plugin_hardcoded-popular.la               \
        libgs_plugin_hardcoded-featured.la              \
        libgs_plugin_fedora-distro-upgrades.la          \
+       libgs_plugin_modalias.la                        \
        libgs_plugin_provenance.la                      \
        libgs_plugin_provenance-license.la              \
        libgs_plugin_repos.la                           \
@@ -129,6 +131,11 @@ libgs_plugin_fedora_distro_upgrades_la_LIBADD = $(GS_PLUGIN_LIBS) $(JSON_GLIB_LI
 libgs_plugin_fedora_distro_upgrades_la_LDFLAGS = -module -avoid-version
 libgs_plugin_fedora_distro_upgrades_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(JSON_GLIB_CFLAGS) $(WARN_CFLAGS)
 
+libgs_plugin_modalias_la_SOURCES = gs-plugin-modalias.c
+libgs_plugin_modalias_la_LIBADD = $(GS_PLUGIN_LIBS) $(GUDEV_LIBS)
+libgs_plugin_modalias_la_LDFLAGS = -module -avoid-version
+libgs_plugin_modalias_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
+
 libgs_plugin_provenance_la_SOURCES = gs-plugin-provenance.c
 libgs_plugin_provenance_la_LIBADD = $(GS_PLUGIN_LIBS)
 libgs_plugin_provenance_la_LDFLAGS = -module -avoid-version
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index 40cb544..e1d6490 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -314,6 +314,15 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
                }
        }
 
+       /* fix up drivers with our nonstandard groups */
+       for (i = 0; i < items->len; i++) {
+               app = g_ptr_array_index (items, i);
+               if (as_app_get_kind (app) == AS_APP_KIND_DRIVER) {
+                       as_app_add_category (app, "Addons");
+                       as_app_add_category (app, "Drivers");
+               }
+       }
+
        /* rely on the store keeping itself updated */
        return TRUE;
 }
diff --git a/src/plugins/gs-plugin-dummy.c b/src/plugins/gs-plugin-dummy.c
index 7e5ad95..d2ded6f 100644
--- a/src/plugins/gs-plugin-dummy.c
+++ b/src/plugins/gs-plugin-dummy.c
@@ -79,6 +79,7 @@ gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app)
        if (g_strcmp0 (gs_app_get_id (app), "mate-spell.desktop") == 0 ||
            g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0 ||
            g_strcmp0 (gs_app_get_id (app), "zeus.desktop") == 0 ||
+           g_strcmp0 (gs_app_get_id (app), "com.hughski.ColorHug2.driver") == 0 ||
            g_strcmp0 (gs_app_get_id (app), "zeus-spell.addon") == 0 ||
            g_strcmp0 (gs_app_get_source_default (app), "chiron") == 0)
                gs_app_set_management_plugin (app, gs_plugin_get_name (plugin));
@@ -388,6 +389,7 @@ gs_plugin_refine_app (GsPlugin *plugin,
        /* default */
        if (g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0 ||
            g_strcmp0 (gs_app_get_id (app), "mate-spell.desktop") == 0 ||
+           g_strcmp0 (gs_app_get_id (app), "com.hughski.ColorHug2.driver") == 0 ||
            g_strcmp0 (gs_app_get_id (app), "zeus.desktop") == 0) {
                if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN)
                        gs_app_set_state (app, AS_APP_STATE_INSTALLED);
diff --git a/src/plugins/gs-plugin-modalias.c b/src/plugins/gs-plugin-modalias.c
new file mode 100644
index 0000000..11d7edc
--- /dev/null
+++ b/src/plugins/gs-plugin-modalias.c
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 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 <fnmatch.h>
+#include <gudev/gudev.h>
+
+#include <gnome-software.h>
+
+struct GsPluginData {
+       GUdevClient             *client;
+       GPtrArray               *devices;
+};
+
+static void
+gs_plugin_modalias_uevent_cb (GUdevClient *client,
+                             const gchar *action,
+                             GUdevDevice *device,
+                             GsPlugin *plugin)
+{
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+       if (g_strcmp0 (action, "add") == 0 ||
+           g_strcmp0 (action, "remove") == 0) {
+               g_debug ("invalidating devices as '%s' sent action '%s'",
+                        g_udev_device_get_sysfs_path (device),
+                        action);
+               g_ptr_array_set_size (priv->devices, 0);
+       }
+}
+
+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, "appstream");
+       gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_BEFORE, "icons");
+       priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+       priv->client = g_udev_client_new (NULL);
+       g_signal_connect (priv->client, "uevent",
+                         G_CALLBACK (gs_plugin_modalias_uevent_cb), plugin);
+}
+
+void
+gs_plugin_destroy (GsPlugin *plugin)
+{
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+       g_object_unref (priv->client);
+       g_ptr_array_unref (priv->devices);
+}
+
+static void
+gs_plugin_modalias_ensure_devices (GsPlugin *plugin)
+{
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+       GList *l;
+       g_autoptr(GList) list = NULL;
+
+       /* already set */
+       if (priv->devices->len > 0)
+               return;
+
+       /* get the devices, and assume ownership of each */
+       list = g_udev_client_query_by_subsystem (priv->client, NULL);
+       for (l = list; l != NULL; l = l->next) {
+               GUdevDevice *device = G_UDEV_DEVICE (l->data);
+               if (g_udev_device_get_sysfs_attr (device, "modalias") == NULL) {
+                       g_object_unref (device);
+                       continue;
+               }
+               g_ptr_array_add (priv->devices, device);
+       }
+       g_debug ("%u devices with modalias", priv->devices->len);
+}
+
+static gboolean
+gs_plugin_modalias_matches (GsPlugin *plugin, const gchar *modalias)
+{
+       GsPluginData *priv = gs_plugin_get_data (plugin);
+       guint i;
+
+       gs_plugin_modalias_ensure_devices (plugin);
+       for (i = 0; i < priv->devices->len; i++) {
+               GUdevDevice *device = g_ptr_array_index (priv->devices, i);
+               const gchar *modalias_tmp;
+
+               /* get the (optional) device modalias */
+               modalias_tmp = g_udev_device_get_sysfs_attr (device, "modalias");
+               if (modalias_tmp == NULL)
+                       continue;
+               if (fnmatch (modalias, modalias_tmp, 0) == 0) {
+                       g_debug ("matched %s against %s", modalias_tmp, modalias);
+                       return TRUE;
+               }
+       }
+       g_debug ("no match for %s", modalias);
+       return FALSE;
+}
+
+gboolean
+gs_plugin_refine_app (GsPlugin *plugin,
+                     GsApp *app,
+                     GsPluginRefineFlags flags,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+       GPtrArray *provides;
+       guint i;
+
+       /* not required */
+       if (gs_app_get_icons(app)->len > 0)
+               return TRUE;
+       if (gs_app_get_kind (app) != AS_APP_KIND_DRIVER)
+               return TRUE;
+
+       /* do any of the modaliases match any installed hardware */
+       provides = gs_app_get_provides (app);
+       for (i = 0 ; i < provides->len; i++) {
+               AsProvide *prov = g_ptr_array_index (provides, i);
+               if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_MODALIAS)
+                       continue;
+               if (gs_plugin_modalias_matches (plugin, as_provide_get_value (prov))) {
+                       g_autoptr(AsIcon) ic = NULL;
+                       ic = as_icon_new ();
+                       as_icon_set_kind (ic, AS_ICON_KIND_STOCK);
+                       as_icon_set_name (ic, "emblem-system-symbolic");
+                       gs_app_add_icon (app, ic);
+                       gs_app_add_quirk (app, AS_APP_QUIRK_NOT_LAUNCHABLE);
+                       break;
+               }
+       }
+       return TRUE;
+}


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