Skip to content

Commit

Permalink
Allow specifying the BKC locally
Browse files Browse the repository at this point in the history
Fixes #4040
  • Loading branch information
hughsie committed Jan 21, 2022
1 parent fc4687f commit f171082
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 1 deletion.
53 changes: 53 additions & 0 deletions README.md
Expand Up @@ -106,6 +106,59 @@ to two updates in the metadata file.
Additionally, the list of approved firmware can be supplemented using
`fwupdmgr set-approved-firmware baz` or using the D-Bus interface.

## Local metadata

Local metadata can be saved in `/var/lib/fwupd/local.d` or `/usr/share/fwupd/local.d`
which are scanned at daemon startup. This can be used to add site-specific BKC
tags to existing metadata stores. For instance:

<?xml version='1.0' encoding='utf-8'?>
<components origin="mycompanyname">
<component merge="append">
<provides>
<firmware>3ef35d3b-ceeb-5e27-8c0a-ac25f90367ab</firmware>
<firmware>2ef35d3b-ceeb-5e27-8c0a-ac25f90367ac</firmware>
<firmware>1ef35d3b-ceeb-5e27-8c0a-ac25f90367ad</firmware>
</provides>
<releases>
<release version="225.53.1649"/>
<release version="224.48.1605"/>
</releases>
<tags>
<tag>mycompanyname-2022q1</tag>
</tags>
</component>
</components>

This then appears when getting the releases for that specific GUID:

fwupdmgr get-releases --json 3ef35d3b-ceeb-5e27-8c0a-ac25f90367ab
{
"Releases" : [
{
...
"Version" : "225.53.1649",
"Tags" : [
"mycompanyname-2022q1"
],
...
},
{
...
"Version" : "224.48.1605",
"Tags" : [
"mycompanyname-2022q1"
],
...
},
{
...
"Version" : "224.45.1389",
...
}
]
}

## Other frontends

1. [GNOME Software](https://wiki.gnome.org/Apps/Software) is the graphical
Expand Down
181 changes: 180 additions & 1 deletion src/fu-engine.c
Expand Up @@ -123,6 +123,7 @@ struct _FuEngine {
gboolean loaded;
gchar *host_security_id;
FuSecurityAttrs *host_security_attrs;
GPtrArray *local_monitors; /* (element-type GFileMonitor) */
};

enum {
Expand Down Expand Up @@ -463,6 +464,76 @@ fu_engine_request_get_localized_xpath(FuEngineRequest *request, const gchar *ele
return g_string_free(xpath, FALSE);
}

/* add any client-side BKC tags */
static gboolean
fu_engine_add_local_release_metadata(FuEngine *self,
FuDevice *dev,
FwupdRelease *rel,
GError **error)
{
GPtrArray *guids = fu_device_get_guids(dev);
g_autoptr(XbQuery) query = NULL;
g_autoptr(GError) error_query = NULL;

/* prepare query with bound GUID parameter */
query = xb_query_new_full(self->silo,
"local/components/component[@merge='append']/provides/"
"firmware[text()=?]/../../releases/release[@version=?]/../../"
"tags/tag",
XB_QUERY_FLAG_OPTIMIZE | XB_QUERY_FLAG_USE_INDEXES,
&error_query);
if (query == NULL) {
if (g_error_matches(error_query, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
return TRUE;
g_propagate_error(error, g_steal_pointer(&error_query));
return FALSE;
}

/* use prepared query for each GUID */
for (guint i = 0; i < guids->len; i++) {
const gchar *guid = g_ptr_array_index(guids, i);
g_autoptr(GError) error_local = NULL;
g_autoptr(GPtrArray) tags = NULL;
#if LIBXMLB_CHECK_VERSION(0, 3, 0)
g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT();
#endif

/* bind GUID and then query */
#if LIBXMLB_CHECK_VERSION(0, 3, 0)
xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, guid, NULL);
xb_value_bindings_bind_str(xb_query_context_get_bindings(&context),
1,
fwupd_release_get_version(rel),
NULL);
tags = xb_silo_query_with_context(self->silo, query, &context, &error_local);
#else
if (!xb_query_bind_str(query, 0, guid, error)) {
g_prefix_error(error, "failed to bind GUID: ");
return FALSE;
}
if (!xb_query_bind_str(query, 1, fwupd_release_get_version(rel), error)) {
g_prefix_error(error, "failed to bind version: ");
return FALSE;
}
tags = xb_silo_query_full(self->silo, query, &error_local);
#endif
if (tags == NULL) {
if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
continue;
g_propagate_error(error, g_steal_pointer(&error_local));
return FALSE;
}
for (guint j = 0; j < tags->len; j++) {
XbNode *tag = g_ptr_array_index(tags, j);
fwupd_release_add_tag(rel, xb_node_get_text(tag));
}
}

/* success */
return TRUE;
}

static gboolean
fu_engine_set_release_from_appstream(FuEngine *self,
FuEngineRequest *request,
Expand Down Expand Up @@ -676,6 +747,12 @@ fu_engine_set_release_from_appstream(FuEngine *self,
g_ptr_array_sort_with_data(fwupd_release_get_locations(rel),
fu_engine_scheme_compare_cb,
self);

/* add any client-side BKC tags */
if (!fu_engine_add_local_release_metadata(self, dev, rel, error))
return FALSE;

/* success */
return TRUE;
}

Expand Down Expand Up @@ -3809,6 +3886,41 @@ fu_engine_md_refresh_devices(FuEngine *self)
}
}

static gboolean
fu_engine_load_metadata_store_local(FuEngine *self,
XbBuilder *builder,
FuPathKind path_kind,
GError **error)
{
g_autofree gchar *fn = fu_common_get_path(path_kind);
g_autofree gchar *metadata_path = g_build_filename(fn, "local.d", NULL);
g_autoptr(GError) error_local = NULL;
g_autoptr(GPtrArray) metadata_fns = NULL;

metadata_fns = fu_common_filename_glob(metadata_path, "*.xml", &error_local);
if (metadata_fns == NULL) {
g_debug("ignoring: %s", error_local->message);
return TRUE;
}
for (guint i = 0; i < metadata_fns->len; i++) {
const gchar *path = g_ptr_array_index(metadata_fns, i);
g_autoptr(XbBuilderSource) source = xb_builder_source_new();
g_autoptr(GFile) file = g_file_new_for_path(path);
g_debug("loading local metadata: %s", path);
if (!xb_builder_source_load_file(source,
file,
XB_BUILDER_SOURCE_FLAG_NONE,
NULL,
error))
return FALSE;
xb_builder_source_set_prefix(source, "local");
xb_builder_import_source(builder, source);
}

/* success */
return TRUE;
}

static gboolean
fu_engine_load_metadata_store(FuEngine *self, FuEngineLoadFlags flags, GError **error)
{
Expand Down Expand Up @@ -3896,6 +4008,15 @@ fu_engine_load_metadata_store(FuEngine *self, FuEngineLoadFlags flags, GError **
xb_builder_import_source(builder, source);
}

/* add any client-side data, e.g. BKC tags */
if (!fu_engine_load_metadata_store_local(self,
builder,
FU_PATH_KIND_LOCALSTATEDIR_PKG,
error))
return FALSE;
if (!fu_engine_load_metadata_store_local(self, builder, FU_PATH_KIND_DATADIR_PKG, error))
return FALSE;

/* on a read-only filesystem don't care about the cache GUID */
if (flags & FU_ENGINE_LOAD_FLAG_READONLY)
compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID;
Expand Down Expand Up @@ -3928,7 +4049,7 @@ fu_engine_config_changed_cb(FuConfig *config, FuEngine *self)
}

static void
fu_engine_remote_list_changed_cb(FuRemoteList *remote_list, FuEngine *self)
fu_engine_metadata_changed(FuEngine *self)
{
g_autoptr(GError) error_local = NULL;
if (!fu_engine_load_metadata_store(self, FU_ENGINE_LOAD_FLAG_NONE, &error_local))
Expand All @@ -3944,6 +4065,12 @@ fu_engine_remote_list_changed_cb(FuRemoteList *remote_list, FuEngine *self)
fu_engine_emit_changed(self);
}

static void
fu_engine_remote_list_changed_cb(FuRemoteList *remote_list, FuEngine *self)
{
fu_engine_metadata_changed(self);
}

static gint
fu_engine_sort_jcat_results_timestamp_cb(gconstpointer a, gconstpointer b)
{
Expand Down Expand Up @@ -6788,6 +6915,47 @@ fu_engine_ensure_paths_exist(GError **error)
return TRUE;
}

static void
fu_engine_local_metadata_changed_cb(GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
gpointer user_data)
{
FuEngine *self = FU_ENGINE(user_data);
fu_engine_metadata_changed(self);
}

static gboolean
fu_engine_load_local_metadata_watches(FuEngine *self, GError **error)
{
const FuPathKind path_kinds[] = {FU_PATH_KIND_DATADIR_PKG, FU_PATH_KIND_LOCALSTATEDIR_PKG};

/* add the watches even if the directory does not exist */
for (guint i = 0; i < G_N_ELEMENTS(path_kinds); i++) {
GFileMonitor *monitor;
GFile *file;
g_autoptr(GError) error_local = NULL;
g_autofree gchar *base = fu_common_get_path(path_kinds[i]);
g_autofree gchar *fn = g_build_filename(base, "local.d", NULL);

file = g_file_new_for_path(fn);
monitor = g_file_monitor_directory(file, G_FILE_MONITOR_NONE, NULL, &error_local);
if (monitor == NULL) {
g_warning("failed to watch %s: %s", fn, error_local->message);
continue;
}
g_signal_connect(monitor,
"changed",
G_CALLBACK(fu_engine_local_metadata_changed_cb),
self);
g_ptr_array_add(self->local_monitors, g_steal_pointer(&monitor));
}

/* success */
return TRUE;
}

/**
* fu_engine_load:
* @self: a #FuEngine
Expand Down Expand Up @@ -6928,6 +7096,10 @@ fu_engine_load(FuEngine *self, FuEngineLoadFlags flags, GError **error)
return FALSE;
}

/* watch the local.d directories for changes */
if (!fu_engine_load_local_metadata_watches(self, error))
return FALSE;

/* add the "built-in" firmware types */
fu_context_add_firmware_gtype(self->ctx, "raw", FU_TYPE_FIRMWARE);
fu_context_add_firmware_gtype(self->ctx, "dfu", FU_TYPE_DFU_FIRMWARE);
Expand Down Expand Up @@ -7218,6 +7390,7 @@ fu_engine_init(FuEngine *self)
self->plugin_filter = g_ptr_array_new_with_free_func(g_free);
self->host_security_attrs = fu_security_attrs_new();
self->backends = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
self->local_monitors = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
self->runtime_versions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
self->compile_versions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);

Expand Down Expand Up @@ -7315,6 +7488,11 @@ fu_engine_finalize(GObject *obj)
{
FuEngine *self = FU_ENGINE(obj);

for (guint i = 0; i < self->local_monitors->len; i++) {
GFileMonitor *monitor = g_ptr_array_index(self->local_monitors, i);
g_file_monitor_cancel(monitor);
}

if (self->silo != NULL)
g_object_unref(self->silo);
if (self->query_component_by_guid != NULL)
Expand All @@ -7338,6 +7516,7 @@ fu_engine_finalize(GObject *obj)
g_object_unref(self->jcat_context);
g_ptr_array_unref(self->plugin_filter);
g_ptr_array_unref(self->backends);
g_ptr_array_unref(self->local_monitors);
g_hash_table_unref(self->runtime_versions);
g_hash_table_unref(self->compile_versions);
g_object_unref(self->plugin_list);
Expand Down

0 comments on commit f171082

Please sign in to comment.