mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-13 19:37:17 +00:00
Split out the remote loading from FuConfig
It's confusing to have FuConfig load both the daemon.conf file and also keep track of the enabled remotes. It's also wasteful of memory to keep the GKeyFile alive the entire time. Logically these are different pools of information and should be managed by different objects. This allows us to implement reload() in a sane way and be less reliant on the inotify event.
This commit is contained in:
parent
038d8f463e
commit
d1808aae67
@ -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
|
||||
|
514
src/fu-config.c
514
src/fu-config.c
@ -8,19 +8,12 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <xmlb.h>
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
535
src/fu-remote-list.c
Normal file
535
src/fu-remote-list.c
Normal file
@ -0,0 +1,535 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2019 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "FuRemoteList"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <xmlb.h>
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#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);
|
||||
}
|
43
src/fu-remote-list.h
Normal file
43
src/fu-remote-list.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2019 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#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);
|
@ -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
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user