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',