Allow overriding daemon parameters using /var/etc/fwupd/daemon.conf

This is super useful for debugging immutable systems like ChromeOS.
This commit is contained in:
Richard Hughes 2022-01-20 11:47:53 +00:00
parent 122dee1a19
commit 38bab8fc4f
3 changed files with 75 additions and 28 deletions

View File

@ -1448,6 +1448,13 @@ fu_common_get_path(FuPathKind path_kind)
return g_build_filename(tmp, NULL);
basedir = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR);
return g_build_filename(basedir, "cache", PACKAGE_NAME, NULL);
/* /var/etc/fwupd */
case FU_PATH_KIND_LOCALCONFDIR_PKG:
tmp = g_getenv("LOCALCONF_DIRECTORY");
if (tmp != NULL && g_file_test(tmp, G_FILE_TEST_EXISTS))
return g_build_filename(tmp, NULL);
basedir = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR);
return g_build_filename(basedir, "etc", PACKAGE_NAME, NULL);
/* /run/lock */
case FU_PATH_KIND_LOCKDIR:
return g_strdup("/run/lock");

View File

@ -83,6 +83,7 @@ typedef guint FuEndianType;
* @FU_PATH_KIND_LOCALSTATEDIR_REMOTES: The local state directory for remotes (IE
* /var/lib/fwupd/remotes.d)
* @FU_PATH_KIND_WIN32_BASEDIR: The root of the install directory on Windows
* @FU_PATH_KIND_LOCALCONFDIR_PKG: The package configuration override (IE /var/etc/fwupd)
*
* Path types to use when dynamically determining a path at runtime
**/
@ -111,6 +112,7 @@ typedef enum {
FU_PATH_KIND_LOCALSTATEDIR_METADATA,
FU_PATH_KIND_LOCALSTATEDIR_REMOTES,
FU_PATH_KIND_WIN32_BASEDIR,
FU_PATH_KIND_LOCALCONFDIR_PKG,
/*< private >*/
FU_PATH_KIND_LAST
} FuPathKind;

View File

@ -23,15 +23,15 @@ fu_config_finalize(GObject *obj);
struct _FuConfig {
GObject parent_instance;
GFileMonitor *monitor;
GPtrArray *monitors; /* (element-type GFileMonitor) */
GPtrArray *disabled_devices; /* (element-type utf-8) */
GPtrArray *disabled_plugins; /* (element-type utf-8) */
GPtrArray *approved_firmware; /* (element-type utf-8) */
GPtrArray *blocked_firmware; /* (element-type utf-8) */
GPtrArray *uri_schemes; /* (element-type utf-8) */
GPtrArray *filenames; /* (element-type utf-8) */
guint64 archive_size_max;
guint idle_timeout;
gchar *config_file;
gchar *host_bkc;
gboolean update_motd;
gboolean enumerate_all_devices;
@ -65,13 +65,30 @@ fu_config_reload(FuConfig *self, GError **error)
g_autoptr(GError) error_ignore_power = NULL;
g_autoptr(GError) error_only_trusted = NULL;
g_autoptr(GError) error_enumerate_all = NULL;
g_autoptr(GByteArray) buf = g_byte_array_new();
if (g_file_test(self->config_file, G_FILE_TEST_EXISTS)) {
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))
/* we have to load each file into a buffer as g_key_file_load_from_file() clears the
* GKeyFile state before loading each file, and we want to allow the mutable version to be
* incomplete and just *override* a specific option */
for (guint i = 0; i < self->filenames->len; i++) {
const gchar *fn = g_ptr_array_index(self->filenames, i);
g_debug("trying to load config values from %s", fn);
if (g_file_test(fn, G_FILE_TEST_EXISTS)) {
g_autoptr(GBytes) blob = fu_common_get_contents_bytes(fn, error);
if (blob == NULL)
return FALSE;
fu_byte_array_append_bytes(buf, blob);
}
}
/* load if either file found */
if (buf->len > 0) {
if (!g_key_file_load_from_data(keyfile,
(const gchar *)buf->data,
buf->len,
G_KEY_FILE_NONE,
error))
return FALSE;
} else {
g_warning("Daemon configuration %s not found", self->config_file);
}
/* get disabled devices */
@ -223,7 +240,8 @@ fu_config_monitor_changed_cb(GFileMonitor *monitor,
{
FuConfig *self = FU_CONFIG(user_data);
g_autoptr(GError) error = NULL;
g_debug("%s changed, reloading all configs", self->config_file);
g_autofree gchar *fn = g_file_get_path(file);
g_debug("%s changed, reloading all configs", fn);
if (!fu_config_reload(self, &error))
g_warning("failed to rescan daemon config: %s", error->message);
fu_config_emit_changed(self);
@ -233,38 +251,55 @@ gboolean
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))
const gchar *fn;
/* sanity check */
if (self->filenames->len == 0) {
g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "no config to load");
return FALSE;
}
/* only write the file in /etc */
fn = g_ptr_array_index(self->filenames, 0);
if (!g_key_file_load_from_file(keyfile, fn, 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))
if (!g_key_file_save_to_file(keyfile, fn, error))
return FALSE;
return fu_config_reload(self, error);
}
gboolean
fu_config_load(FuConfig *self, GError **error)
{
g_autofree gchar *configdir = NULL;
g_autoptr(GFile) file = NULL;
g_autofree gchar *configdir_mut = fu_common_get_path(FU_PATH_KIND_LOCALCONFDIR_PKG);
g_autofree gchar *configdir = fu_common_get_path(FU_PATH_KIND_SYSCONFDIR_PKG);
g_return_val_if_fail(FU_IS_CONFIG(self), FALSE);
g_return_val_if_fail(self->config_file == NULL, FALSE);
g_return_val_if_fail(self->filenames->len == 0, 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);
g_ptr_array_add(self->filenames, g_build_filename(configdir, "daemon.conf", NULL));
g_ptr_array_add(self->filenames, g_build_filename(configdir_mut, "daemon.conf", NULL));
if (!fu_config_reload(self, error))
return FALSE;
/* 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(G_FILE_MONITOR(self->monitor),
"changed",
G_CALLBACK(fu_config_monitor_changed_cb),
self);
/* set up a notify watches */
for (guint i = 0; i < self->filenames->len; i++) {
const gchar *fn = g_ptr_array_index(self->filenames, i);
g_autoptr(GFile) file = g_file_new_for_path(fn);
g_autoptr(GFileMonitor) monitor = NULL;
monitor = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, error);
if (monitor == NULL)
return FALSE;
g_signal_connect(G_FILE_MONITOR(monitor),
"changed",
G_CALLBACK(fu_config_monitor_changed_cb),
self);
g_ptr_array_add(self->monitors, g_steal_pointer(&monitor));
}
/* success */
return TRUE;
@ -391,11 +426,13 @@ fu_config_class_init(FuConfigClass *klass)
static void
fu_config_init(FuConfig *self)
{
self->filenames = g_ptr_array_new_with_free_func(g_free);
self->disabled_devices = g_ptr_array_new_with_free_func(g_free);
self->disabled_plugins = g_ptr_array_new_with_free_func(g_free);
self->approved_firmware = g_ptr_array_new_with_free_func(g_free);
self->blocked_firmware = g_ptr_array_new_with_free_func(g_free);
self->uri_schemes = g_ptr_array_new_with_free_func(g_free);
self->monitors = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
}
static void
@ -403,16 +440,17 @@ fu_config_finalize(GObject *obj)
{
FuConfig *self = FU_CONFIG(obj);
if (self->monitor != NULL) {
g_file_monitor_cancel(self->monitor);
g_object_unref(self->monitor);
for (guint i = 0; i < self->monitors->len; i++) {
GFileMonitor *monitor = g_ptr_array_index(self->monitors, i);
g_file_monitor_cancel(monitor);
}
g_ptr_array_unref(self->filenames);
g_ptr_array_unref(self->monitors);
g_ptr_array_unref(self->disabled_devices);
g_ptr_array_unref(self->disabled_plugins);
g_ptr_array_unref(self->approved_firmware);
g_ptr_array_unref(self->blocked_firmware);
g_ptr_array_unref(self->uri_schemes);
g_free(self->config_file);
g_free(self->host_bkc);
G_OBJECT_CLASS(fu_config_parent_class)->finalize(obj);