diff --git a/.lgtm.yml b/.lgtm.yml
index 1463254d6..36bc8173d 100644
--- a/.lgtm.yml
+++ b/.lgtm.yml
@@ -12,7 +12,7 @@ extraction:
- python3-cairo
- libssl-dev
after_prepare:
- - "wget -O libxmlb.zip https://github.com/hughsie/libxmlb/archive/0.1.7.zip"
+ - "wget -O libxmlb.zip https://github.com/hughsie/libxmlb/archive/0.1.13.zip"
- "mkdir -p subprojects/libxmlb"
- "bsdtar --strip-components=1 -xvf libxmlb.zip -C subprojects/libxmlb"
- "wget -O flashrom.zip https://github.com/hughsie/flashrom/archive/wip/hughsie/fwupd.zip"
diff --git a/contrib/ci/dependencies.xml b/contrib/ci/dependencies.xml
index daa6556d4..eeaaccf26 100644
--- a/contrib/ci/dependencies.xml
+++ b/contrib/ci/dependencies.xml
@@ -602,20 +602,18 @@
- (>= 0.1.5)
+ (>= 0.1.13)
libxmlb-dev:s390x
-
diff --git a/data/tests/quirks.d/merged.quirk b/data/tests/quirks.d/merged.quirk
deleted file mode 100644
index 7c9f67620..000000000
--- a/data/tests/quirks.d/merged.quirk
+++ /dev/null
@@ -1,2 +0,0 @@
-[USB\VID_0A5C&PID_6412]
-Flags = MERGE_ME
diff --git a/data/tests/quirks.d/tests.quirk b/data/tests/quirks.d/tests.quirk
index bcb6e5022..8b8ed30b9 100644
--- a/data/tests/quirks.d/tests.quirk
+++ b/data/tests/quirks.d/tests.quirk
@@ -1,9 +1,6 @@
[USB\VID_0A5C&PID_6412]
Flags= ignore-runtime
-[USB\VID_FFFF&PID_FFFF]
-Flags =
-
[ACME Inc.=True]
Test = awesome
diff --git a/meson.build b/meson.build
index 0eae5b6bb..8e1de8879 100644
--- a/meson.build
+++ b/meson.build
@@ -169,7 +169,7 @@ gudev = dependency('gudev-1.0')
if gudev.version().version_compare('>= 232')
conf.set('HAVE_GUDEV_232', '1')
endif
-libxmlb = dependency('xmlb', version : '>= 0.1.7', fallback : ['libxmlb', 'libxmlb_dep'])
+libxmlb = dependency('xmlb', version : '>= 0.1.13', fallback : ['libxmlb', 'libxmlb_dep'])
gusb = dependency('gusb', version : '>= 0.2.9')
sqlite = dependency('sqlite3')
libarchive = dependency('libarchive')
diff --git a/plugins/dfu/dfu-tool.c b/plugins/dfu/dfu-tool.c
index ea639e1c9..7c0d9f9d3 100644
--- a/plugins/dfu/dfu-tool.c
+++ b/plugins/dfu/dfu-tool.c
@@ -1287,7 +1287,7 @@ main (int argc, char *argv[])
/* use quirks */
priv->quirks = fu_quirks_new ();
- if (!fu_quirks_load (priv->quirks, &error)) {
+ if (!fu_quirks_load (priv->quirks, FU_QUIRKS_LOAD_FLAG_NONE, &error)) {
/* TRANSLATORS: quirks are device-specific workarounds */
g_print ("%s: %s\n", _("Failed to load quirks"), error->message);
return EXIT_FAILURE;
diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c
index f322660ff..34a422536 100644
--- a/plugins/uefi/fu-plugin-uefi.c
+++ b/plugins/uefi/fu-plugin-uefi.c
@@ -751,7 +751,6 @@ fu_plugin_uefi_create_dummy (FuPlugin *plugin, const gchar *reason, GError **err
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_REQUIRE_AC);
fu_device_add_icon (dev, "computer");
- fu_device_set_plugin (dev, fu_plugin_get_name (plugin));
fu_device_set_id (dev, "UEFI-dummy");
fu_device_add_instance_id (dev, "main-system-firmware");
if (!fu_device_setup (dev, error))
diff --git a/src/fu-config.c b/src/fu-config.c
index b3c7c6d76..38b8b48f7 100644
--- a/src/fu-config.c
+++ b/src/fu-config.c
@@ -550,11 +550,9 @@ fu_config_load (FuConfig *self, FuConfigLoadFlags flags, GError **error)
for (guint i = 0; locales[i] != NULL; i++)
xb_builder_add_locale (builder, locales[i]);
-#if LIBXMLB_CHECK_VERSION(0,1,7)
/* on a read-only filesystem don't care about the cache GUID */
if (flags & FU_CONFIG_LOAD_FLAG_READONLY_FS)
compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID;
-#endif
/* build the metainfo silo */
cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG);
diff --git a/src/fu-device-private.h b/src/fu-device-private.h
index bd1c6eb60..4b121402c 100644
--- a/src/fu-device-private.h
+++ b/src/fu-device-private.h
@@ -9,6 +9,8 @@
#include
#include
+#define fu_device_set_plugin(d,v) fwupd_device_set_plugin(FWUPD_DEVICE(d),v)
+
/**
* FuDeviceInstanceFlags:
* @FU_DEVICE_INSTANCE_FLAG_NONE: No flags set
@@ -46,3 +48,4 @@ void fu_device_add_instance_id_full (FuDevice *self,
const gchar *instance_id,
FuDeviceInstanceFlags flags);
gchar *fu_device_get_guids_as_str (FuDevice *self);
+GPtrArray *fu_device_get_possible_plugins (FuDevice *self);
diff --git a/src/fu-device.c b/src/fu-device.c
index 4462043ce..ff2ba7c0b 100644
--- a/src/fu-device.c
+++ b/src/fu-device.c
@@ -56,6 +56,7 @@ typedef struct {
guint64 size_max;
gint open_refcount; /* atomic */
GType specialized_gtype;
+ GPtrArray *possible_plugins;
} FuDevicePrivate;
enum {
@@ -126,6 +127,40 @@ fu_device_set_property (GObject *object, guint prop_id,
}
}
+/**
+ * fu_device_get_possible_plugins:
+ * @self: A #FuDevice
+ *
+ * Gets the list of possible plugin names, typically added from quirk files.
+ *
+ * Returns: (element-type utf-8) (transfer container): plugin names
+ *
+ * Since: 1.3.3
+ **/
+GPtrArray *
+fu_device_get_possible_plugins (FuDevice *self)
+{
+ FuDevicePrivate *priv = GET_PRIVATE (self);
+ return g_ptr_array_ref (priv->possible_plugins);
+}
+
+/**
+ * fu_device_add_possible_plugin:
+ * @self: A #FuDevice
+ * @plugin: A plugin name, e.g. `dfu`
+ *
+ * Adds a plugin name to the list of plugins that *might* be able to handle this
+ * device. This is tyically called from a quirk handler.
+ *
+ * Since: 1.3.3
+ **/
+static void
+fu_device_add_possible_plugin (FuDevice *self, const gchar *plugin)
+{
+ FuDevicePrivate *priv = GET_PRIVATE (self);
+ g_ptr_array_add (priv->possible_plugins, g_strdup (plugin));
+}
+
/**
* fu_device_poll:
* @self: A #FuDevice
@@ -644,7 +679,7 @@ fu_device_set_quirk_kv (FuDevice *self,
FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self);
if (g_strcmp0 (key, FU_QUIRKS_PLUGIN) == 0) {
- fu_device_set_plugin (self, value);
+ fu_device_add_possible_plugin (self, value);
return TRUE;
}
if (g_strcmp0 (key, FU_QUIRKS_FLAGS) == 0) {
@@ -750,28 +785,26 @@ fu_device_get_specialized_gtype (FuDevice *self)
return priv->specialized_gtype;
}
+static void
+fu_device_quirks_iter_cb (FuQuirks *quirks, const gchar *key, const gchar *value, gpointer user_data)
+{
+ FuDevice *self = FU_DEVICE (user_data);
+ g_autoptr(GError) error = NULL;
+ if (!fu_device_set_quirk_kv (self, key, value, &error)) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
+ g_warning ("failed to set quirk key %s=%s: %s",
+ key, value, error->message);
+ }
+ }
+}
+
static void
fu_device_add_guid_quirks (FuDevice *self, const gchar *guid)
{
FuDevicePrivate *priv = GET_PRIVATE (self);
- const gchar *key;
- const gchar *value;
- GHashTableIter iter;
-
- /* not set */
if (priv->quirks == NULL)
return;
- if (!fu_quirks_get_kvs_for_guid (priv->quirks, guid, &iter))
- return;
- while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
- g_autoptr(GError) error = NULL;
- if (!fu_device_set_quirk_kv (self, key, value, &error)) {
- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
- g_warning ("failed to set quirk key %s=%s: %s",
- key, value, error->message);
- }
- }
- }
+ fu_quirks_lookup_by_id_iter (priv->quirks, guid, fu_device_quirks_iter_cb, self);
}
/**
@@ -2522,6 +2555,7 @@ fu_device_init (FuDevice *self)
priv->status = FWUPD_STATUS_IDLE;
priv->children = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
priv->parent_guids = g_ptr_array_new_with_free_func (g_free);
+ priv->possible_plugins = g_ptr_array_new_with_free_func (g_free);
g_rw_lock_init (&priv->parent_guids_mutex);
priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_free);
@@ -2547,6 +2581,7 @@ fu_device_finalize (GObject *object)
g_hash_table_unref (priv->metadata);
g_ptr_array_unref (priv->children);
g_ptr_array_unref (priv->parent_guids);
+ g_ptr_array_unref (priv->possible_plugins);
g_free (priv->alternate_id);
g_free (priv->equivalent_id);
g_free (priv->physical_id);
diff --git a/src/fu-engine.c b/src/fu-engine.c
index 05b59077f..e4173cd7e 100644
--- a/src/fu-engine.c
+++ b/src/fu-engine.c
@@ -2422,11 +2422,9 @@ fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError *
xb_builder_import_source (builder, source);
}
-#if LIBXMLB_CHECK_VERSION(0,1,7)
/* on a read-only filesystem don't care about the cache GUID */
if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS)
compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID;
-#endif
/* ensure silo is up to date */
cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG);
@@ -3983,9 +3981,9 @@ fu_engine_recoldplug_delay_cb (gpointer user_data)
static void
fu_engine_udev_device_add (FuEngine *self, GUdevDevice *udev_device)
{
- const gchar *plugin_name;
g_autoptr(FuUdevDevice) device = fu_udev_device_new (udev_device);
g_autoptr(GError) error_local = NULL;
+ g_autoptr(GPtrArray) possible_plugins = NULL;
/* add any extra quirks */
fu_device_set_quirks (FU_DEVICE (device), self->quirks);
@@ -3997,35 +3995,32 @@ fu_engine_udev_device_add (FuEngine *self, GUdevDevice *udev_device)
}
/* can be specified using a quirk */
- plugin_name = fu_device_get_plugin (FU_DEVICE (device));
- if (plugin_name != NULL) {
- g_auto(GStrv) plugins = g_strsplit (plugin_name, ",", -1);
- for (guint i = 0; plugins[i] != NULL; i++) {
- FuPlugin *plugin;
- g_autoptr(GError) error = NULL;
+ possible_plugins = fu_device_get_possible_plugins (FU_DEVICE (device));
+ for (guint i = 0; i < possible_plugins->len; i++) {
+ FuPlugin *plugin;
+ const gchar *plugin_name = g_ptr_array_index (possible_plugins, i);
+ g_autoptr(GError) error = NULL;
- plugin = fu_plugin_list_find_by_name (self->plugin_list,
- plugins[i], &error);
- if (plugin == NULL) {
- g_warning ("failed to find specified plugin %s: %s",
- plugin_name, error->message);
- continue;
- }
- if (!fu_plugin_runner_udev_device_added (plugin, device, &error)) {
- if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
- if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
- g_debug ("%s ignoring: %s",
- fu_plugin_get_name (plugin),
- error->message);
- }
- continue;
+ plugin = fu_plugin_list_find_by_name (self->plugin_list,
+ plugin_name, &error);
+ if (plugin == NULL) {
+ g_warning ("failed to find specified plugin %s: %s",
+ plugin_name, error->message);
+ continue;
+ }
+ if (!fu_plugin_runner_udev_device_added (plugin, device, &error)) {
+ if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
+ if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
+ g_debug ("%s ignoring: %s",
+ fu_plugin_get_name (plugin),
+ error->message);
}
- g_warning ("failed to add udev device %s: %s",
- g_udev_device_get_sysfs_path (udev_device),
- error->message);
continue;
}
- return;
+ g_warning ("failed to add udev device %s: %s",
+ g_udev_device_get_sysfs_path (udev_device),
+ error->message);
+ continue;
}
}
}
@@ -4467,10 +4462,10 @@ fu_engine_usb_device_added_cb (GUsbContext *ctx,
static void
-fu_engine_load_quirks (FuEngine *self)
+fu_engine_load_quirks (FuEngine *self, FuQuirksLoadFlags quirks_flags)
{
g_autoptr(GError) error = NULL;
- if (!fu_quirks_load (self->quirks, &error))
+ if (!fu_quirks_load (self->quirks, quirks_flags, &error))
g_warning ("Failed to load quirks: %s", error->message);
}
@@ -4655,6 +4650,7 @@ gboolean
fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error)
{
FuConfigLoadFlags config_flags = FU_CONFIG_LOAD_FLAG_NONE;
+ FuQuirksLoadFlags quirks_flags = FU_QUIRKS_LOAD_FLAG_NONE;
g_autoptr(GPtrArray) checksums = NULL;
g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
@@ -4703,7 +4699,10 @@ fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error)
/* load quirks, SMBIOS and the hwids */
fu_engine_load_smbios (self);
fu_engine_load_hwids (self);
- fu_engine_load_quirks (self);
+ /* on a read-only filesystem don't care about the cache GUID */
+ if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS)
+ quirks_flags |= FU_QUIRKS_LOAD_FLAG_READONLY_FS;
+ fu_engine_load_quirks (self, quirks_flags);
/* load AppStream metadata */
if (!fu_engine_load_metadata_store (self, flags, error)) {
diff --git a/src/fu-quirks.c b/src/fu-quirks.c
index 0b4bd267f..9c809c079 100644
--- a/src/fu-quirks.c
+++ b/src/fu-quirks.c
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include "fu-common.h"
#include "fu-mutex.h"
@@ -50,44 +51,12 @@ static void fu_quirks_finalize (GObject *obj);
struct _FuQuirks
{
GObject parent_instance;
- GPtrArray *monitors;
- GHashTable *hash; /* of group:{key:value} */
- GRWLock hash_mutex;
+ FuQuirksLoadFlags load_flags;
+ XbSilo *silo;
};
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;
-}
-
static gchar *
fu_quirks_build_group_key (const gchar *group)
{
@@ -107,158 +76,58 @@ fu_quirks_build_group_key (const gchar *group)
return g_strdup (group);
}
-/**
- * fu_quirks_lookup_by_id:
- * @self: A #FuPlugin
- * @group: A string group, e.g. "DeviceInstanceId=USB\VID_1235&PID_AB11"
- * @key: An ID to match the entry, e.g. "Name"
- *
- * 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 *group, const gchar *key)
+static GInputStream *
+fu_quirks_convert_quirk_to_xml_cb (XbBuilderSource *self,
+ XbBuilderSourceCtx *ctx,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
{
- GHashTable *kvs;
- g_autofree gchar *group_key = NULL;
- g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&self->hash_mutex);
-
- g_return_val_if_fail (FU_IS_QUIRKS (self), NULL);
- g_return_val_if_fail (group != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
- g_return_val_if_fail (locker != NULL, NULL);
-
- group_key = fu_quirks_build_group_key (group);
- kvs = g_hash_table_lookup (self->hash, group_key);
- if (kvs == NULL)
- return NULL;
- return g_hash_table_lookup (kvs, key);
-}
-
-/**
- * fu_quirks_get_kvs_for_guid:
- * @self: A #FuPlugin
- * @guid: a GUID
- * @iter: A #GHashTableIter, typically allocated on the stack by the caller
- *
- * Looks up all entries in the hardware database using a GUID value.
- *
- * Returns: %TRUE if the GUID was found, and @iter was set
- *
- * Since: 1.1.2
- **/
-gboolean
-fu_quirks_get_kvs_for_guid (FuQuirks *self, const gchar *guid, GHashTableIter *iter)
-{
- GHashTable *kvs;
- g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&self->hash_mutex);
- g_return_val_if_fail (locker != NULL, FALSE);
- kvs = g_hash_table_lookup (self->hash, guid);
- if (kvs == NULL)
- return FALSE;
- g_hash_table_iter_init (iter, kvs);
- return TRUE;
-}
-
-static gchar *
-fu_quirks_merge_values (const gchar *old, const gchar *new)
-{
- guint cnt = 0;
- g_autofree gchar **resv = NULL;
- g_auto(GStrv) newv = g_strsplit (new, ",", -1);
- g_auto(GStrv) oldv = g_strsplit (old, ",", -1);
-
- /* segment flags, and append if they do not already exists */
- resv = g_new0 (gchar *, g_strv_length (oldv) + g_strv_length (newv) + 1);
- for (guint i = 0; oldv[i] != NULL; i++) {
- if (!g_strv_contains ((const gchar * const *) resv, oldv[i]))
- resv[cnt++] = oldv[i];
- }
- for (guint i = 0; newv[i] != NULL; i++) {
- if (!g_strv_contains ((const gchar * const *) resv, newv[i]))
- resv[cnt++] = newv[i];
- }
- return g_strjoinv (",", resv);
-}
-
-/**
- * fu_quirks_add_value: (skip)
- * @self: A #FuQuirks
- * @group: group, e.g. `DeviceInstanceId=USB\VID_0BDA&PID_1100`
- * @key: group, e.g. `Name`
- * @value: group, e.g. `Unknown Device`
- *
- * Adds a value to the quirk database. Normally this is achieved by loading a
- * quirk file using fu_quirks_load().
- *
- * Since: 1.1.2
- **/
-void
-fu_quirks_add_value (FuQuirks *self, const gchar *group, const gchar *key, const gchar *value)
-{
- GHashTable *kvs;
- const gchar *value_old;
- g_autofree gchar *group_key = NULL;
- g_autofree gchar *value_new = NULL;
- g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_writer_locker_new (&self->hash_mutex);
-
- g_return_if_fail (locker != NULL);
-
- /* does the key already exists in our hash */
- group_key = fu_quirks_build_group_key (group);
- kvs = g_hash_table_lookup (self->hash, group_key);
- if (kvs == NULL) {
- kvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
- g_hash_table_insert (self->hash,
- g_steal_pointer (&group_key),
- kvs);
- value_new = g_strdup (value);
- } else {
- /* look up in the 2nd level hash */
- value_old = g_hash_table_lookup (kvs, key);
- if (value_old != NULL) {
- g_debug ("already found %s=%s, merging with %s",
- group_key, value_old, value);
- value_new = fu_quirks_merge_values (value_old, value);
- } else {
- value_new = g_strdup (value);
- }
- }
-
- /* insert the new value */
- g_hash_table_insert (kvs, g_strdup (key), g_steal_pointer (&value_new));
-}
-
-static gboolean
-fu_quirks_add_quirks_from_filename (FuQuirks *self, const gchar *filename, GError **error)
-{
- g_autoptr(GKeyFile) kf = g_key_file_new ();
+ g_autofree gchar *xml = NULL;
g_auto(GStrv) groups = NULL;
+ g_autoptr(GBytes) bytes = NULL;
+ g_autoptr(GKeyFile) kf = g_key_file_new ();
+ g_autoptr(XbBuilderNode) root = xb_builder_node_new ("quirk");
- /* load keyfile */
- if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_NONE, error))
- return FALSE;
+ /* parse keyfile */
+ bytes = xb_builder_source_ctx_get_bytes (ctx, cancellable, error);
+ if (bytes == NULL)
+ return NULL;
+ if (!g_key_file_load_from_data (kf,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ G_KEY_FILE_NONE,
+ error))
+ return NULL;
/* 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;
+ g_autofree gchar *group_id = NULL;
+ g_autoptr(XbBuilderNode) bn = NULL;
keys = g_key_file_get_keys (kf, groups[i], NULL, error);
if (keys == NULL)
return FALSE;
+ group_id = fu_quirks_build_group_key (groups[i]);
+ bn = xb_builder_node_insert (root, "device", "id", group_id, NULL);
for (guint j = 0; keys[j] != NULL; j++) {
g_autofree gchar *value = NULL;
- /* get value from keyfile */
value = g_key_file_get_value (kf, groups[i], keys[j], error);
if (value == NULL)
- return FALSE;
- fu_quirks_add_value (self, groups[i], keys[j], value);
+ return NULL;
+ xb_builder_node_insert_text (bn,
+ "value", value,
+ "key", keys[j],
+ NULL);
}
}
- return TRUE;
+
+ /* export as XML */
+ xml = xb_builder_node_export (root, XB_NODE_EXPORT_FLAG_ADD_HEADER, error);
+ if (xml == NULL)
+ return NULL;
+ return g_memory_input_stream_new_from_data (g_steal_pointer (&xml), -1, g_free);
}
static gint
@@ -270,7 +139,8 @@ fu_quirks_filename_sort_cb (gconstpointer a, gconstpointer b)
}
static gboolean
-fu_quirks_add_quirks_for_path (FuQuirks *self, const gchar *path, GError **error)
+fu_quirks_add_quirks_for_path (FuQuirks *self, XbBuilder *builder,
+ const gchar *path, GError **error)
{
const gchar *tmp;
g_autofree gchar *path_hw = NULL;
@@ -300,21 +170,200 @@ fu_quirks_add_quirks_for_path (FuQuirks *self, const gchar *path, GError **error
/* process files */
for (guint i = 0; i < filenames->len; i++) {
const gchar *filename = g_ptr_array_index (filenames, i);
+ g_autoptr(GFile) file = g_file_new_for_path (filename);
+ g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
/* load from keyfile */
- g_debug ("loading quirks from %s", filename);
- if (!fu_quirks_add_quirks_from_filename (self, filename, error)) {
+ xb_builder_source_add_adapter (source, "text/plain",
+ fu_quirks_convert_quirk_to_xml_cb,
+ NULL, NULL);
+ if (!xb_builder_source_load_file (source, file,
+ XB_BUILDER_SOURCE_FLAG_WATCH_FILE |
+ XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT,
+ NULL, 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;
+ xb_builder_import_source (builder, source);
}
/* success */
- g_debug ("now %u quirk entries", g_hash_table_size (self->hash));
+ return TRUE;
+}
+
+static gboolean
+fu_quirks_check_silo (FuQuirks *self, GError **error)
+{
+ XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_WATCH_BLOB;
+ g_autofree gchar *cachedirpkg = NULL;
+ g_autofree gchar *datadir = NULL;
+ g_autofree gchar *localstatedir = NULL;
+ g_autofree gchar *xmlbfn = NULL;
+ g_autoptr(GFile) file = NULL;
+ g_autoptr(XbBuilder) builder = NULL;
+
+ /* everything is okay */
+ if (self->silo != NULL && xb_silo_is_valid (self->silo))
+ return TRUE;
+
+ /* system datadir */
+ builder = xb_builder_new ();
+ datadir = fu_common_get_path (FU_PATH_KIND_DATADIR_PKG);
+ if (!fu_quirks_add_quirks_for_path (self, builder, datadir, error))
+ return FALSE;
+
+ /* something we can write when using Ostree */
+ localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
+ if (!fu_quirks_add_quirks_for_path (self, builder, localstatedir, error))
+ return FALSE;
+
+ /* load silo */
+ cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG);
+ xmlbfn = g_build_filename (cachedirpkg, "quirks.xmlb", NULL);
+ file = g_file_new_for_path (xmlbfn);
+ if (g_getenv ("XMLB_VERBOSE") != NULL) {
+ xb_builder_set_profile_flags (builder,
+ XB_SILO_PROFILE_FLAG_XPATH |
+ XB_SILO_PROFILE_FLAG_DEBUG);
+ }
+ if (self->load_flags & FU_QUIRKS_LOAD_FLAG_READONLY_FS)
+ compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID;
+ self->silo = xb_builder_ensure (builder, file, compile_flags, NULL, error);
+ return self->silo != NULL;
+}
+
+/**
+ * fu_quirks_lookup_by_id:
+ * @self: A #FuPlugin
+ * @group: A string group, e.g. "DeviceInstanceId=USB\VID_1235&PID_AB11"
+ * @key: An ID to match the entry, e.g. "Name"
+ *
+ * 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 *group, const gchar *key)
+{
+ g_autofree gchar *group_key = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(XbNode) n = NULL;
+ g_autoptr(XbQuery) query = NULL;
+
+ g_return_val_if_fail (FU_IS_QUIRKS (self), NULL);
+ g_return_val_if_fail (group != NULL, NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ /* ensure up to date */
+ if (!fu_quirks_check_silo (self, &error)) {
+ g_warning ("failed to build silo: %s", error->message);
+ return NULL;
+ }
+
+ /* query */
+ group_key = fu_quirks_build_group_key (group);
+ query = xb_query_new_full (self->silo,
+ "quirk/device[@id=?]/value[@key=?]",
+ XB_QUERY_FLAG_NONE,
+ &error);
+ if (query == NULL) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ return NULL;
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+ return NULL;
+ g_warning ("failed to build query: %s", error->message);
+ return NULL;
+ }
+ if (!xb_query_bind_str (query, 0, group_key, &error)) {
+ g_warning ("failed to bind 0: %s", error->message);
+ return NULL;
+ }
+ if (!xb_query_bind_str (query, 1, key, &error)) {
+ g_warning ("failed to bind 1: %s", error->message);
+ return NULL;
+ }
+ n = xb_silo_query_first_full (self->silo, query, &error);
+ if (n == NULL) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ return NULL;
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+ return NULL;
+ g_warning ("failed to query: %s", error->message);
+ return NULL;
+ }
+ return xb_node_get_text (n);
+}
+
+/**
+ * fu_quirks_lookup_by_id_iter:
+ * @self: A #FuPlugin
+ * @guid: a GUID
+ * @iter_cb: A #FuQuirksIter
+ * @user_data: user data passed to @iter_cb
+ *
+ * Looks up all entries in the hardware database using a GUID value.
+ *
+ * Returns: %TRUE if the ID was found, and @iter was called
+ *
+ * Since: 1.3.3
+ **/
+gboolean
+fu_quirks_lookup_by_id_iter (FuQuirks *self, const gchar *group,
+ FuQuirksIter iter_cb, gpointer user_data)
+{
+ g_autofree gchar *group_key = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GPtrArray) results = NULL;
+ g_autoptr(XbQuery) query = NULL;
+
+ g_return_val_if_fail (FU_IS_QUIRKS (self), FALSE);
+ g_return_val_if_fail (group != NULL, FALSE);
+ g_return_val_if_fail (iter_cb != NULL, FALSE);
+
+ /* ensure up to date */
+ if (!fu_quirks_check_silo (self, &error)) {
+ g_warning ("failed to build silo: %s", error->message);
+ return FALSE;
+ }
+
+ /* query */
+ group_key = fu_quirks_build_group_key (group);
+ query = xb_query_new_full (self->silo,
+ "quirk/device[@id=?]/value",
+ XB_QUERY_FLAG_NONE,
+ &error);
+ if (query == NULL) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ return FALSE;
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+ return FALSE;
+ g_warning ("failed to build query: %s", error->message);
+ return FALSE;
+ }
+ if (!xb_query_bind_str (query, 0, group_key, &error)) {
+ g_warning ("failed to bind 0: %s", error->message);
+ return FALSE;
+ }
+ results = xb_silo_query_full (self->silo, query, &error);
+ if (results == NULL) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ return FALSE;
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
+ return FALSE;
+ g_warning ("failed to query: %s", error->message);
+ return FALSE;
+ }
+ for (guint i = 0; i < results->len; i++) {
+ XbNode *n = g_ptr_array_index (results, i);
+ iter_cb (self,
+ xb_node_get_attr (n, "key"),
+ xb_node_get_text (n),
+ user_data);
+ }
return TRUE;
}
@@ -330,31 +379,11 @@ fu_quirks_add_quirks_for_path (FuQuirks *self, const gchar *path, GError **error
* Since: 1.0.1
**/
gboolean
-fu_quirks_load (FuQuirks *self, GError **error)
+fu_quirks_load (FuQuirks *self, FuQuirksLoadFlags load_flags, GError **error)
{
- g_autofree gchar *datadir = NULL;
- g_autofree gchar *localstatedir = 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_rw_lock_writer_lock (&self->hash_mutex);
- g_hash_table_remove_all (self->hash);
- g_rw_lock_writer_unlock (&self->hash_mutex);
-
- /* system datadir */
- datadir = fu_common_get_path (FU_PATH_KIND_DATADIR_PKG);
- if (!fu_quirks_add_quirks_for_path (self, datadir, error))
- return FALSE;
-
- /* something we can write when using Ostree */
- localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
- if (!fu_quirks_add_quirks_for_path (self, localstatedir, error))
- return FALSE;
-
- /* success */
- return TRUE;
+ self->load_flags = load_flags;
+ return fu_quirks_check_silo (self, error);
}
static void
@@ -367,18 +396,14 @@ fu_quirks_class_init (FuQuirksClass *klass)
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, (GDestroyNotify) g_hash_table_unref);
- g_rw_lock_init (&self->hash_mutex);
}
static void
fu_quirks_finalize (GObject *obj)
{
FuQuirks *self = FU_QUIRKS (obj);
- g_ptr_array_unref (self->monitors);
- g_rw_lock_clear (&self->hash_mutex);
- g_hash_table_unref (self->hash);
+ if (self->silo != NULL)
+ g_object_unref (self->silo);
G_OBJECT_CLASS (fu_quirks_parent_class)->finalize (obj);
}
diff --git a/src/fu-quirks.h b/src/fu-quirks.h
index 8aca6373e..40c01d297 100644
--- a/src/fu-quirks.h
+++ b/src/fu-quirks.h
@@ -12,19 +12,36 @@
#define FU_TYPE_QUIRKS (fu_quirks_get_type ())
G_DECLARE_FINAL_TYPE (FuQuirks, fu_quirks, FU, QUIRKS, GObject)
+/**
+ * FuQuirksLoadFlags:
+ * @FU_QUIRKS_LOAD_FLAG_NONE: No flags set
+ * @FU_QUIRKS_LOAD_FLAG_READONLY_FS: Ignore readonly filesystem errors
+ *
+ * The flags to use when loading quirks.
+ **/
+typedef enum {
+ FU_QUIRKS_LOAD_FLAG_NONE = 0,
+ FU_QUIRKS_LOAD_FLAG_READONLY_FS = 1 << 0,
+ /*< private >*/
+ FU_QUIRKS_LOAD_FLAG_LAST
+} FuQuirksLoadFlags;
+
+typedef void (*FuQuirksIter) (FuQuirks *self,
+ const gchar *key,
+ const gchar *value,
+ gpointer user_data);
+
FuQuirks *fu_quirks_new (void);
gboolean fu_quirks_load (FuQuirks *self,
+ FuQuirksLoadFlags load_flags,
GError **error);
const gchar *fu_quirks_lookup_by_id (FuQuirks *self,
const gchar *group,
const gchar *key);
-void fu_quirks_add_value (FuQuirks *self,
+gboolean fu_quirks_lookup_by_id_iter (FuQuirks *self,
const gchar *group,
- const gchar *key,
- const gchar *value);
-gboolean fu_quirks_get_kvs_for_guid (FuQuirks *self,
- const gchar *guid,
- GHashTableIter *iter);
+ FuQuirksIter iter,
+ gpointer user_data);
#define FU_QUIRKS_PLUGIN "Plugin"
#define FU_QUIRKS_UEFI_VERSION_FORMAT "UefiVersionFormat"
diff --git a/src/fu-self-test.c b/src/fu-self-test.c
index 2f4a83aa8..de27741a1 100644
--- a/src/fu-self-test.c
+++ b/src/fu-self-test.c
@@ -2114,20 +2114,18 @@ fu_plugin_quirks_func (void)
g_autoptr(FuPlugin) plugin = fu_plugin_new ();
g_autoptr(GError) error = NULL;
- ret = fu_quirks_load (quirks, &error);
+ ret = fu_quirks_load (quirks, FU_QUIRKS_LOAD_FLAG_NONE, &error);
g_assert_no_error (error);
g_assert (ret);
fu_plugin_set_quirks (plugin, quirks);
/* exact */
tmp = fu_plugin_lookup_quirk_by_id (plugin, "USB\\VID_0A5C&PID_6412", "Flags");
- g_assert_cmpstr (tmp, ==, "MERGE_ME,ignore-runtime");
+ g_assert_cmpstr (tmp, ==, "ignore-runtime");
tmp = fu_plugin_lookup_quirk_by_id (plugin, "ACME Inc.=True", "Test");
g_assert_cmpstr (tmp, ==, "awesome");
tmp = fu_plugin_lookup_quirk_by_id (plugin, "CORP*", "Test");
g_assert_cmpstr (tmp, ==, "town");
- tmp = fu_plugin_lookup_quirk_by_id (plugin, "USB\\VID_FFFF&PID_FFFF", "Flags");
- g_assert_cmpstr (tmp, ==, "");
tmp = fu_plugin_lookup_quirk_by_id (plugin, "baz", "Unfound");
g_assert_cmpstr (tmp, ==, NULL);
tmp = fu_plugin_lookup_quirk_by_id (plugin, "unfound", "tests");
@@ -2141,29 +2139,23 @@ fu_plugin_quirks_func (void)
static void
fu_plugin_quirks_performance_func (void)
{
+ gboolean ret;
g_autoptr(FuQuirks) quirks = fu_quirks_new ();
g_autoptr(GTimer) timer = g_timer_new ();
- const gchar *keys[] = {
- "Name", "Icon", "Children", "Plugin", "Flags",
- "FirmwareSizeMin", "FirmwareSizeMax", NULL };
+ g_autoptr(GError) error = NULL;
+ const gchar *keys[] = { "Name", "Children", "Flags", NULL };
- /* insert */
- for (guint j = 0; j < 1000; j++) {
- g_autofree gchar *group = NULL;
- group = g_strdup_printf ("DeviceInstanceId=USB\\VID_0BDA&PID_%04X", j);
- for (guint i = 0; keys[i] != NULL; i++)
- fu_quirks_add_value (quirks, group, keys[i], "Value");
- }
- g_print ("insert=%.3fms ", g_timer_elapsed (timer, NULL) * 1000.f);
+ ret = fu_quirks_load (quirks, FU_QUIRKS_LOAD_FLAG_NONE, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
/* lookup */
g_timer_reset (timer);
for (guint j = 0; j < 1000; j++) {
- g_autofree gchar *group = NULL;
- group = g_strdup_printf ("DeviceInstanceId=USB\\VID_0BDA&PID_%04X", j);
+ const gchar *group = "DeviceInstanceId=USB\\VID_0BDA&PID_1100";
for (guint i = 0; keys[i] != NULL; i++) {
const gchar *tmp = fu_quirks_lookup_by_id (quirks, group, keys[i]);
- g_assert_cmpstr (tmp, ==, "Value");
+ g_assert_cmpstr (tmp, !=, NULL);
}
}
g_print ("lookup=%.3fms ", g_timer_elapsed (timer, NULL) * 1000.f);
@@ -2179,7 +2171,7 @@ fu_plugin_quirks_device_func (void)
g_autoptr(FuQuirks) quirks = fu_quirks_new ();
g_autoptr(GError) error = NULL;
- ret = fu_quirks_load (quirks, &error);
+ ret = fu_quirks_load (quirks, FU_QUIRKS_LOAD_FLAG_NONE, &error);
g_assert_no_error (error);
g_assert (ret);
diff --git a/subprojects/libxmlb.wrap b/subprojects/libxmlb.wrap
index 95d74fdb5..b98545bf5 100644
--- a/subprojects/libxmlb.wrap
+++ b/subprojects/libxmlb.wrap
@@ -1,4 +1,4 @@
[wrap-git]
directory = libxmlb
url = https://github.com/hughsie/libxmlb.git
-revision = 0.1.7
+revision = 0.1.13