diff --git a/po/POTFILES.in b/po/POTFILES.in index 1c13355c7..c459d7eb4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -6,11 +6,11 @@ plugins/tpm-eventlog/fu-tpm-eventlog.c plugins/uefi/fu-plugin-uefi.c plugins/uefi/fu-uefi-tool.c src/fu-agent.c -src/fu-config.c src/fu-debug.c src/fu-main.c src/fu-offline.c src/fu-progressbar.c +src/fu-remote-list.c src/fu-tool.c src/fu-util.c src/fu-util-common.c diff --git a/src/fu-config.c b/src/fu-config.c index 38b8b48f7..ec3f54ef5 100644 --- a/src/fu-config.c +++ b/src/fu-config.c @@ -8,19 +8,12 @@ #include "config.h" -#include #include #include -#include #include "fu-common.h" #include "fu-config.h" -#include "fwupd-common.h" -#include "fwupd-error.h" -#include "fwupd-remote-private.h" - - enum { SIGNAL_CHANGED, SIGNAL_LAST @@ -33,16 +26,12 @@ static void fu_config_finalize (GObject *obj); struct _FuConfig { GObject parent_instance; - GKeyFile *keyfile; - GPtrArray *remotes; - GPtrArray *monitors; - GPtrArray *blacklist_devices; - GPtrArray *blacklist_plugins; - GPtrArray *approved_firmware; + GFileMonitor *monitor; + GPtrArray *blacklist_devices; /* (element-type utf-8) */ + GPtrArray *blacklist_plugins; /* (element-type utf-8) */ + GPtrArray *approved_firmware; /* (element-type utf-8) */ guint64 archive_size_max; guint idle_timeout; - XbSilo *silo; - GHashTable *os_release; gchar *config_file; }; @@ -55,369 +44,25 @@ fu_config_emit_changed (FuConfig *self) g_signal_emit (self, signals[SIGNAL_CHANGED], 0); } -static GPtrArray * -fu_config_get_config_paths (void) -{ - GPtrArray *paths = g_ptr_array_new_with_free_func (g_free); - const gchar *remotes_dir; - const gchar *system_prefixlibdir = "/usr/lib/fwupd"; - g_autofree gchar *configdir = NULL; - - /* only set by the self test program */ - remotes_dir = g_getenv ("FU_SELF_TEST_REMOTES_DIR"); - if (remotes_dir != NULL) { - g_ptr_array_add (paths, g_strdup (remotes_dir)); - return paths; - } - - /* use sysconfig, and then fall back to /etc */ - configdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR_PKG); - if (g_file_test (configdir, G_FILE_TEST_EXISTS)) - g_ptr_array_add (paths, g_steal_pointer (&configdir)); - - /* add in system-wide locations */ - if (g_file_test (system_prefixlibdir, G_FILE_TEST_EXISTS)) - g_ptr_array_add (paths, g_strdup (system_prefixlibdir)); - - return paths; -} - -static void -fu_config_monitor_changed_cb (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) -{ - FuConfig *self = FU_CONFIG (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_config_load (self, FU_CONFIG_LOAD_FLAG_NONE, &error)) - g_warning ("failed to rescan config: %s", error->message); - fu_config_emit_changed (self); -} - -static guint64 -fu_config_get_remote_mtime (FuConfig *self, FwupdRemote *remote) -{ - g_autoptr(GFile) file = NULL; - g_autoptr(GFileInfo) info = NULL; - - file = g_file_new_for_path (fwupd_remote_get_filename_cache (remote)); - if (!g_file_query_exists (file, NULL)) - return G_MAXUINT64; - info = g_file_query_info (file, - G_FILE_ATTRIBUTE_TIME_MODIFIED, - G_FILE_QUERY_INFO_NONE, - NULL, NULL); - if (info == NULL) - return G_MAXUINT64; - return g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED); -} - static gboolean -fu_config_add_inotify (FuConfig *self, const gchar *filename, GError **error) +fu_config_reload (FuConfig *self, 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_config_monitor_changed_cb), self); - g_ptr_array_add (self->monitors, monitor); - return TRUE; -} - -static GString * -fu_config_get_remote_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, "%s\n", - /* 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, "%s\n", - /* TRANSLATORS: show the user a warning */ - _("Enabling this remote is done at your own risk.")); - return str; -} - -static GString * -fu_config_get_remote_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 * -fu_config_build_remote_component_id (FwupdRemote *remote) -{ - return g_strdup_printf ("org.freedesktop.fwupd.remotes.%s", - fwupd_remote_get_id (remote)); -} - -static gboolean -fu_config_add_remotes_for_path (FuConfig *self, const gchar *path, GError **error) -{ - const gchar *tmp; - g_autofree gchar *path_remotes = NULL; - g_autoptr(GDir) dir = 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_config_add_inotify (self, path_remotes, error)) - return FALSE; - dir = g_dir_open (path_remotes, 0, error); - if (dir == NULL) - return FALSE; - while ((tmp = g_dir_read_name (dir)) != NULL) { - g_autofree gchar *filename = g_build_filename (path_remotes, tmp, NULL); - g_autoptr(FwupdRemote) remote = fwupd_remote_new (); - g_autofree gchar *localstatedir = NULL; - g_autofree gchar *remotesdir = NULL; - - /* skip invalid files */ - if (!g_str_has_suffix (tmp, ".conf")) { - g_debug ("skipping invalid file %s", filename); - continue; - } - - /* set directory to store data */ - localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); - remotesdir = g_build_filename (localstatedir, "remotes.d", NULL); - fwupd_remote_set_remotes_dir (remote, remotesdir); - - /* load from keyfile */ - g_debug ("loading config from %s", filename); - if (!fwupd_remote_load_from_filename (remote, filename, - NULL, error)) { - g_prefix_error (error, "failed to load %s: ", filename); - return FALSE; - } - - /* watch the config file and the XML file itself */ - if (!fu_config_add_inotify (self, filename, error)) - return FALSE; - if (!fu_config_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_autoptr(GString) agreement_markup = NULL; - g_autofree gchar *component_id = fu_config_build_remote_component_id (remote); - g_autoptr(XbNode) component = NULL; - g_autofree gchar *xpath = NULL; - - xpath = g_strdup_printf ("component/id[text()='%s']/..", component_id); - component = xb_silo_query_first (self->silo, xpath, NULL); - if (component != NULL) { - agreement_markup = fu_config_get_remote_agreement_for_app (remote, component, error); - } else { - agreement_markup = fu_config_get_remote_agreement_default (remote, error); - } - if (agreement_markup == NULL) - return FALSE; - - /* replace any dynamic values from os-release */ - tmp = g_hash_table_lookup (self->os_release, "NAME"); - if (tmp == NULL) - tmp = "this distribution"; - fu_common_string_replace (agreement_markup, "$OS_RELEASE:NAME$", tmp); - tmp = g_hash_table_lookup (self->os_release, "BUG_REPORT_URL"); - if (tmp == NULL) - tmp = "https://github.com/fwupd/fwupd/issues"; - fu_common_string_replace (agreement_markup, "$OS_RELEASE:BUG_REPORT_URL$", tmp); - fwupd_remote_set_agreement (remote, agreement_markup->str); - } - - /* set mtime */ - fwupd_remote_set_mtime (remote, fu_config_get_remote_mtime (self, remote)); - g_ptr_array_add (self->remotes, g_steal_pointer (&remote)); - } - return TRUE; -} - -static gint -fu_config_remote_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 FwupdRemote * -fu_config_get_remote_by_id_noref (GPtrArray *remotes, const gchar *remote_id) -{ - for (guint i = 0; i < remotes->len; i++) { - FwupdRemote *remote = g_ptr_array_index (remotes, i); - if (g_strcmp0 (remote_id, fwupd_remote_get_id (remote)) == 0) - return remote; - } - return NULL; -} - -static guint -fu_config_remotes_depsolve_with_direction (FuConfig *self, gint inc) -{ - guint cnt = 0; - for (guint i = 0; i < self->remotes->len; i++) { - FwupdRemote *remote = g_ptr_array_index (self->remotes, i); - gchar **order = inc < 0 ? fwupd_remote_get_order_after (remote) : - fwupd_remote_get_order_before (remote); - if (order == NULL) - continue; - for (guint j = 0; order[j] != NULL; j++) { - FwupdRemote *remote2; - if (g_strcmp0 (order[j], fwupd_remote_get_id (remote)) == 0) { - g_debug ("ignoring self-dep remote %s", order[j]); - continue; - } - remote2 = fu_config_get_remote_by_id_noref (self->remotes, order[j]); - 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+%i", - fwupd_remote_get_id (remote), - fwupd_remote_get_id (remote2), - inc); - fwupd_remote_set_priority (remote, fwupd_remote_get_priority (remote2) + inc); - - /* increment changes counter */ - cnt++; - } - } - return cnt; -} - -static gboolean -fu_config_load_remotes (FuConfig *self, GError **error) -{ - guint depsolve_check; - g_autoptr(GPtrArray) paths = NULL; - - /* get a list of all config paths */ - paths = fu_config_get_config_paths (); - if (paths->len == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No search paths found"); - return FALSE; - } - - /* look for all remotes */ - for (guint i = 0; i < paths->len; i++) { - const gchar *path = g_ptr_array_index (paths, i); - g_debug ("using config path of %s", path); - if (!fu_config_add_remotes_for_path (self, path, error)) - return FALSE; - } - - /* depsolve */ - for (depsolve_check = 0; depsolve_check < 100; depsolve_check++) { - guint cnt = 0; - cnt += fu_config_remotes_depsolve_with_direction (self, 1); - cnt += fu_config_remotes_depsolve_with_direction (self, -1); - 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->remotes, fu_config_remote_sort_cb); - - /* success */ - return TRUE; -} - -gboolean -fu_config_modify_and_save (FuConfig *self, const gchar *key, const gchar *value, GError **error) -{ - g_key_file_set_string (self->keyfile, "fwupd", key, value); - return g_key_file_save_to_file (self->keyfile, self->config_file, error); -} - -static gboolean -fu_config_load_from_file (FuConfig *self, const gchar *config_file, - GError **error) -{ - GFileMonitor *monitor; guint64 archive_size_max; guint idle_timeout; g_auto(GStrv) approved_firmware = NULL; g_auto(GStrv) devices = NULL; g_auto(GStrv) plugins = NULL; - g_autoptr(GFile) file = NULL; g_autofree gchar *domains = NULL; + g_autoptr(GKeyFile) keyfile = g_key_file_new (); - /* ensure empty in case we're called from a monitor change */ - g_ptr_array_set_size (self->blacklist_devices, 0); - g_ptr_array_set_size (self->blacklist_plugins, 0); - g_ptr_array_set_size (self->approved_firmware, 0); - g_ptr_array_set_size (self->monitors, 0); - g_ptr_array_set_size (self->remotes, 0); - - g_debug ("loading config values from %s", config_file); - if (!g_key_file_load_from_file (self->keyfile, config_file, - G_KEY_FILE_KEEP_COMMENTS, error)) + g_debug ("loading config values from %s", self->config_file); + if (!g_key_file_load_from_file (keyfile, self->config_file, + G_KEY_FILE_NONE, error)) return FALSE; - /* set up a notify watch */ - file = g_file_new_for_path (config_file); - monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, error); - if (monitor == NULL) - return FALSE; - g_signal_connect (monitor, "changed", - G_CALLBACK (fu_config_monitor_changed_cb), self); - g_ptr_array_add (self->monitors, monitor); - /* get blacklisted devices */ - devices = g_key_file_get_string_list (self->keyfile, + g_ptr_array_set_size (self->blacklist_devices, 0); + devices = g_key_file_get_string_list (keyfile, "fwupd", "BlacklistDevices", NULL, /* length */ @@ -430,7 +75,8 @@ fu_config_load_from_file (FuConfig *self, const gchar *config_file, } /* get blacklisted plugins */ - plugins = g_key_file_get_string_list (self->keyfile, + g_ptr_array_set_size (self->blacklist_plugins, 0); + plugins = g_key_file_get_string_list (keyfile, "fwupd", "BlacklistPlugins", NULL, /* length */ @@ -443,7 +89,8 @@ fu_config_load_from_file (FuConfig *self, const gchar *config_file, } /* get approved firmware */ - approved_firmware = g_key_file_get_string_list (self->keyfile, + g_ptr_array_set_size (self->approved_firmware, 0); + approved_firmware = g_key_file_get_string_list (keyfile, "fwupd", "ApprovedFirmware", NULL, /* length */ @@ -456,7 +103,7 @@ fu_config_load_from_file (FuConfig *self, const gchar *config_file, } /* get maximum archive size, defaulting to something sane */ - archive_size_max = g_key_file_get_uint64 (self->keyfile, + archive_size_max = g_key_file_get_uint64 (keyfile, "fwupd", "ArchiveSizeMax", NULL); @@ -464,7 +111,7 @@ fu_config_load_from_file (FuConfig *self, const gchar *config_file, self->archive_size_max = archive_size_max *= 0x100000; /* get idle timeout */ - idle_timeout = g_key_file_get_uint64 (self->keyfile, + idle_timeout = g_key_file_get_uint64 (keyfile, "fwupd", "IdleTimeout", NULL); @@ -472,7 +119,7 @@ fu_config_load_from_file (FuConfig *self, const gchar *config_file, self->idle_timeout = idle_timeout; /* get the domains to run in verbose */ - domains = g_key_file_get_string (self->keyfile, + domains = g_key_file_get_string (keyfile, "fwupd", "VerboseDomains", NULL); @@ -482,101 +129,65 @@ fu_config_load_from_file (FuConfig *self, const gchar *config_file, return TRUE; } -static gboolean -fu_config_load_metainfos (XbBuilder *builder, GError **error) +static void +fu_config_monitor_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) { - const gchar *fn; - g_autofree gchar *datadir = NULL; - g_autofree gchar *metainfo_path = NULL; - g_autoptr(GDir) dir = NULL; - - /* pkg metainfo dir */ - datadir = fu_common_get_path (FU_PATH_KIND_DATADIR_PKG); - metainfo_path = g_build_filename (datadir, "metainfo", NULL); - 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)) - return FALSE; - xb_builder_import_source (builder, source); - } - } - return TRUE; + FuConfig *self = FU_CONFIG (user_data); + g_autoptr(GError) error = NULL; + g_debug ("%s changed, reloading all configs", self->config_file); + if (!fu_config_reload (self, &error)) + g_warning ("failed to rescan daemon config: %s", error->message); + fu_config_emit_changed (self); } gboolean -fu_config_load (FuConfig *self, FuConfigLoadFlags flags, GError **error) +fu_config_set_key_value (FuConfig *self, const gchar *key, const gchar *value, GError **error) +{ + g_autoptr(GKeyFile) keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, self->config_file, + G_KEY_FILE_KEEP_COMMENTS, error)) + return FALSE; + g_key_file_set_string (keyfile, "fwupd", key, value); + if (!g_key_file_save_to_file (keyfile, self->config_file, error)) + return FALSE; + return fu_config_reload (self, error); +} + +gboolean +fu_config_load (FuConfig *self, GError **error) { - const gchar *const *locales = g_get_language_names (); g_autofree gchar *configdir = NULL; - g_autofree gchar *cachedirpkg = NULL; - g_autofree gchar *xmlbfn = 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_autoptr(GFile) file = NULL; g_return_val_if_fail (FU_IS_CONFIG (self), FALSE); + g_return_val_if_fail (self->config_file == NULL, FALSE); /* load the main daemon config file */ configdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR_PKG); self->config_file = g_build_filename (configdir, "daemon.conf", NULL); if (g_file_test (self->config_file, G_FILE_TEST_EXISTS)) { - if (!fu_config_load_from_file (self, self->config_file, error)) + if (!fu_config_reload (self, error)) return FALSE; } else { g_warning ("Daemon configuration %s not found", self->config_file); } - /* load AppStream about the remotes */ - self->os_release = fwupd_get_os_release (error); - if (self->os_release == NULL) - return FALSE; - if (!fu_config_load_metainfos (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_CONFIG_LOAD_FLAG_READONLY_FS) - compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID; - - /* build the metainfo silo */ - cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG); - xmlbfn = g_build_filename (cachedirpkg, "metainfo.xmlb", NULL); - xmlb = g_file_new_for_path (xmlbfn); - self->silo = xb_builder_ensure (builder, xmlb, compile_flags, NULL, error); - if (self->silo == NULL) - return FALSE; - - /* load remotes */ - if (!fu_config_load_remotes (self, error)) + /* set up a notify watch */ + file = g_file_new_for_path (self->config_file); + self->monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, error); + if (self->monitor == NULL) return FALSE; + g_signal_connect (self->monitor, "changed", + G_CALLBACK (fu_config_monitor_changed_cb), self); /* success */ return TRUE; } -GPtrArray * -fu_config_get_remotes (FuConfig *self) -{ - g_return_val_if_fail (FU_IS_CONFIG (self), NULL); - return self->remotes; -} - guint fu_config_get_idle_timeout (FuConfig *self) { @@ -584,13 +195,6 @@ fu_config_get_idle_timeout (FuConfig *self) return self->idle_timeout; } -FwupdRemote * -fu_config_get_remote_by_id (FuConfig *self, const gchar *remote_id) -{ - g_return_val_if_fail (FU_IS_CONFIG (self), NULL); - return fu_config_get_remote_by_id_noref (self->remotes, remote_id); -} - GPtrArray * fu_config_get_blacklist_devices (FuConfig *self) { @@ -636,12 +240,9 @@ static void fu_config_init (FuConfig *self) { self->archive_size_max = 512 * 0x100000; - self->keyfile = g_key_file_new (); self->blacklist_devices = g_ptr_array_new_with_free_func (g_free); self->blacklist_plugins = g_ptr_array_new_with_free_func (g_free); self->approved_firmware = g_ptr_array_new_with_free_func (g_free); - self->remotes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - self->monitors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); } static void @@ -649,16 +250,11 @@ fu_config_finalize (GObject *obj) { FuConfig *self = FU_CONFIG (obj); - if (self->os_release != NULL) - g_hash_table_unref (self->os_release); - if (self->silo != NULL) - g_object_unref (self->silo); - g_key_file_unref (self->keyfile); + if (self->monitor != NULL) + g_object_unref (self->monitor); g_ptr_array_unref (self->blacklist_devices); g_ptr_array_unref (self->blacklist_plugins); g_ptr_array_unref (self->approved_firmware); - g_ptr_array_unref (self->remotes); - g_ptr_array_unref (self->monitors); g_free (self->config_file); G_OBJECT_CLASS (fu_config_parent_class)->finalize (obj); diff --git a/src/fu-config.h b/src/fu-config.h index 7b3a4b0b7..845baa425 100644 --- a/src/fu-config.h +++ b/src/fu-config.h @@ -13,25 +13,10 @@ #define FU_TYPE_CONFIG (fu_config_get_type ()) G_DECLARE_FINAL_TYPE (FuConfig, fu_config, FU, CONFIG, GObject) -/** - * FuConfigLoadFlags: - * @FU_CONFIG_LOAD_FLAG_NONE: No flags set - * @FU_CONFIG_LOAD_FLAG_READONLY_FS: Ignore readonly filesystem errors - * - * The flags to use when loading a configuration file. - **/ -typedef enum { - FU_CONFIG_LOAD_FLAG_NONE = 0, - FU_CONFIG_LOAD_FLAG_READONLY_FS = 1 << 0, - /*< private >*/ - FU_CONFIG_LOAD_FLAG_LAST -} FuConfigLoadFlags; - FuConfig *fu_config_new (void); gboolean fu_config_load (FuConfig *self, - FuConfigLoadFlags flags, GError **error); -gboolean fu_config_modify_and_save (FuConfig *self, +gboolean fu_config_set_key_value (FuConfig *self, const gchar *key, const gchar *value, GError **error); @@ -41,6 +26,3 @@ guint fu_config_get_idle_timeout (FuConfig *self); GPtrArray *fu_config_get_blacklist_devices (FuConfig *self); GPtrArray *fu_config_get_blacklist_plugins (FuConfig *self); GPtrArray *fu_config_get_approved_firmware (FuConfig *self); -GPtrArray *fu_config_get_remotes (FuConfig *self); -FwupdRemote *fu_config_get_remote_by_id (FuConfig *self, - const gchar *remote_id); diff --git a/src/fu-engine.c b/src/fu-engine.c index 54d471ab1..6017d386b 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -46,6 +46,7 @@ #include "fu-plugin-list.h" #include "fu-plugin-private.h" #include "fu-quirks.h" +#include "fu-remote-list.h" #include "fu-smbios-private.h" #include "fu-udev-device-private.h" #include "fu-usb-device-private.h" @@ -69,6 +70,7 @@ struct _FuEngine GUdevClient *gudev_client; #endif FuConfig *config; + FuRemoteList *remote_list; FuDeviceList *device_list; FwupdStatus status; gboolean tainted; @@ -400,7 +402,7 @@ fu_engine_set_release_from_appstream (FuEngine *self, remote_id = xb_node_query_text (component, "../custom/value[@key='fwupd::RemoteId']", NULL); if (remote_id != NULL) { fwupd_release_set_remote_id (rel, remote_id); - remote = fu_config_get_remote_by_id (self->config, remote_id); + remote = fu_remote_list_get_by_id (self->remote_list, remote_id); if (remote == NULL) g_warning ("no remote found for release %s", version_rel); } @@ -566,7 +568,7 @@ fu_engine_modify_config (FuEngine *self, const gchar *key, const gchar *value, G } /* modify, effective next reboot */ - return fu_config_modify_and_save (self->config, key, value, error); + return fu_config_set_key_value (self->config, key, value, error); } /** @@ -588,20 +590,7 @@ fu_engine_modify_remote (FuEngine *self, const gchar *value, GError **error) { - FwupdRemote *remote; - const gchar *filename; const gchar *keys[] = { "Enabled", "MetadataURI", "FirmwareBaseURI", "ReportURI", "AutomaticReports", NULL }; - g_autoptr(GKeyFile) keyfile = g_key_file_new (); - - /* check remote is valid */ - remote = fu_config_get_remote_by_id (self->config, remote_id); - if (remote == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "remote %s not found", remote_id); - return FALSE; - } /* check keys are valid */ if (!g_strv_contains (keys, key)) { @@ -611,17 +600,7 @@ fu_engine_modify_remote (FuEngine *self, "key %s not supported", key); return FALSE; } - - /* modify the remote filename */ - 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; - } - g_key_file_set_string (keyfile, "fwupd Remote", key, value); - return g_key_file_save_to_file (keyfile, filename, error); + return fu_remote_list_set_key_value (self->remote_list, remote_id, key, value, error); } /** @@ -2662,7 +2641,7 @@ fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError * } /* load each enabled metadata file */ - remotes = fu_config_get_remotes (self->config); + remotes = fu_remote_list_get_all (self->remote_list); for (guint i = 0; i < remotes->len; i++) { const gchar *path = NULL; g_autoptr(GError) error_local = NULL; @@ -2780,6 +2759,12 @@ fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError * static void fu_engine_config_changed_cb (FuConfig *config, FuEngine *self) +{ + fu_idle_set_timeout (self->idle, fu_config_get_idle_timeout (config)); +} + +static void +fu_engine_remote_list_changed_cb (FuRemoteList *remote_list, FuEngine *self) { g_autoptr(GError) error_local = NULL; if (!fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE, @@ -2847,7 +2832,7 @@ fu_engine_update_metadata (FuEngine *self, const gchar *remote_id, stream_sig = g_unix_input_stream_new (fd_sig, TRUE); /* check remote is valid */ - remote = fu_config_get_remote_by_id (self->config, remote_id); + remote = fu_remote_list_get_by_id (self->remote_list, remote_id); if (remote == NULL) { g_set_error (error, FWUPD_ERROR, @@ -3306,7 +3291,7 @@ fu_engine_get_remotes (FuEngine *self, GError **error) g_return_val_if_fail (FU_IS_ENGINE (self), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - remotes = fu_config_get_remotes (self->config); + remotes = fu_remote_list_get_all (self->remote_list); if (remotes->len == 0) { g_set_error (error, FWUPD_ERROR, @@ -4985,7 +4970,7 @@ fu_engine_ensure_client_certificate (FuEngine *self) gboolean fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error) { - FuConfigLoadFlags config_flags = FU_CONFIG_LOAD_FLAG_NONE; + FuRemoteListLoadFlags remote_list_flags = FU_REMOTE_LIST_LOAD_FLAG_NONE; FuQuirksLoadFlags quirks_flags = FU_QUIRKS_LOAD_FLAG_NONE; g_autoptr(GPtrArray) checksums = NULL; @@ -5004,13 +4989,19 @@ fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error) return FALSE; #endif /* read config file */ - if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS) - config_flags |= FU_CONFIG_LOAD_FLAG_READONLY_FS; - if (!fu_config_load (self->config, config_flags, error)) { + if (!fu_config_load (self->config, error)) { g_prefix_error (error, "Failed to load config: "); return FALSE; } + /* read remotes */ + if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS) + remote_list_flags |= FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS; + if (!fu_remote_list_load (self->remote_list, remote_list_flags, error)) { + g_prefix_error (error, "Failed to load remotes: "); + return FALSE; + } + /* create client certificate */ fu_engine_ensure_client_certificate (self); @@ -5204,6 +5195,7 @@ fu_engine_init (FuEngine *self) self->percentage = 0; self->status = FWUPD_STATUS_IDLE; self->config = fu_config_new (); + self->remote_list = fu_remote_list_new (); self->device_list = fu_device_list_new (); self->smbios = fu_smbios_new (); self->hwids = fu_hwids_new (); @@ -5225,6 +5217,9 @@ fu_engine_init (FuEngine *self) g_signal_connect (self->config, "changed", G_CALLBACK (fu_engine_config_changed_cb), self); + g_signal_connect (self->remote_list, "changed", + G_CALLBACK (fu_engine_remote_list_changed_cb), + self); g_signal_connect (self->idle, "notify::status", G_CALLBACK (fu_engine_idle_status_notify_cb), self); @@ -5278,6 +5273,7 @@ fu_engine_finalize (GObject *obj) g_free (self->host_machine_id); g_object_unref (self->idle); g_object_unref (self->config); + g_object_unref (self->remote_list); g_object_unref (self->smbios); g_object_unref (self->quirks); g_object_unref (self->hwids); diff --git a/src/fu-remote-list.c b/src/fu-remote-list.c new file mode 100644 index 000000000..8742f52e0 --- /dev/null +++ b/src/fu-remote-list.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2017-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuRemoteList" + +#include "config.h" + +#include +#include +#include +#include + +#include "fu-common.h" +#include "fu-remote-list.h" + +#include "fwupd-common.h" +#include "fwupd-error.h" +#include "fwupd-remote-private.h" + +enum { + SIGNAL_CHANGED, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST] = { 0 }; + +static void fu_remote_list_finalize (GObject *obj); + +struct _FuRemoteList +{ + GObject parent_instance; + GPtrArray *array; /* (element-type FwupdRemote) */ + GPtrArray *monitors; /* (element-type GFileMonitor) */ + XbSilo *silo; +}; + +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 GPtrArray * +fu_remote_list_get_all_paths (void) +{ + GPtrArray *paths = g_ptr_array_new_with_free_func (g_free); + const gchar *remotes_dir; + const gchar *system_prefixlibdir = "/usr/lib/fwupd"; + g_autofree gchar *remotesdir = NULL; + + /* only set by the self test program */ + remotes_dir = g_getenv ("FU_SELF_TEST_REMOTES_DIR"); + if (remotes_dir != NULL) { + g_ptr_array_add (paths, g_strdup (remotes_dir)); + return paths; + } + + /* use sysremotes, and then fall back to /etc */ + remotesdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR_PKG); + if (g_file_test (remotesdir, G_FILE_TEST_EXISTS)) + g_ptr_array_add (paths, g_steal_pointer (&remotesdir)); + + /* add in system-wide locations */ + if (g_file_test (system_prefixlibdir, G_FILE_TEST_EXISTS)) + g_ptr_array_add (paths, g_strdup (system_prefixlibdir)); + + return paths; +} + +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); + g_debug ("%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); +} + +static guint64 +_fwupd_remote_get_mtime (FwupdRemote *remote) +{ + g_autoptr(GFile) file = NULL; + g_autoptr(GFileInfo) info = NULL; + + file = g_file_new_for_path (fwupd_remote_get_filename_cache (remote)); + if (!g_file_query_exists (file, NULL)) + return G_MAXUINT64; + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + if (info == NULL) + return G_MAXUINT64; + return g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED); +} + +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) + return FALSE; + g_signal_connect (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, "%s\n", + /* 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, "%s\n", + /* 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 gboolean +fu_remote_list_add_for_path (FuRemoteList *self, const gchar *path, GError **error) +{ + const gchar *tmp; + g_autofree gchar *path_remotes = NULL; + g_autoptr(GDir) dir = NULL; + g_autoptr(GHashTable) os_release = 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; + dir = g_dir_open (path_remotes, 0, error); + if (dir == NULL) + return FALSE; + os_release = fwupd_get_os_release (error); + if (os_release == NULL) + return FALSE; + while ((tmp = g_dir_read_name (dir)) != NULL) { + g_autofree gchar *filename = g_build_filename (path_remotes, tmp, NULL); + g_autoptr(FwupdRemote) remote = fwupd_remote_new (); + g_autofree gchar *localstatedir = NULL; + g_autofree gchar *remotesdir = NULL; + + /* skip invalid files */ + if (!g_str_has_suffix (tmp, ".conf")) { + g_debug ("skipping invalid file %s", filename); + continue; + } + + /* set directory to store data */ + localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG); + remotesdir = g_build_filename (localstatedir, "remotes.d", NULL); + fwupd_remote_set_remotes_dir (remote, remotesdir); + + /* load from keyfile */ + g_debug ("loading remotes from %s", filename); + if (!fwupd_remote_load_from_filename (remote, filename, + NULL, error)) { + g_prefix_error (error, "failed to load %s: ", filename); + return FALSE; + } + + /* 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_autoptr(GString) agreement_markup = NULL; + g_autofree gchar *component_id = _fwupd_remote_build_component_id (remote); + g_autoptr(XbNode) component = NULL; + g_autofree gchar *xpath = NULL; + + xpath = g_strdup_printf ("component/id[text()='%s']/..", component_id); + component = xb_silo_query_first (self->silo, xpath, 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 */ + tmp = g_hash_table_lookup (os_release, "NAME"); + if (tmp == NULL) + tmp = "this distribution"; + fu_common_string_replace (agreement_markup, "$OS_RELEASE:NAME$", tmp); + tmp = g_hash_table_lookup (os_release, "BUG_REPORT_URL"); + if (tmp == NULL) + tmp = "https://github.com/fwupd/fwupd/issues"; + fu_common_string_replace (agreement_markup, "$OS_RELEASE:BUG_REPORT_URL$", tmp); + fwupd_remote_set_agreement (remote, agreement_markup->str); + } + + /* set mtime */ + fwupd_remote_set_mtime (remote, _fwupd_remote_get_mtime (remote)); + g_ptr_array_add (self->array, g_steal_pointer (&remote)); + } + return TRUE; +} + +gboolean +fu_remote_list_set_key_value (FuRemoteList *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GError **error) +{ + FwupdRemote *remote; + const gchar *filename; + g_autoptr(GKeyFile) keyfile = g_key_file_new (); + + /* check remote is valid */ + remote = fu_remote_list_get_by_id (self, remote_id); + if (remote == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "remote %s not found", remote_id); + 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; + } + g_key_file_set_string (keyfile, "fwupd Remote", key, value); + if (!g_key_file_save_to_file (keyfile, filename, error)) + return FALSE; + + /* reload values */ + if (!fwupd_remote_load_from_filename (remote, filename, + NULL, error)) { + g_prefix_error (error, "failed to load %s: ", filename); + return FALSE; + } + 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_with_direction (FuRemoteList *self, gint inc) +{ + guint cnt = 0; + for (guint i = 0; i < self->array->len; i++) { + FwupdRemote *remote = g_ptr_array_index (self->array, i); + gchar **order = inc < 0 ? fwupd_remote_get_order_after (remote) : + fwupd_remote_get_order_before (remote); + if (order == NULL) + continue; + for (guint j = 0; order[j] != NULL; j++) { + FwupdRemote *remote2; + 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]); + 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+%i", + fwupd_remote_get_id (remote), + fwupd_remote_get_id (remote2), + inc); + fwupd_remote_set_priority (remote, fwupd_remote_get_priority (remote2) + inc); + + /* increment changes counter */ + cnt++; + } + } + return cnt; +} + +gboolean +fu_remote_list_reload (FuRemoteList *self, GError **error) +{ + guint depsolve_check; + g_autoptr(GPtrArray) paths = NULL; + + /* clear */ + g_ptr_array_set_size (self->array, 0); + g_ptr_array_set_size (self->monitors, 0); + + /* get a list of all remotes paths */ + paths = fu_remote_list_get_all_paths (); + if (paths->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No search paths found"); + return FALSE; + } + + /* look for all remote_list */ + for (guint i = 0; i < paths->len; i++) { + const gchar *path = g_ptr_array_index (paths, i); + g_debug ("using remotes path of %s", path); + if (!fu_remote_list_add_for_path (self, path, error)) + return FALSE; + } + + /* depsolve */ + for (depsolve_check = 0; depsolve_check < 100; depsolve_check++) { + guint cnt = 0; + cnt += fu_remote_list_depsolve_with_direction (self, 1); + cnt += fu_remote_list_depsolve_with_direction (self, -1); + 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); + + /* success */ + return TRUE; +} + +static gboolean +fu_remote_list_load_metainfos (XbBuilder *builder, GError **error) +{ + const gchar *fn; + g_autofree gchar *datadir = NULL; + g_autofree gchar *metainfo_path = NULL; + g_autoptr(GDir) dir = NULL; + + /* pkg metainfo dir */ + datadir = fu_common_get_path (FU_PATH_KIND_DATADIR_PKG); + metainfo_path = g_build_filename (datadir, "metainfo", NULL); + 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)) + return FALSE; + xb_builder_import_source (builder, source); + } + } + return TRUE; +} + +gboolean +fu_remote_list_load (FuRemoteList *self, FuRemoteListLoadFlags flags, GError **error) +{ + const gchar *const *locales = g_get_language_names (); + g_autofree gchar *cachedirpkg = NULL; + g_autofree gchar *xmlbfn = 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); + + /* load AppStream about the remote_list */ + if (!fu_remote_list_load_metainfos (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_REMOTE_LIST_LOAD_FLAG_READONLY_FS) + compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID; + + /* build the metainfo silo */ + cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG); + xmlbfn = g_build_filename (cachedirpkg, "metainfo.xmlb", NULL); + xmlb = g_file_new_for_path (xmlbfn); + self->silo = xb_builder_ensure (builder, xmlb, compile_flags, NULL, error); + if (self->silo == NULL) + return FALSE; + + /* 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 self->array; +} + +FwupdRemote * +fu_remote_list_get_by_id (FuRemoteList *self, const gchar *remote_id) +{ + g_return_val_if_fail (FU_IS_REMOTE_LIST (self), 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 remote; + } + 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; + + 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); +} + +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) g_object_unref); +} + +static void +fu_remote_list_finalize (GObject *obj) +{ + FuRemoteList *self = FU_REMOTE_LIST (obj); + if (self->silo != NULL) + g_object_unref (self->silo); + g_ptr_array_unref (self->array); + g_ptr_array_unref (self->monitors); + G_OBJECT_CLASS (fu_remote_list_parent_class)->finalize (obj); +} + +FuRemoteList * +fu_remote_list_new (void) +{ + FuRemoteList *self; + self = g_object_new (FU_TYPE_REMOTE_LIST, NULL); + return FU_REMOTE_LIST (self); +} diff --git a/src/fu-remote-list.h b/src/fu-remote-list.h new file mode 100644 index 000000000..f4d4a3074 --- /dev/null +++ b/src/fu-remote-list.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fwupd-remote.h" + +#define FU_TYPE_REMOTE_LIST (fu_remote_list_get_type ()) +G_DECLARE_FINAL_TYPE (FuRemoteList, fu_remote_list, FU, REMOTE_LIST, GObject) + +/** + * FuRemoteListLoadFlags: + * @FU_REMOTE_LIST_LOAD_FLAG_NONE: No flags set + * @FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS: Ignore readonly filesystem errors + * + * The flags to use when loading a remote_listuration file. + **/ +typedef enum { + FU_REMOTE_LIST_LOAD_FLAG_NONE = 0, + FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS = 1 << 0, + /*< private >*/ + FU_REMOTE_LIST_LOAD_FLAG_LAST +} FuRemoteListLoadFlags; + +FuRemoteList *fu_remote_list_new (void); +gboolean fu_remote_list_load (FuRemoteList *self, + FuRemoteListLoadFlags flags, + GError **error); +gboolean fu_remote_list_reload (FuRemoteList *self, + GError **error); +gboolean fu_remote_list_set_key_value (FuRemoteList *self, + const gchar *remote_id, + const gchar *key, + const gchar *value, + GError **error); +GPtrArray *fu_remote_list_get_all (FuRemoteList *self); +FwupdRemote *fu_remote_list_get_by_id (FuRemoteList *self, + const gchar *remote_id); diff --git a/src/meson.build b/src/meson.build index 83e611977..f3747dca8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -145,6 +145,7 @@ fwupdtool = executable( 'fu-keyring-utils.c', 'fu-plugin-list.c', 'fu-progressbar.c', + 'fu-remote-list.c', 'fu-util-common.c', systemd_src ], @@ -212,6 +213,7 @@ executable( 'fu-keyring-utils.c', 'fu-main.c', 'fu-plugin-list.c', + 'fu-remote-list.c', systemd_src ], include_directories : [ @@ -269,6 +271,7 @@ if get_option('tests') 'fu-keyring-utils.c', 'fu-plugin-list.c', 'fu-progressbar.c', + 'fu-remote-list.c', 'fu-self-test.c', systemd_src ],