From 9c028f06b5a68e5b96b497c11de448b2ca2d85d7 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Sat, 28 Oct 2017 21:14:28 +0100 Subject: [PATCH] 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 --- contrib/fwupd.spec.in | 2 + data/tests/quirks.d/tests.quirk | 12 ++ docs/libfwupd/libfwupd-docs.xml | 1 + docs/libfwupd/libfwupd.types | 1 + src/fu-engine.c | 9 + src/fu-plugin-private.h | 3 + src/fu-plugin.c | 75 +++++++ src/fu-plugin.h | 8 + src/fu-quirks.c | 369 ++++++++++++++++++++++++++++++++ src/fu-quirks.h | 28 ++- src/fu-self-test.c | 42 ++++ src/meson.build | 8 + 12 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 data/tests/quirks.d/tests.quirk create mode 100644 src/fu-quirks.c diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 16585d714..e9cc0a15e 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -208,6 +208,8 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %{_unitdir}/fwupd.service %{_unitdir}/system-update.target.wants/ %dir %{_localstatedir}/lib/fwupd +%dir %{_datadir}/fwupd/quirks.d +%{_datadir}/fwupd/quirks.d/*.quirk %{_localstatedir}/lib/fwupd/builder/README.md %{_libdir}/libfwupd*.so.* %{_libdir}/girepository-1.0/Fwupd-2.0.typelib diff --git a/data/tests/quirks.d/tests.quirk b/data/tests/quirks.d/tests.quirk new file mode 100644 index 000000000..0383be65c --- /dev/null +++ b/data/tests/quirks.d/tests.quirk @@ -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 diff --git a/docs/libfwupd/libfwupd-docs.xml b/docs/libfwupd/libfwupd-docs.xml index 41054479d..1f46e7f77 100644 --- a/docs/libfwupd/libfwupd-docs.xml +++ b/docs/libfwupd/libfwupd-docs.xml @@ -45,6 +45,7 @@ + diff --git a/docs/libfwupd/libfwupd.types b/docs/libfwupd/libfwupd.types index 3ebd04da7..0fbcf8158 100644 --- a/docs/libfwupd/libfwupd.types +++ b/docs/libfwupd/libfwupd.types @@ -1,5 +1,6 @@ fwupd_client_get_type fwupd_device_get_type +fwupd_quirks_get_type fwupd_release_get_type fwupd_remote_get_type fwupd_result_get_type diff --git a/src/fu-engine.c b/src/fu-engine.c index d6b926bfe..2e5166ca4 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -75,6 +75,7 @@ struct _FuEngine GPtrArray *supported_guids; FuSmbios *smbios; FuHwids *hwids; + FuQuirks *quirks; }; enum { @@ -2662,6 +2663,7 @@ fu_engine_load_plugins (FuEngine *self, GError **error) fu_plugin_set_hwids (plugin, self->hwids); fu_plugin_set_smbios (plugin, self->smbios); fu_plugin_set_supported (plugin, self->supported_guids); + fu_plugin_set_quirks (plugin, self->quirks); g_debug ("adding plugin %s", filename); if (!fu_plugin_open (plugin, filename, &error_local)) { g_warning ("failed to open plugin %s: %s", @@ -2867,6 +2869,7 @@ fu_engine_cleanup_state (GError **error) gboolean fu_engine_load (FuEngine *self, GError **error) { + g_autoptr(GError) error_quirks = NULL; g_autoptr(GError) error_hwids = NULL; g_autoptr(GError) error_smbios = NULL; @@ -2879,6 +2882,10 @@ fu_engine_load (FuEngine *self, GError **error) 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 */ as_store_add_filter (self->store, AS_APP_KIND_FIRMWARE); 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->smbios = fu_smbios_new (); self->hwids = fu_hwids_new (); + self->quirks = fu_quirks_new (); self->pending = fu_pending_new (); self->profile = as_profile_new (); self->store = as_store_new (); @@ -2989,6 +2997,7 @@ fu_engine_finalize (GObject *obj) g_hash_table_unref (self->plugins_hash); g_object_unref (self->config); g_object_unref (self->smbios); + g_object_unref (self->quirks); g_object_unref (self->hwids); g_object_unref (self->pending); g_object_unref (self->profile); diff --git a/src/fu-plugin-private.h b/src/fu-plugin-private.h index b1a36dbd8..9096b3c94 100644 --- a/src/fu-plugin-private.h +++ b/src/fu-plugin-private.h @@ -22,6 +22,7 @@ #ifndef __FU_PLUGIN_PRIVATE_H #define __FU_PLUGIN_PRIVATE_H +#include "fu-quirks.h" #include "fu-plugin.h" #include "fu-smbios.h" @@ -37,6 +38,8 @@ void fu_plugin_set_hwids (FuPlugin *plugin, FuHwids *hwids); void fu_plugin_set_supported (FuPlugin *plugin, GPtrArray *supported_guids); +void fu_plugin_set_quirks (FuPlugin *plugin, + FuQuirks *quirks); void fu_plugin_set_smbios (FuPlugin *plugin, FuSmbios *smbios); guint fu_plugin_get_order (FuPlugin *plugin); diff --git a/src/fu-plugin.c b/src/fu-plugin.c index c74a158f3..24f767bd1 100644 --- a/src/fu-plugin.c +++ b/src/fu-plugin.c @@ -56,6 +56,7 @@ typedef struct { GPtrArray *rules[FU_PLUGIN_RULE_LAST]; gchar *name; FuHwids *hwids; + FuQuirks *quirks; GPtrArray *supported_guids; FuSmbios *smbios; 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); } +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: * @plugin: A #FuPlugin @@ -1386,6 +1459,8 @@ fu_plugin_finalize (GObject *object) g_object_unref (priv->usb_ctx); if (priv->hwids != NULL) g_object_unref (priv->hwids); + if (priv->quirks != NULL) + g_object_unref (priv->quirks); if (priv->supported_guids != NULL) g_ptr_array_unref (priv->supported_guids); if (priv->smbios != NULL) diff --git a/src/fu-plugin.h b/src/fu-plugin.h index 232973e27..b7cb87993 100644 --- a/src/fu-plugin.h +++ b/src/fu-plugin.h @@ -30,6 +30,7 @@ #include "fu-common.h" #include "fu-device.h" #include "fu-device-locker.h" +#include "fu-quirks.h" #include "fu-hwids.h" G_BEGIN_DECLS @@ -135,6 +136,13 @@ GBytes *fu_plugin_get_smbios_data (FuPlugin *plugin, void fu_plugin_add_rule (FuPlugin *plugin, FuPluginRule rule, 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 diff --git a/src/fu-quirks.c b/src/fu-quirks.c new file mode 100644 index 000000000..b02229d47 --- /dev/null +++ b/src/fu-quirks.c @@ -0,0 +1,369 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2017 Richard Hughes + * + * 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 +#include +#include +#include + +#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); +} diff --git a/src/fu-quirks.h b/src/fu-quirks.h index d91824b58..50d60c0e0 100644 --- a/src/fu-quirks.h +++ b/src/fu-quirks.h @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2016 Mario Limonciello + * Copyright (C) 2017 Richard Hughes * * Licensed under the GNU General Public License Version 2 * @@ -22,6 +23,30 @@ #ifndef __FU_QUIRKS_H #define __FU_QUIRKS_H +G_BEGIN_DECLS + +#include +#include + +#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 + +/* FIXME: port to above */ typedef struct { const gchar *sys_vendor; const gchar *identifier; @@ -35,5 +60,6 @@ static const FuVendorQuirks quirk_table[] = { { NULL, NULL, AS_VERSION_PARSE_FLAG_NONE } }; -#endif /* __FU_QUIRKS_H */ +G_END_DECLS +#endif /* __FU_QUIRKS_H */ diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 08990d6be..44207ff01 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -29,8 +29,10 @@ #include #include +#include "fu-config.h" #include "fu-device-private.h" #include "fu-engine.h" +#include "fu-quirks.h" #include "fu-keyring.h" #include "fu-pending.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); } +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 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/plugin{delay}", fu_plugin_delay_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{pkcs7}", fu_keyring_pkcs7_func); g_test_add_func ("/fwupd/common{spawn)", fu_common_spawn_func); diff --git a/src/meson.build b/src/meson.build index 4ceb6a16c..c1dce65d8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -28,6 +28,7 @@ libfwupdprivate = static_library( 'fu-pending.c', 'fu-plugin.c', 'fu-progressbar.c', + 'fu-quirks.c', 'fu-smbios.c', 'fu-test.c', ], @@ -50,6 +51,7 @@ libfwupdprivate = static_library( cargs, '-DLOCALSTATEDIR="' + localstatedir + '"', '-DSYSFSFIRMWAREDIR="/sys/firmware"', + '-DFWUPDDATADIR="' + join_paths(get_option('prefix'), get_option('datadir'), 'fwupd') + '"', '-DFU_OFFLINE_DESTDIR=""', ], ) @@ -129,6 +131,7 @@ executable( 'fu-keyring.c', 'fu-pending.c', 'fu-plugin.c', + 'fu-quirks.c', 'fu-smbios.c', ], include_directories : [ @@ -155,6 +158,7 @@ executable( '-DPLUGINDIR="' + plugin_dir + '"', '-DSYSFSFIRMWAREDIR="/sys/firmware"', '-DSYSCONFDIR="' + default_sysconfdir + '"', + '-DFWUPDDATADIR="' + join_paths(get_option('prefix'), get_option('datadir'), 'fwupd') + '"', '-DFWUPDCONFIGDIR="' + join_paths(default_sysconfdir, 'fwupd') + '"', '-DFU_OFFLINE_DESTDIR=""', ], @@ -187,6 +191,7 @@ if get_option('enable-tests') 'fu-keyring-result.c', 'fu-plugin.c', 'fu-progressbar.c', + 'fu-quirks.c', 'fu-smbios.c', 'fu-test.c', ], @@ -220,6 +225,7 @@ if get_option('enable-tests') '-DFU_OFFLINE_DESTDIR="/tmp/fwupd-self-test"', '-DPLUGINDIR="' + testdatadir_src + '"', '-DSYSFSFIRMWAREDIR="' + testdatadir_src + '"', + '-DFWUPDDATADIR="' + testdatadir_src + '"', '-DSYSCONFDIR="' + testdatadir_src + '"', '-DFWUPDCONFIGDIR="' + testdatadir_src + '"', ], @@ -239,6 +245,8 @@ if get_option('enable-introspection') 'fu-device-locker.h', 'fu-plugin.c', 'fu-plugin.h', + 'fu-quirks.c', + 'fu-quirks.h', ], nsversion : '1.0', namespace : 'Fu',