Move the database of supported devices out into runtime loaded files

When fwupd is installed in long-term support distros it's very hard to backport
new versions as new hardware is released.

There are several reasons why we can't just include the mapping and quirk
information in the AppStream metadata:

 * The extra data is hugely specific to the installed fwupd plugin versions
 * The device-id is per-device, and the mapping is usually per-plugin
 * Often the information is needed before the FuDevice is created
 * There are security implications in allowing plugins to handle new devices

The idea with quirks is that the end user can drop an additional (or replace
an existing) file in a .d director with a simple format and the hardware will
magically start working. This assumes no new quirks are required, as this would
obviously need code changes, but allows us to get most existing devices working
in an easy way without the user compiling anything.

This allows us to fix issues like https://github.com/hughsie/fwupd/issues/265
This commit is contained in:
Richard Hughes 2017-10-28 21:14:28 +01:00
parent 78c1e6c009
commit 9c028f06b5
12 changed files with 557 additions and 1 deletions

View File

@ -208,6 +208,8 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg
%{_unitdir}/fwupd.service %{_unitdir}/fwupd.service
%{_unitdir}/system-update.target.wants/ %{_unitdir}/system-update.target.wants/
%dir %{_localstatedir}/lib/fwupd %dir %{_localstatedir}/lib/fwupd
%dir %{_datadir}/fwupd/quirks.d
%{_datadir}/fwupd/quirks.d/*.quirk
%{_localstatedir}/lib/fwupd/builder/README.md %{_localstatedir}/lib/fwupd/builder/README.md
%{_libdir}/libfwupd*.so.* %{_libdir}/libfwupd*.so.*
%{_libdir}/girepository-1.0/Fwupd-2.0.typelib %{_libdir}/girepository-1.0/Fwupd-2.0.typelib

View File

@ -0,0 +1,12 @@
[fwupd-plugin-test]
USB\VID_0A5C&PID_6412=ignore-runtime
# this is an empty key
USB\VID_FFFF&PID_FFFF=
# this is a key with a space
ACME Inc.=awesome
# this is a wildcard
CORP*=town

View File

@ -45,6 +45,7 @@
<xi:include href="xml/fu-device.xml"/> <xi:include href="xml/fu-device.xml"/>
<xi:include href="xml/fu-device-locker.xml"/> <xi:include href="xml/fu-device-locker.xml"/>
<xi:include href="xml/fu-common.xml"/> <xi:include href="xml/fu-common.xml"/>
<xi:include href="xml/fu-quirks.xml"/>
<xi:include href="xml/fu-device-metadata.xml"/> <xi:include href="xml/fu-device-metadata.xml"/>
</reference> </reference>

View File

@ -1,5 +1,6 @@
fwupd_client_get_type fwupd_client_get_type
fwupd_device_get_type fwupd_device_get_type
fwupd_quirks_get_type
fwupd_release_get_type fwupd_release_get_type
fwupd_remote_get_type fwupd_remote_get_type
fwupd_result_get_type fwupd_result_get_type

View File

@ -75,6 +75,7 @@ struct _FuEngine
GPtrArray *supported_guids; GPtrArray *supported_guids;
FuSmbios *smbios; FuSmbios *smbios;
FuHwids *hwids; FuHwids *hwids;
FuQuirks *quirks;
}; };
enum { enum {
@ -2662,6 +2663,7 @@ fu_engine_load_plugins (FuEngine *self, GError **error)
fu_plugin_set_hwids (plugin, self->hwids); fu_plugin_set_hwids (plugin, self->hwids);
fu_plugin_set_smbios (plugin, self->smbios); fu_plugin_set_smbios (plugin, self->smbios);
fu_plugin_set_supported (plugin, self->supported_guids); fu_plugin_set_supported (plugin, self->supported_guids);
fu_plugin_set_quirks (plugin, self->quirks);
g_debug ("adding plugin %s", filename); g_debug ("adding plugin %s", filename);
if (!fu_plugin_open (plugin, filename, &error_local)) { if (!fu_plugin_open (plugin, filename, &error_local)) {
g_warning ("failed to open plugin %s: %s", g_warning ("failed to open plugin %s: %s",
@ -2867,6 +2869,7 @@ fu_engine_cleanup_state (GError **error)
gboolean gboolean
fu_engine_load (FuEngine *self, GError **error) fu_engine_load (FuEngine *self, GError **error)
{ {
g_autoptr(GError) error_quirks = NULL;
g_autoptr(GError) error_hwids = NULL; g_autoptr(GError) error_hwids = NULL;
g_autoptr(GError) error_smbios = NULL; g_autoptr(GError) error_smbios = NULL;
@ -2879,6 +2882,10 @@ fu_engine_load (FuEngine *self, GError **error)
return FALSE; return FALSE;
} }
/* load the quirk files */
if (!fu_quirks_load (self->quirks, &error_quirks))
g_warning ("Failed to load quirks: %s", error_quirks->message);
/* load AppStream metadata */ /* load AppStream metadata */
as_store_add_filter (self->store, AS_APP_KIND_FIRMWARE); as_store_add_filter (self->store, AS_APP_KIND_FIRMWARE);
if (!fu_engine_load_metadata_store (self, error)) { if (!fu_engine_load_metadata_store (self, error)) {
@ -2967,6 +2974,7 @@ fu_engine_init (FuEngine *self)
self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_engine_item_free); self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_engine_item_free);
self->smbios = fu_smbios_new (); self->smbios = fu_smbios_new ();
self->hwids = fu_hwids_new (); self->hwids = fu_hwids_new ();
self->quirks = fu_quirks_new ();
self->pending = fu_pending_new (); self->pending = fu_pending_new ();
self->profile = as_profile_new (); self->profile = as_profile_new ();
self->store = as_store_new (); self->store = as_store_new ();
@ -2989,6 +2997,7 @@ fu_engine_finalize (GObject *obj)
g_hash_table_unref (self->plugins_hash); g_hash_table_unref (self->plugins_hash);
g_object_unref (self->config); g_object_unref (self->config);
g_object_unref (self->smbios); g_object_unref (self->smbios);
g_object_unref (self->quirks);
g_object_unref (self->hwids); g_object_unref (self->hwids);
g_object_unref (self->pending); g_object_unref (self->pending);
g_object_unref (self->profile); g_object_unref (self->profile);

View File

@ -22,6 +22,7 @@
#ifndef __FU_PLUGIN_PRIVATE_H #ifndef __FU_PLUGIN_PRIVATE_H
#define __FU_PLUGIN_PRIVATE_H #define __FU_PLUGIN_PRIVATE_H
#include "fu-quirks.h"
#include "fu-plugin.h" #include "fu-plugin.h"
#include "fu-smbios.h" #include "fu-smbios.h"
@ -37,6 +38,8 @@ void fu_plugin_set_hwids (FuPlugin *plugin,
FuHwids *hwids); FuHwids *hwids);
void fu_plugin_set_supported (FuPlugin *plugin, void fu_plugin_set_supported (FuPlugin *plugin,
GPtrArray *supported_guids); GPtrArray *supported_guids);
void fu_plugin_set_quirks (FuPlugin *plugin,
FuQuirks *quirks);
void fu_plugin_set_smbios (FuPlugin *plugin, void fu_plugin_set_smbios (FuPlugin *plugin,
FuSmbios *smbios); FuSmbios *smbios);
guint fu_plugin_get_order (FuPlugin *plugin); guint fu_plugin_get_order (FuPlugin *plugin);

View File

@ -56,6 +56,7 @@ typedef struct {
GPtrArray *rules[FU_PLUGIN_RULE_LAST]; GPtrArray *rules[FU_PLUGIN_RULE_LAST];
gchar *name; gchar *name;
FuHwids *hwids; FuHwids *hwids;
FuQuirks *quirks;
GPtrArray *supported_guids; GPtrArray *supported_guids;
FuSmbios *smbios; FuSmbios *smbios;
GHashTable *devices; /* platform_id:GObject */ GHashTable *devices; /* platform_id:GObject */
@ -657,6 +658,78 @@ fu_plugin_set_supported (FuPlugin *plugin, GPtrArray *supported_guids)
priv->supported_guids = g_ptr_array_ref (supported_guids); priv->supported_guids = g_ptr_array_ref (supported_guids);
} }
void
fu_plugin_set_quirks (FuPlugin *plugin, FuQuirks *quirks)
{
FuPluginPrivate *priv = GET_PRIVATE (plugin);
g_set_object (&priv->quirks, quirks);
}
/**
* fu_plugin_get_quirks:
* @plugin: A #FuPlugin
*
* Returns the hardware database object. This can be used to discover device
* quirks or other device-specific settings.
*
* Returns: (transfer none): a #FuQuirks, or %NULL if not set
*
* Since: 1.0.1
**/
FuQuirks *
fu_plugin_get_quirks (FuPlugin *plugin)
{
FuPluginPrivate *priv = GET_PRIVATE (plugin);
return priv->quirks;
}
/**
* fu_plugin_lookup_quirk_by_id:
* @plugin: A #FuPlugin
* @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
* @id: An ID to match the entry, e.g. "012345"
*
* Looks up an entry in the hardware database using a string value.
*
* Returns: (transfer none): values from the database, or %NULL if not found
*
* Since: 1.0.1
**/
const gchar *
fu_plugin_lookup_quirk_by_id (FuPlugin *plugin, const gchar *prefix, const gchar *id)
{
FuPluginPrivate *priv = GET_PRIVATE (plugin);
g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
/* wildcard */
if (g_strstr_len (id, -1, "*") != NULL)
return fu_quirks_lookup_by_glob (priv->quirks, prefix, id);
/* exact ID */
return fu_quirks_lookup_by_id (priv->quirks, prefix, id);
}
/**
* fu_plugin_lookup_quirk_by_usb_device:
* @plugin: A #FuPlugin
* @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
* @dev: A #GUsbDevice
*
* Looks up an entry in the hardware database using various keys generated
* from @dev.
*
* Returns: (transfer none): values from the database, or %NULL if not found
*
* Since: 1.0.1
**/
const gchar *
fu_plugin_lookup_quirk_by_usb_device (FuPlugin *plugin, const gchar *prefix, GUsbDevice *dev)
{
FuPluginPrivate *priv = GET_PRIVATE (plugin);
g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
return fu_quirks_lookup_by_usb_device (priv->quirks, prefix, dev);
}
/** /**
* fu_plugin_get_supported: * fu_plugin_get_supported:
* @plugin: A #FuPlugin * @plugin: A #FuPlugin
@ -1386,6 +1459,8 @@ fu_plugin_finalize (GObject *object)
g_object_unref (priv->usb_ctx); g_object_unref (priv->usb_ctx);
if (priv->hwids != NULL) if (priv->hwids != NULL)
g_object_unref (priv->hwids); g_object_unref (priv->hwids);
if (priv->quirks != NULL)
g_object_unref (priv->quirks);
if (priv->supported_guids != NULL) if (priv->supported_guids != NULL)
g_ptr_array_unref (priv->supported_guids); g_ptr_array_unref (priv->supported_guids);
if (priv->smbios != NULL) if (priv->smbios != NULL)

View File

@ -30,6 +30,7 @@
#include "fu-common.h" #include "fu-common.h"
#include "fu-device.h" #include "fu-device.h"
#include "fu-device-locker.h" #include "fu-device-locker.h"
#include "fu-quirks.h"
#include "fu-hwids.h" #include "fu-hwids.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -135,6 +136,13 @@ GBytes *fu_plugin_get_smbios_data (FuPlugin *plugin,
void fu_plugin_add_rule (FuPlugin *plugin, void fu_plugin_add_rule (FuPlugin *plugin,
FuPluginRule rule, FuPluginRule rule,
const gchar *name); const gchar *name);
FuQuirks *fu_plugin_get_quirks (FuPlugin *plugin);
const gchar *fu_plugin_lookup_quirk_by_id (FuPlugin *plugin,
const gchar *prefix,
const gchar *id);
const gchar *fu_plugin_lookup_quirk_by_usb_device (FuPlugin *plugin,
const gchar *prefix,
GUsbDevice *dev);
G_END_DECLS G_END_DECLS

369
src/fu-quirks.c Normal file
View File

@ -0,0 +1,369 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2017 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 <glib-object.h>
#include <gio/gio.h>
#include <fnmatch.h>
#include <string.h>
#include "fu-quirks.h"
#include "fwupd-error.h"
#include "fwupd-remote-private.h"
/**
* SECTION:fu-quirks
* @short_description: device quirks
*
* Quirks can be used to modify device behaviour.
* When fwupd is installed in long-term support distros it's very hard to
* backport new versions as new hardware is released.
*
* There are several reasons why we can't just include the mapping and quirk
* information in the AppStream metadata:
*
* * The extra data is hugely specific to the installed fwupd plugin versions
* * The device-id is per-device, and the mapping is usually per-plugin
* * Often the information is needed before the FuDevice is created
* * There are security implications in allowing plugins to handle new devices
*
* The idea with quirks is that the end user can drop an additional (or replace
* an existing) file in a .d director with a simple format and the hardware will
* magically start working. This assumes no new quirks are required, as this would
* obviously need code changes, but allows us to get most existing devices working
* in an easy way without the user compiling anything.
*
* See also: #FuDevice, #FuPlugin
*/
static void fu_quirks_finalize (GObject *obj);
struct _FuQuirks
{
GObject parent_instance;
GPtrArray *monitors;
GHashTable *hash; /* of prefix/id:string */
};
G_DEFINE_TYPE (FuQuirks, fu_quirks, G_TYPE_OBJECT)
static void
fu_quirks_monitor_changed_cb (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
gpointer user_data)
{
FuQuirks *self = FU_QUIRKS (user_data);
g_autoptr(GError) error = NULL;
g_autofree gchar *filename = g_file_get_path (file);
g_debug ("%s changed, reloading all configs", filename);
if (!fu_quirks_load (self, &error))
g_warning ("failed to rescan quirks: %s", error->message);
}
static gboolean
fu_quirks_add_inotify (FuQuirks *self, const gchar *filename, GError **error)
{
GFileMonitor *monitor;
g_autoptr(GFile) file = g_file_new_for_path (filename);
/* set up a notify watch */
monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, error);
if (monitor == NULL)
return FALSE;
g_signal_connect (monitor, "changed",
G_CALLBACK (fu_quirks_monitor_changed_cb), self);
g_ptr_array_add (self->monitors, monitor);
return TRUE;
}
/**
* fu_quirks_lookup_by_id:
* @self: A #FuPlugin
* @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
* @id: An ID to match the entry, e.g. "012345"
*
* Looks up an entry in the hardware database using a string value.
*
* Returns: (transfer none): values from the database, or %NULL if not found
*
* Since: 1.0.1
**/
const gchar *
fu_quirks_lookup_by_id (FuQuirks *self, const gchar *prefix, const gchar *id)
{
g_autofree gchar *key = NULL;
g_return_val_if_fail (FU_IS_QUIRKS (self), NULL);
g_return_val_if_fail (prefix != NULL, NULL);
g_return_val_if_fail (id != NULL, NULL);
key = g_strdup_printf ("%s/%s", prefix, id);
return g_hash_table_lookup (self->hash, key);
}
/**
* fu_quirks_lookup_by_glob:
* @self: A #FuPlugin
* @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
* @glob: An glob to match the entry, e.g. "foo*bar?baz"
*
* Looks up an entry in the hardware database using a key glob.
* NOTE: This is *much* slower than using fu_quirks_lookup_by_id() as each key
* in the quirk database is compared.
*
* Returns: (transfer none): values from the database, or %NULL if not found
*
* Since: 1.0.1
**/
const gchar *
fu_quirks_lookup_by_glob (FuQuirks *self, const gchar *prefix, const gchar *glob)
{
g_autoptr(GList) keys = NULL;
gsize prefix_len;
g_return_val_if_fail (FU_IS_QUIRKS (self), NULL);
g_return_val_if_fail (prefix != NULL, NULL);
g_return_val_if_fail (glob != NULL, NULL);
prefix_len = strlen (prefix);
keys = g_hash_table_get_keys (self->hash);
for (GList *l = keys; l != NULL; l = l->next) {
const gchar *id = l->data;
if (strncmp (id, prefix, prefix_len) != 0)
continue;
id += prefix_len + 1;
if (fnmatch (glob, id, 0) == 0)
return fu_quirks_lookup_by_id (self, prefix, id);
if (fnmatch (id, glob, 0) == 0)
return fu_quirks_lookup_by_id (self, prefix, id);
}
return NULL;
}
/**
* fu_quirks_lookup_by_usb_device:
* @self: A #FuPlugin
* @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
* @dev: A #GUsbDevice
*
* Looks up an entry in the hardware database using various keys generated
* from @dev.
*
* Returns: (transfer none): values from the database, or %NULL if not found
*
* Since: 1.0.1
**/
const gchar *
fu_quirks_lookup_by_usb_device (FuQuirks *self, const gchar *prefix, GUsbDevice *dev)
{
const gchar *tmp;
g_autofree gchar *key1 = NULL;
g_autofree gchar *key2 = NULL;
g_autofree gchar *key3 = NULL;
g_return_val_if_fail (FU_IS_QUIRKS (self), NULL);
g_return_val_if_fail (prefix != NULL, NULL);
g_return_val_if_fail (G_USB_IS_DEVICE (dev), NULL);
/* prefer an exact match, VID:PID:REV */
key1 = g_strdup_printf ("USB\\VID_%04X&PID_%04X&REV_%04X",
g_usb_device_get_vid (dev),
g_usb_device_get_pid (dev),
g_usb_device_get_release (dev));
tmp = fu_quirks_lookup_by_id (self, prefix, key1);
if (tmp != NULL)
return tmp;
/* VID:PID */
key2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X",
g_usb_device_get_vid (dev),
g_usb_device_get_pid (dev));
tmp = fu_quirks_lookup_by_id (self, prefix, key2);
if (tmp != NULL)
return tmp;
/* VID */
key3 = g_strdup_printf ("USB\\VID_%04X", g_usb_device_get_vid (dev));
return fu_quirks_lookup_by_id (self, prefix, key3);
}
static gboolean
fu_quirks_add_quirks_from_filename (FuQuirks *self, const gchar *filename, GError **error)
{
g_autoptr(GKeyFile) kf = g_key_file_new ();
g_auto(GStrv) groups = NULL;
/* load keyfile */
if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_NONE, error))
return FALSE;
/* add each set of groups and keys */
groups = g_key_file_get_groups (kf, NULL);
for (guint i = 0; groups[i] != NULL; i++) {
g_auto(GStrv) keys = NULL;
keys = g_key_file_get_keys (kf, groups[i], NULL, error);
if (keys == NULL)
return FALSE;
for (guint j = 0; keys[j] != NULL; j++) {
g_autofree gchar *tmp = NULL;
tmp = g_key_file_get_string (kf, groups[i], keys[j], error);
if (tmp == NULL)
return FALSE;
g_hash_table_insert (self->hash,
g_strdup_printf ("%s/%s", groups[i], keys[j]),
g_steal_pointer (&tmp));
}
}
g_debug ("now %u quirk entries", g_hash_table_size (self->hash));
return TRUE;
}
static gint
fu_quirks_filename_sort_cb (gconstpointer a, gconstpointer b)
{
const gchar *stra = *((const gchar **) a);
const gchar *strb = *((const gchar **) b);
return g_strcmp0 (stra, strb);
}
static gboolean
fu_quirks_add_quirks_for_path (FuQuirks *self, const gchar *path, GError **error)
{
const gchar *tmp;
g_autofree gchar *path_hw = NULL;
g_autoptr(GDir) dir = NULL;
g_autoptr(GPtrArray) filenames = g_ptr_array_new_with_free_func (g_free);
/* add valid files to the array */
path_hw = g_build_filename (path, "quirks.d", NULL);
if (!g_file_test (path_hw, G_FILE_TEST_EXISTS)) {
g_debug ("no %s, skipping", path_hw);
return TRUE;
}
dir = g_dir_open (path_hw, 0, error);
if (dir == NULL)
return FALSE;
while ((tmp = g_dir_read_name (dir)) != NULL) {
if (!g_str_has_suffix (tmp, ".quirk")) {
g_debug ("skipping invalid file %s", tmp);
continue;
}
g_ptr_array_add (filenames, g_build_filename (path_hw, tmp, NULL));
}
/* sort */
g_ptr_array_sort (filenames, fu_quirks_filename_sort_cb);
/* process files */
for (guint i = 0; i < filenames->len; i++) {
const gchar *filename = g_ptr_array_index (filenames, i);
/* load from keyfile */
g_debug ("loading quirks from %s", filename);
if (!fu_quirks_add_quirks_from_filename (self, filename, error)) {
g_prefix_error (error, "failed to load %s: ", filename);
return FALSE;
}
/* watch the file for changes */
if (!fu_quirks_add_inotify (self, filename, error))
return FALSE;
}
/* success */
return TRUE;
}
/**
* fu_quirks_load: (skip)
* @self: A #FuQuirks
* @error: A #GError, or %NULL
*
* Loads the various files that define the hardware quirks used in plugins.
*
* Returns: %TRUE for success
*
* Since: 1.0.1
**/
gboolean
fu_quirks_load (FuQuirks *self, GError **error)
{
g_autofree gchar *localstate_fwupd = NULL;
g_return_val_if_fail (FU_IS_QUIRKS (self), FALSE);
/* ensure empty in case we're called from a monitor change */
g_ptr_array_set_size (self->monitors, 0);
g_hash_table_remove_all (self->hash);
/* system datadir */
if (!fu_quirks_add_quirks_for_path (self, FWUPDDATADIR, error))
return FALSE;
/* something we can write when using Ostree */
localstate_fwupd = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", NULL);
if (!fu_quirks_add_quirks_for_path (self, localstate_fwupd, error))
return FALSE;
/* success */
return TRUE;
}
static void
fu_quirks_class_init (FuQuirksClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fu_quirks_finalize;
}
static void
fu_quirks_init (FuQuirks *self)
{
self->monitors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
self->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
}
static void
fu_quirks_finalize (GObject *obj)
{
FuQuirks *self = FU_QUIRKS (obj);
g_ptr_array_unref (self->monitors);
g_hash_table_unref (self->hash);
G_OBJECT_CLASS (fu_quirks_parent_class)->finalize (obj);
}
/**
* fu_quirks_new: (skip)
*
* Creates a new quirks object.
*
* Return value: a new #FuQuirks
**/
FuQuirks *
fu_quirks_new (void)
{
FuQuirks *self;
self = g_object_new (FU_TYPE_QUIRKS, NULL);
return FU_QUIRKS (self);
}

View File

@ -1,6 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
* *
* Copyright (C) 2016 Mario Limonciello <mario_limonciello@dell.com> * Copyright (C) 2016 Mario Limonciello <mario_limonciello@dell.com>
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
* *
* Licensed under the GNU General Public License Version 2 * Licensed under the GNU General Public License Version 2
* *
@ -22,6 +23,30 @@
#ifndef __FU_QUIRKS_H #ifndef __FU_QUIRKS_H
#define __FU_QUIRKS_H #define __FU_QUIRKS_H
G_BEGIN_DECLS
#include <glib-object.h>
#include <gusb.h>
#define FU_TYPE_QUIRKS (fu_quirks_get_type ())
G_DECLARE_FINAL_TYPE (FuQuirks, fu_quirks, FU, QUIRKS, GObject)
FuQuirks *fu_quirks_new (void);
gboolean fu_quirks_load (FuQuirks *self,
GError **error);
const gchar *fu_quirks_lookup_by_id (FuQuirks *self,
const gchar *prefix,
const gchar *id);
const gchar *fu_quirks_lookup_by_glob (FuQuirks *self,
const gchar *prefix,
const gchar *glob);
const gchar *fu_quirks_lookup_by_usb_device (FuQuirks *self,
const gchar *prefix,
GUsbDevice *dev);
#include <appstream-glib.h>
/* FIXME: port to above */
typedef struct { typedef struct {
const gchar *sys_vendor; const gchar *sys_vendor;
const gchar *identifier; const gchar *identifier;
@ -35,5 +60,6 @@ static const FuVendorQuirks quirk_table[] = {
{ NULL, NULL, AS_VERSION_PARSE_FLAG_NONE } { NULL, NULL, AS_VERSION_PARSE_FLAG_NONE }
}; };
#endif /* __FU_QUIRKS_H */ G_END_DECLS
#endif /* __FU_QUIRKS_H */

View File

@ -29,8 +29,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "fu-config.h"
#include "fu-device-private.h" #include "fu-device-private.h"
#include "fu-engine.h" #include "fu-engine.h"
#include "fu-quirks.h"
#include "fu-keyring.h" #include "fu-keyring.h"
#include "fu-pending.h" #include "fu-pending.h"
#include "fu-plugin-private.h" #include "fu-plugin-private.h"
@ -413,6 +415,45 @@ _plugin_device_register_cb (FuPlugin *plugin, FuDevice *device, gpointer user_da
fu_plugin_runner_device_register (plugin, device); fu_plugin_runner_device_register (plugin, device);
} }
static void
fu_plugin_quirks_func (void)
{
const gchar *tmp;
gboolean ret;
g_autoptr(FuQuirks) quirks = fu_quirks_new ();
g_autoptr(FuPlugin) plugin = fu_plugin_new ();
g_autoptr(GError) error = NULL;
ret = fu_quirks_load (quirks, &error);
g_assert_no_error (error);
g_assert (ret);
fu_plugin_set_quirks (plugin, quirks);
/* exact */
tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "USB\\VID_0A5C&PID_6412");
g_assert_cmpstr (tmp, ==, "ignore-runtime");
tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "ACME Inc.");
g_assert_cmpstr (tmp, ==, "awesome");
tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "CORP*");
g_assert_cmpstr (tmp, ==, "town");
tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "USB\\VID_FFFF&PID_FFFF");
g_assert_cmpstr (tmp, ==, "");
tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-Unfound", "baz");
g_assert_cmpstr (tmp, ==, NULL);
tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-tests", "unfound");
g_assert_cmpstr (tmp, ==, NULL);
tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-unfound", "unfound");
g_assert_cmpstr (tmp, ==, NULL);
/* glob */
tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "ACME*");
g_assert_cmpstr (tmp, ==, "awesome");
tmp = fu_quirks_lookup_by_glob (quirks, "fwupd-plugin-test", "CORPORATION");
g_assert_cmpstr (tmp, ==, "town");
tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "unfound*");
g_assert_cmpstr (tmp, ==, NULL);
}
static void static void
fu_plugin_module_func (void) fu_plugin_module_func (void)
{ {
@ -927,6 +968,7 @@ main (int argc, char **argv)
g_test_add_func ("/fwupd/pending", fu_pending_func); g_test_add_func ("/fwupd/pending", fu_pending_func);
g_test_add_func ("/fwupd/plugin{delay}", fu_plugin_delay_func); g_test_add_func ("/fwupd/plugin{delay}", fu_plugin_delay_func);
g_test_add_func ("/fwupd/plugin{module}", fu_plugin_module_func); g_test_add_func ("/fwupd/plugin{module}", fu_plugin_module_func);
g_test_add_func ("/fwupd/plugin{quirks}", fu_plugin_quirks_func);
g_test_add_func ("/fwupd/keyring{gpg}", fu_keyring_gpg_func); g_test_add_func ("/fwupd/keyring{gpg}", fu_keyring_gpg_func);
g_test_add_func ("/fwupd/keyring{pkcs7}", fu_keyring_pkcs7_func); g_test_add_func ("/fwupd/keyring{pkcs7}", fu_keyring_pkcs7_func);
g_test_add_func ("/fwupd/common{spawn)", fu_common_spawn_func); g_test_add_func ("/fwupd/common{spawn)", fu_common_spawn_func);

View File

@ -28,6 +28,7 @@ libfwupdprivate = static_library(
'fu-pending.c', 'fu-pending.c',
'fu-plugin.c', 'fu-plugin.c',
'fu-progressbar.c', 'fu-progressbar.c',
'fu-quirks.c',
'fu-smbios.c', 'fu-smbios.c',
'fu-test.c', 'fu-test.c',
], ],
@ -50,6 +51,7 @@ libfwupdprivate = static_library(
cargs, cargs,
'-DLOCALSTATEDIR="' + localstatedir + '"', '-DLOCALSTATEDIR="' + localstatedir + '"',
'-DSYSFSFIRMWAREDIR="/sys/firmware"', '-DSYSFSFIRMWAREDIR="/sys/firmware"',
'-DFWUPDDATADIR="' + join_paths(get_option('prefix'), get_option('datadir'), 'fwupd') + '"',
'-DFU_OFFLINE_DESTDIR=""', '-DFU_OFFLINE_DESTDIR=""',
], ],
) )
@ -129,6 +131,7 @@ executable(
'fu-keyring.c', 'fu-keyring.c',
'fu-pending.c', 'fu-pending.c',
'fu-plugin.c', 'fu-plugin.c',
'fu-quirks.c',
'fu-smbios.c', 'fu-smbios.c',
], ],
include_directories : [ include_directories : [
@ -155,6 +158,7 @@ executable(
'-DPLUGINDIR="' + plugin_dir + '"', '-DPLUGINDIR="' + plugin_dir + '"',
'-DSYSFSFIRMWAREDIR="/sys/firmware"', '-DSYSFSFIRMWAREDIR="/sys/firmware"',
'-DSYSCONFDIR="' + default_sysconfdir + '"', '-DSYSCONFDIR="' + default_sysconfdir + '"',
'-DFWUPDDATADIR="' + join_paths(get_option('prefix'), get_option('datadir'), 'fwupd') + '"',
'-DFWUPDCONFIGDIR="' + join_paths(default_sysconfdir, 'fwupd') + '"', '-DFWUPDCONFIGDIR="' + join_paths(default_sysconfdir, 'fwupd') + '"',
'-DFU_OFFLINE_DESTDIR=""', '-DFU_OFFLINE_DESTDIR=""',
], ],
@ -187,6 +191,7 @@ if get_option('enable-tests')
'fu-keyring-result.c', 'fu-keyring-result.c',
'fu-plugin.c', 'fu-plugin.c',
'fu-progressbar.c', 'fu-progressbar.c',
'fu-quirks.c',
'fu-smbios.c', 'fu-smbios.c',
'fu-test.c', 'fu-test.c',
], ],
@ -220,6 +225,7 @@ if get_option('enable-tests')
'-DFU_OFFLINE_DESTDIR="/tmp/fwupd-self-test"', '-DFU_OFFLINE_DESTDIR="/tmp/fwupd-self-test"',
'-DPLUGINDIR="' + testdatadir_src + '"', '-DPLUGINDIR="' + testdatadir_src + '"',
'-DSYSFSFIRMWAREDIR="' + testdatadir_src + '"', '-DSYSFSFIRMWAREDIR="' + testdatadir_src + '"',
'-DFWUPDDATADIR="' + testdatadir_src + '"',
'-DSYSCONFDIR="' + testdatadir_src + '"', '-DSYSCONFDIR="' + testdatadir_src + '"',
'-DFWUPDCONFIGDIR="' + testdatadir_src + '"', '-DFWUPDCONFIGDIR="' + testdatadir_src + '"',
], ],
@ -239,6 +245,8 @@ if get_option('enable-introspection')
'fu-device-locker.h', 'fu-device-locker.h',
'fu-plugin.c', 'fu-plugin.c',
'fu-plugin.h', 'fu-plugin.h',
'fu-quirks.c',
'fu-quirks.h',
], ],
nsversion : '1.0', nsversion : '1.0',
namespace : 'Fu', namespace : 'Fu',