/
githubmirror
/
fwupd
Обзор
Документация
Войти
/
githubmirror
/
fwupd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/fu-remote-list.c
890 строк
25 KB
Richard Hughes
trivial: Use FuContextLoadFlags when loading the remotes
09 июн 2026, 12:54
09 июн 2026, 12:54
981542e
Код
Авторство
О чём код?
/* * Copyright 2017 Richard Hughes <richard@hughsie.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define G_LOG_DOMAIN "FuRemoteList" #include "config.h" #include <glib/gi18n.h> #include <glib/gstdio.h> #include <xmlb.h> #ifdef HAVE_INOTIFY_H #include <errno.h> #include <sys/inotify.h> #endif #include "fwupd-remote-private.h" #include "fu-context-private.h" #include "fu-remote-list.h" #include "fu-remote.h" enum { SIGNAL_CHANGED, SIGNAL_ADDED, SIGNAL_LAST }; static guint signals[SIGNAL_LAST] = {0}; static gboolean fu_remote_list_reload(FuRemoteList *self, GError **error); static void fu_remote_list_finalize(GObject *obj); struct _FuRemoteList { GObject parent_instance; FuContext *ctx; GPtrArray *array; /* (element-type FwupdRemote) */ GPtrArray *monitors; /* (element-type GFileMonitor) */ gboolean testing_remote; gboolean fix_metadata_uri; XbSilo *silo; XbQuery *query; gchar *lvfs_metadata_format; }; G_DEFINE_TYPE(FuRemoteList, fu_remote_list, G_TYPE_OBJECT) static void fu_remote_list_emit_changed(FuRemoteList *self) { g_debug("::remote_list changed"); g_signal_emit(self, signals[SIGNAL_CHANGED], 0); } static void fu_remote_list_emit_added(FuRemoteList *self, FwupdRemote *remote) { g_debug("::remote_list changed"); g_signal_emit(self, signals[SIGNAL_ADDED], 0, remote); } void fu_remote_list_set_lvfs_metadata_format(FuRemoteList *self, const gchar *lvfs_metadata_format) { g_return_if_fail(FU_IS_REMOTE_LIST(self)); g_return_if_fail(lvfs_metadata_format != NULL); if (g_strcmp0(lvfs_metadata_format, self->lvfs_metadata_format) == 0) return; g_free(self->lvfs_metadata_format); self->lvfs_metadata_format = g_strdup(lvfs_metadata_format); } static void fu_remote_list_monitor_changed_cb(GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer user_data) { FuRemoteList *self = FU_REMOTE_LIST(user_data); g_autoptr(GError) error = NULL; g_autofree gchar *filename = g_file_get_path(file); /* ignore permission changes */ if (event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) return; g_info("%s changed, reloading all remotes", filename); if (!fu_remote_list_reload(self, &error)) g_warning("failed to rescan remotes: %s", error->message); fu_remote_list_emit_changed(self); } /* GLib only returns the very unhelpful "Unable to find default local file monitor type" * when /proc/sys/fs/inotify/max_user_instances is set too low; detect this and set a proper * error prefix to aid debugging when the daemon fails to start */ static void fu_remote_list_fixup_inotify_error(GError **error) /* nocheck:error */ { #ifdef HAVE_INOTIFY_H g_autofd int fd = -1; int wd; const gchar *fn = "/proc/sys/fs/inotify/max_user_instances"; fd = inotify_init(); if (fd == -1) { g_prefix_error(error, /* nocheck:error */ "Could not initialize inotify, check %s: ", fn); return; } wd = inotify_add_watch(fd, fn, IN_MODIFY); if (wd < 0) { if (errno == ENOSPC) { g_prefix_error(error, /* nocheck:error */ "No space for inotify, check %s: ", fn); } } else { inotify_rm_watch(fd, wd); } #endif } static gboolean fu_remote_list_add_inotify(FuRemoteList *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) { fu_remote_list_fixup_inotify_error(error); return FALSE; } g_signal_connect(G_FILE_MONITOR(monitor), "changed", G_CALLBACK(fu_remote_list_monitor_changed_cb), self); g_ptr_array_add(self->monitors, monitor); return TRUE; } static GString * _fwupd_remote_get_agreement_default(FwupdRemote *self, GError **error) { GString *str = g_string_new(NULL); /* this is designed as a fallback; the actual warning should ideally * come from the LVFS instance that is serving the remote */ g_string_append_printf(str, "<p>%s</p>", /* TRANSLATORS: show the user a warning */ _("Your distributor may not have verified any of " "the firmware updates for compatibility with your " "system or connected devices.")); g_string_append_printf(str, "<p>%s</p>", /* TRANSLATORS: show the user a warning */ _("Enabling this remote is done at your own risk.")); return str; } static GString * _fwupd_remote_get_agreement_for_app(FwupdRemote *self, XbNode *component, GError **error) { g_autofree gchar *tmp = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(XbNode) n = NULL; /* manually find the first agreement section */ n = xb_node_query_first(component, "agreement/agreement_section/description/*", &error_local); if (n == NULL) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "No agreement description found: %s", error_local->message); return NULL; } tmp = xb_node_export(n, XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS, error); if (tmp == NULL) return NULL; return g_string_new(tmp); } static gchar * _fwupd_remote_build_component_id(FwupdRemote *remote) { return g_strdup_printf("org.freedesktop.fwupd.remotes.%s", fwupd_remote_get_id(remote)); } static gchar * fu_remote_list_get_last_ext(const gchar *filename) { gchar *tmp; g_return_val_if_fail(filename != NULL, NULL); tmp = g_strrstr(filename, "."); if (tmp == NULL) return NULL; return g_strdup(tmp + 1); } static gboolean fu_remote_list_remote_filename_cache_fn_is_obsolete(FuRemoteList *self, const gchar *fn) { g_autofree gchar *ext = fu_remote_list_get_last_ext(fn); g_autofree gchar *basename = g_path_get_basename(fn); /* fwupd >= 2.0.0 calls this firmware.xml.* so that we can validate with jcat-tool */ if (g_str_has_prefix(basename, "metadata.")) return TRUE; /* in a format that we no longer use */ if (g_strcmp0(ext, "jcat") == 0) return FALSE; return g_strcmp0(ext, self->lvfs_metadata_format) != 0; } static gboolean fu_remote_list_cleanup_lvfs_remote(FuRemoteList *self, FwupdRemote *remote, GError **error) { const gchar *fn_cache = fwupd_remote_get_filename_cache(remote); g_autofree gchar *dirname = NULL; g_autoptr(GPtrArray) files = NULL; /* sanity check */ if (fn_cache == NULL) return TRUE; if (self->lvfs_metadata_format == NULL) return TRUE; /* get all files */ dirname = g_path_get_dirname(fn_cache); files = fu_path_get_files(dirname, NULL); if (files == NULL) return TRUE; /* delete any obsolete ones */ for (guint i = 0; i < files->len; i++) { const gchar *fn = g_ptr_array_index(files, i); if (fu_remote_list_remote_filename_cache_fn_is_obsolete(self, fn)) { g_info("deleting obsolete %s", fn); if (g_unlink(fn) == -1) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to delete obsolete %s", fn); return FALSE; } } } /* success */ return TRUE; } void fu_remote_list_add_remote(FuRemoteList *self, FwupdRemote *remote) { g_return_if_fail(FU_IS_REMOTE_LIST(self)); g_return_if_fail(FWUPD_IS_REMOTE(remote)); fu_remote_list_emit_added(self, remote); g_ptr_array_add(self->array, g_object_ref(remote)); } static gboolean fu_remote_list_is_remote_origin_lvfs(FwupdRemote *remote) { if (fwupd_remote_get_id(remote) != NULL && g_strstr_len(fwupd_remote_get_id(remote), -1, "lvfs") != NULL) return TRUE; if (fwupd_remote_get_metadata_uri(remote) != NULL && g_strstr_len(fwupd_remote_get_metadata_uri(remote), -1, "fwupd.org") != NULL) return TRUE; return FALSE; } static gboolean fu_remote_list_add_for_file(FuRemoteList *self, const gchar *filename, GError **error) { const gchar *remotesdir; g_autoptr(FwupdRemote) remote = fu_remote_new(); g_autoptr(FwupdRemote) remote_tmp = NULL; /* set directory to store data */ remotesdir = fu_context_get_path(self->ctx, FU_PATH_KIND_LOCALSTATEDIR_METADATA, error); if (remotesdir == NULL) return FALSE; fwupd_remote_set_remotes_dir(remote, remotesdir); /* load from keyfile */ g_info("loading remote from %s", filename); if (!fu_remote_load_from_filename(remote, filename, NULL, error)) { g_prefix_error(error, "failed to load %s: ", filename); return FALSE; } /* does it already exist */ remote_tmp = fu_remote_list_get_by_id(self, fwupd_remote_get_id(remote), NULL); if (remote_tmp != NULL) { g_debug("remote %s already added from %s", fwupd_remote_get_id(remote), fwupd_remote_get_filename_source(remote_tmp)); return TRUE; } /* auto-fix before setup */ if (self->fix_metadata_uri && fu_remote_list_is_remote_origin_lvfs(remote)) { const gchar *metadata_url = fwupd_remote_get_metadata_uri(remote); g_autofree gchar *ext = fu_remote_list_get_last_ext(metadata_url); if (g_strcmp0(ext, self->lvfs_metadata_format) != 0) { g_autoptr(GString) str = g_string_new(metadata_url); g_autofree gchar *metadata_ext = g_strdup_printf(".%s", self->lvfs_metadata_format); g_string_replace(str, ".gz", metadata_ext, 0); g_string_replace(str, ".xz", metadata_ext, 0); g_string_replace(str, ".zst", metadata_ext, 0); g_info("auto-fixing remote %s MetadataURI from %s to %s", fwupd_remote_get_id(remote), metadata_url, str->str); fwupd_remote_set_metadata_uri(remote, str->str); } } /* load remote */ if (!fwupd_remote_setup(remote, error)) { g_prefix_error(error, "failed to setup %s: ", filename); return FALSE; } /* delete the obsolete files if the remote is now set up to use a new metadata format */ if (fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_ENABLED) && fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_DOWNLOAD && fu_remote_list_is_remote_origin_lvfs(remote)) { if (!fu_remote_list_cleanup_lvfs_remote(self, remote, error)) return FALSE; } #ifndef _WIN32 /* if the remote has a username or password it should be readable only by owner */ if (fwupd_remote_get_username(remote) != NULL || fwupd_remote_get_password(remote) != NULL) { if (g_chmod(filename, 0600) != 0) g_warning("failed to ensure permissions on %s", filename); } #endif /* watch the remote_list file and the XML file itself */ if (!fu_remote_list_add_inotify(self, filename, error)) return FALSE; if (!fu_remote_list_add_inotify(self, fwupd_remote_get_filename_cache(remote), error)) return FALSE; /* try to find a custom agreement, falling back to a generic warning */ if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_DOWNLOAD) { g_autofree gchar *component_id = _fwupd_remote_build_component_id(remote); g_autoptr(GString) agreement_markup = NULL; g_autoptr(XbNode) component = NULL; struct { const gchar *key; const gchar *search; const gchar *fallback; } distro_kv[] = { { G_OS_INFO_KEY_NAME, "$OS_RELEASE:NAME$", "this distribution", }, { G_OS_INFO_KEY_BUG_REPORT_URL, "$OS_RELEASE:BUG_REPORT_URL$", "https://github.com/fwupd/fwupd/issues", }, }; if (self->query != NULL) { g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, component_id, NULL); component = xb_silo_query_first_with_context(self->silo, self->query, &context, NULL); } if (component != NULL) { agreement_markup = _fwupd_remote_get_agreement_for_app(remote, component, error); } else { agreement_markup = _fwupd_remote_get_agreement_default(remote, error); } if (agreement_markup == NULL) return FALSE; /* replace any dynamic values from os-release */ for (guint i = 0; i < G_N_ELEMENTS(distro_kv); i++) { g_autofree gchar *os_replace = g_get_os_info(distro_kv[i].key); if (os_replace == NULL) os_replace = g_strdup(distro_kv[i].fallback); g_string_replace(agreement_markup, distro_kv[i].search, os_replace, 0); } } /* set mtime */ if (!fwupd_remote_ensure_mtime(remote, error)) return FALSE; fu_remote_list_add_remote(self, remote); /* success */ return TRUE; } static gboolean fu_remote_list_add_for_path(FuRemoteList *self, const gchar *path, GError **error) { g_autofree gchar *path_remotes = NULL; g_autoptr(GPtrArray) paths = NULL; path_remotes = g_build_filename(path, "remotes.d", NULL); if (!g_file_test(path_remotes, G_FILE_TEST_EXISTS)) { g_debug("path %s does not exist", path_remotes); return TRUE; } if (!fu_remote_list_add_inotify(self, path_remotes, error)) return FALSE; paths = fu_path_glob(path_remotes, "*.conf", NULL); if (paths == NULL) return TRUE; for (guint i = 0; i < paths->len; i++) { const gchar *filename = g_ptr_array_index(paths, i); if (g_str_has_suffix(filename, "fwupd-tests.conf") && !self->testing_remote) continue; if (!fu_remote_list_add_for_file(self, filename, error)) return FALSE; } return TRUE; } gboolean fu_remote_list_set_key_value(FuRemoteList *self, const gchar *remote_id, const gchar *key, const gchar *value, GError **error) { const gchar *filename; g_autofree gchar *filename_new = NULL; g_autofree gchar *value_old = NULL; g_autoptr(FwupdRemote) remote = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GKeyFile) keyfile = g_key_file_new(); /* check remote is valid */ remote = fu_remote_list_get_by_id(self, remote_id, error); if (remote == NULL) return FALSE; /* modify the remote */ filename = fwupd_remote_get_filename_source(remote); if (!g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_KEEP_COMMENTS, error)) { g_prefix_error(error, "failed to load %s: ", filename); return FALSE; } value_old = g_key_file_get_string(keyfile, "fwupd Remote", key, NULL); if (g_strcmp0(value_old, value) == 0) return TRUE; g_key_file_set_string(keyfile, "fwupd Remote", key, value); /* try existing file first, then call back to the mutable location */ if (!g_key_file_save_to_file(keyfile, filename, &error_local)) { if (g_error_matches(error_local, G_FILE_ERROR, G_FILE_ERROR_PERM)) { g_autofree gchar *basename = g_path_get_basename(filename); filename_new = fu_context_build_filename(self->ctx, error, FU_PATH_KIND_LOCALSTATEDIR_PKG, "remotes.d", basename, NULL); if (filename_new == NULL) return FALSE; if (!fu_path_mkdir_parent(filename_new, error)) return FALSE; g_info("falling back from %s to %s", filename, filename_new); if (!g_key_file_save_to_file(keyfile, filename_new, error)) return FALSE; } else { g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } } else { filename_new = g_strdup(filename); } /* reload values */ if (!fu_remote_load_from_filename(remote, filename_new, NULL, error)) { g_prefix_error(error, "failed to load %s: ", filename_new); return FALSE; } fu_remote_list_emit_changed(self); return TRUE; } static gint fu_remote_list_sort_cb(gconstpointer a, gconstpointer b) { FwupdRemote *remote_a = *((FwupdRemote **)a); FwupdRemote *remote_b = *((FwupdRemote **)b); /* use priority first */ if (fwupd_remote_get_priority(remote_a) < fwupd_remote_get_priority(remote_b)) return 1; if (fwupd_remote_get_priority(remote_a) > fwupd_remote_get_priority(remote_b)) return -1; /* fall back to name */ return g_strcmp0(fwupd_remote_get_id(remote_a), fwupd_remote_get_id(remote_b)); } static guint fu_remote_list_depsolve_order_before(FuRemoteList *self) { guint cnt = 0; for (guint i = 0; i < self->array->len; i++) { FwupdRemote *remote = g_ptr_array_index(self->array, i); gchar **order = fwupd_remote_get_order_before(remote); if (order == NULL) continue; for (guint j = 0; order[j] != NULL; j++) { g_autoptr(FwupdRemote) remote2 = NULL; if (g_strcmp0(order[j], fwupd_remote_get_id(remote)) == 0) { g_debug("ignoring self-dep remote %s", order[j]); continue; } remote2 = fu_remote_list_get_by_id(self, order[j], NULL); if (remote2 == NULL) { g_debug("ignoring unfound remote %s", order[j]); continue; } if (fwupd_remote_get_priority(remote) > fwupd_remote_get_priority(remote2)) continue; g_debug("ordering %s=%s+1", fwupd_remote_get_id(remote), fwupd_remote_get_id(remote2)); fwupd_remote_set_priority(remote, fwupd_remote_get_priority(remote2) + 1); cnt++; } } return cnt; } static guint fu_remote_list_depsolve_order_after(FuRemoteList *self) { guint cnt = 0; for (guint i = 0; i < self->array->len; i++) { FwupdRemote *remote = g_ptr_array_index(self->array, i); gchar **order = fwupd_remote_get_order_after(remote); if (order == NULL) continue; for (guint j = 0; order[j] != NULL; j++) { g_autoptr(FwupdRemote) remote2 = NULL; if (g_strcmp0(order[j], fwupd_remote_get_id(remote)) == 0) { g_debug("ignoring self-dep remote %s", order[j]); continue; } remote2 = fu_remote_list_get_by_id(self, order[j], NULL); if (remote2 == NULL) { g_debug("ignoring unfound remote %s", order[j]); continue; } if (fwupd_remote_get_priority(remote) < fwupd_remote_get_priority(remote2)) continue; g_debug("ordering %s=%s+1", fwupd_remote_get_id(remote2), fwupd_remote_get_id(remote)); fwupd_remote_set_priority(remote2, fwupd_remote_get_priority(remote) + 1); cnt++; } } return cnt; } static gboolean fu_remote_list_reload(FuRemoteList *self, GError **error) { guint depsolve_check; const gchar *remotesdir; const gchar *remotesdir_mut; const gchar *remotesdir_immut; g_autoptr(GString) str = g_string_new(NULL); /* clear */ g_ptr_array_set_size(self->array, 0); g_ptr_array_set_size(self->monitors, 0); /* search mutable, and then fall back to /etc and immutable */ remotesdir_mut = fu_context_get_path(self->ctx, FU_PATH_KIND_LOCALSTATEDIR_PKG, NULL); if (remotesdir_mut != NULL) { if (!fu_remote_list_add_for_path(self, remotesdir_mut, error)) return FALSE; } remotesdir = fu_context_get_path(self->ctx, FU_PATH_KIND_SYSCONFDIR_PKG, NULL); if (remotesdir != NULL) { if (!fu_remote_list_add_for_path(self, remotesdir, error)) return FALSE; } remotesdir_immut = fu_context_get_path(self->ctx, FU_PATH_KIND_DATADIR_PKG, NULL); if (remotesdir_immut != NULL) { if (!fu_remote_list_add_for_path(self, remotesdir_immut, error)) return FALSE; } /* depsolve */ for (depsolve_check = 0; depsolve_check < 100; depsolve_check++) { guint cnt = 0; cnt += fu_remote_list_depsolve_order_before(self); cnt += fu_remote_list_depsolve_order_after(self); if (cnt == 0) break; } if (depsolve_check == 100) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Cannot depsolve remotes ordering"); return FALSE; } /* order these by priority, then name */ g_ptr_array_sort(self->array, fu_remote_list_sort_cb); /* print to the console */ for (guint i = 0; i < self->array->len; i++) { FwupdRemote *remote = g_ptr_array_index(self->array, i); if (!fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_ENABLED)) continue; if (str->len > 0) g_string_append(str, ", "); g_string_append_printf(str, "%s[%i]", fwupd_remote_get_id(remote), fwupd_remote_get_priority(remote)); } g_info("enabled remotes: %s", str->str); /* success */ return TRUE; } static gboolean fu_remote_list_load_metainfos(FuRemoteList *self, XbBuilder *builder, GError **error) { const gchar *fn; g_autofree gchar *metainfo_path = NULL; g_autoptr(GDir) dir = NULL; /* pkg metainfo dir */ metainfo_path = fu_context_build_filename(self->ctx, error, FU_PATH_KIND_DATADIR_PKG, "metainfo", NULL); if (metainfo_path == NULL) return FALSE; if (!g_file_test(metainfo_path, G_FILE_TEST_EXISTS)) return TRUE; g_debug("loading %s", metainfo_path); dir = g_dir_open(metainfo_path, 0, error); if (dir == NULL) return FALSE; while ((fn = g_dir_read_name(dir)) != NULL) { if (g_str_has_suffix(fn, ".metainfo.xml")) { g_autofree gchar *filename = g_build_filename(metainfo_path, fn, NULL); g_autoptr(GFile) file = g_file_new_for_path(filename); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); if (!xb_builder_source_load_file(source, file, XB_BUILDER_SOURCE_FLAG_NONE, NULL, error)) { fwupd_error_convert(error); return FALSE; } xb_builder_import_source(builder, source); } } return TRUE; } gboolean fu_remote_list_set_testing_remote_enabled(FuRemoteList *self, gboolean enable, GError **error) { g_return_val_if_fail(FU_IS_REMOTE_LIST(self), FALSE); /* not yet initialized */ if (self->silo == NULL) return TRUE; if (self->testing_remote == enable) return TRUE; self->testing_remote = enable; if (!fu_remote_list_reload(self, error)) return FALSE; fu_remote_list_emit_changed(self); return TRUE; } gboolean fu_remote_list_load(FuRemoteList *self, FuContextLoadFlags flags, GError **error) { const gchar *const *locales = g_get_language_names(); g_autoptr(GError) error_local = NULL; g_autoptr(GFile) xmlb = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_SINGLE_LANG | XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID; g_return_val_if_fail(FU_IS_REMOTE_LIST(self), FALSE); g_return_val_if_fail(self->silo == NULL, FALSE); /* enable testing only remotes */ if (fu_context_get_config_bool(self->ctx, "TestDevices")) self->testing_remote = TRUE; /* autofix on reload too */ if ((flags & FU_CONTEXT_FLAG_READONLY_FS) == 0) self->fix_metadata_uri = TRUE; /* load AppStream about the remote_list */ if (!fu_remote_list_load_metainfos(self, builder, error)) return FALSE; /* add the locales, which is really only going to be 'C' or 'en' */ for (guint i = 0; locales[i] != NULL; i++) xb_builder_add_locale(builder, locales[i]); /* on a read-only filesystem don't care about the cache GUID */ if (flags & FU_CONTEXT_FLAG_READONLY_FS) compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID; /* build the metainfo silo */ if (flags & FU_CONTEXT_FLAG_NO_CACHE) { g_autoptr(GFileIOStream) iostr = NULL; xmlb = g_file_new_tmp(NULL, &iostr, error); if (xmlb == NULL) return FALSE; } else { g_autofree gchar *xmlbfn = NULL; xmlbfn = fu_context_build_filename(self->ctx, error, FU_PATH_KIND_CACHEDIR_PKG, "metainfo.xmlb", NULL); if (xmlbfn == NULL) return FALSE; xmlb = g_file_new_for_path(xmlbfn); } self->silo = xb_builder_ensure(builder, xmlb, compile_flags, NULL, error); if (self->silo == NULL) return FALSE; /* if the silo is empty the query will fail to be created */ self->query = xb_query_new_full(self->silo, "component/id[text()=?]/..", XB_QUERY_FLAG_OPTIMIZE, &error_local); if (self->query == NULL) g_debug("failed to prepare query: %s", error_local->message); /* load remote_list */ return fu_remote_list_reload(self, error); } GPtrArray * fu_remote_list_get_all(FuRemoteList *self) { g_return_val_if_fail(FU_IS_REMOTE_LIST(self), NULL); return g_ptr_array_ref(self->array); } FwupdRemote * fu_remote_list_get_by_id(FuRemoteList *self, const gchar *remote_id, GError **error) { g_return_val_if_fail(FU_IS_REMOTE_LIST(self), NULL); g_return_val_if_fail(remote_id != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); for (guint i = 0; i < self->array->len; i++) { FwupdRemote *remote = g_ptr_array_index(self->array, i); if (g_strcmp0(remote_id, fwupd_remote_get_id(remote)) == 0) return g_object_ref(remote); } g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "remote %s not found", remote_id); return NULL; } static void fu_remote_list_class_init(FuRemoteListClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = fu_remote_list_finalize; /** * FuRemoteList::changed: * @self: the #FuRemoteList instance that emitted the signal * * The ::changed signal is emitted when the list of remotes has * changed, for instance when a remote has been added or removed. **/ signals[SIGNAL_CHANGED] = g_signal_new("changed", G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * FuRemoteList::added: * @self: the #FuRemoteList instance that emitted the signal * @remote: the #FwupdRemote that was added **/ signals[SIGNAL_ADDED] = g_signal_new("added", G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, FWUPD_TYPE_REMOTE); } static void fu_remote_list_monitor_unref(GFileMonitor *monitor) { g_file_monitor_cancel(monitor); g_object_unref(monitor); } static void fu_remote_list_init(FuRemoteList *self) { self->array = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); self->monitors = g_ptr_array_new_with_free_func((GDestroyNotify)fu_remote_list_monitor_unref); } static void fu_remote_list_finalize(GObject *obj) { FuRemoteList *self = FU_REMOTE_LIST(obj); if (self->ctx != NULL) g_object_unref(self->ctx); if (self->silo != NULL) g_object_unref(self->silo); if (self->query != NULL) g_object_unref(self->query); g_ptr_array_unref(self->array); g_ptr_array_unref(self->monitors); g_free(self->lvfs_metadata_format); G_OBJECT_CLASS(fu_remote_list_parent_class)->finalize(obj); } FuRemoteList * fu_remote_list_new(FuContext *ctx) { FuRemoteList *self; g_return_val_if_fail(FU_IS_CONTEXT(ctx), NULL); self = g_object_new(FU_TYPE_REMOTE_LIST, NULL); self->ctx = g_object_ref(ctx); return FU_REMOTE_LIST(self); }