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); return g_build_filename(tmp, NULL);
basedir = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR); basedir = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR);
return g_build_filename(basedir, "cache", PACKAGE_NAME, NULL); 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 */ /* /run/lock */
case FU_PATH_KIND_LOCKDIR: case FU_PATH_KIND_LOCKDIR:
return g_strdup("/run/lock"); 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 * @FU_PATH_KIND_LOCALSTATEDIR_REMOTES: The local state directory for remotes (IE
* /var/lib/fwupd/remotes.d) * /var/lib/fwupd/remotes.d)
* @FU_PATH_KIND_WIN32_BASEDIR: The root of the install directory on Windows * @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 * 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_METADATA,
FU_PATH_KIND_LOCALSTATEDIR_REMOTES, FU_PATH_KIND_LOCALSTATEDIR_REMOTES,
FU_PATH_KIND_WIN32_BASEDIR, FU_PATH_KIND_WIN32_BASEDIR,
FU_PATH_KIND_LOCALCONFDIR_PKG,
/*< private >*/ /*< private >*/
FU_PATH_KIND_LAST FU_PATH_KIND_LAST
} FuPathKind; } FuPathKind;

View File

@ -23,15 +23,15 @@ fu_config_finalize(GObject *obj);
struct _FuConfig { struct _FuConfig {
GObject parent_instance; GObject parent_instance;
GFileMonitor *monitor; GPtrArray *monitors; /* (element-type GFileMonitor) */
GPtrArray *disabled_devices; /* (element-type utf-8) */ GPtrArray *disabled_devices; /* (element-type utf-8) */
GPtrArray *disabled_plugins; /* (element-type utf-8) */ GPtrArray *disabled_plugins; /* (element-type utf-8) */
GPtrArray *approved_firmware; /* (element-type utf-8) */ GPtrArray *approved_firmware; /* (element-type utf-8) */
GPtrArray *blocked_firmware; /* (element-type utf-8) */ GPtrArray *blocked_firmware; /* (element-type utf-8) */
GPtrArray *uri_schemes; /* (element-type utf-8) */ GPtrArray *uri_schemes; /* (element-type utf-8) */
GPtrArray *filenames; /* (element-type utf-8) */
guint64 archive_size_max; guint64 archive_size_max;
guint idle_timeout; guint idle_timeout;
gchar *config_file;
gchar *host_bkc; gchar *host_bkc;
gboolean update_motd; gboolean update_motd;
gboolean enumerate_all_devices; 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_ignore_power = NULL;
g_autoptr(GError) error_only_trusted = NULL; g_autoptr(GError) error_only_trusted = NULL;
g_autoptr(GError) error_enumerate_all = 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)) { /* we have to load each file into a buffer as g_key_file_load_from_file() clears the
g_debug("loading config values from %s", self->config_file); * GKeyFile state before loading each file, and we want to allow the mutable version to be
if (!g_key_file_load_from_file(keyfile, self->config_file, G_KEY_FILE_NONE, error)) * 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; return FALSE;
} else {
g_warning("Daemon configuration %s not found", self->config_file);
} }
/* get disabled devices */ /* get disabled devices */
@ -223,7 +240,8 @@ fu_config_monitor_changed_cb(GFileMonitor *monitor,
{ {
FuConfig *self = FU_CONFIG(user_data); FuConfig *self = FU_CONFIG(user_data);
g_autoptr(GError) error = NULL; 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)) if (!fu_config_reload(self, &error))
g_warning("failed to rescan daemon config: %s", error->message); g_warning("failed to rescan daemon config: %s", error->message);
fu_config_emit_changed(self); 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) fu_config_set_key_value(FuConfig *self, const gchar *key, const gchar *value, GError **error)
{ {
g_autoptr(GKeyFile) keyfile = g_key_file_new(); 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; return FALSE;
g_key_file_set_string(keyfile, "fwupd", key, value); 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 FALSE;
return fu_config_reload(self, error); return fu_config_reload(self, error);
} }
gboolean gboolean
fu_config_load(FuConfig *self, GError **error) fu_config_load(FuConfig *self, GError **error)
{ {
g_autofree gchar *configdir = NULL; g_autofree gchar *configdir_mut = fu_common_get_path(FU_PATH_KIND_LOCALCONFDIR_PKG);
g_autoptr(GFile) file = NULL; 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(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 */ /* load the main daemon config file */
configdir = fu_common_get_path(FU_PATH_KIND_SYSCONFDIR_PKG); g_ptr_array_add(self->filenames, g_build_filename(configdir, "daemon.conf", NULL));
self->config_file = 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)) if (!fu_config_reload(self, error))
return FALSE; return FALSE;
/* set up a notify watch */ /* set up a notify watches */
file = g_file_new_for_path(self->config_file); for (guint i = 0; i < self->filenames->len; i++) {
self->monitor = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, error); const gchar *fn = g_ptr_array_index(self->filenames, i);
if (self->monitor == NULL) 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; return FALSE;
g_signal_connect(G_FILE_MONITOR(self->monitor), g_signal_connect(G_FILE_MONITOR(monitor),
"changed", "changed",
G_CALLBACK(fu_config_monitor_changed_cb), G_CALLBACK(fu_config_monitor_changed_cb),
self); self);
g_ptr_array_add(self->monitors, g_steal_pointer(&monitor));
}
/* success */ /* success */
return TRUE; return TRUE;
@ -391,11 +426,13 @@ fu_config_class_init(FuConfigClass *klass)
static void static void
fu_config_init(FuConfig *self) 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_devices = g_ptr_array_new_with_free_func(g_free);
self->disabled_plugins = 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->approved_firmware = g_ptr_array_new_with_free_func(g_free);
self->blocked_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->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 static void
@ -403,16 +440,17 @@ fu_config_finalize(GObject *obj)
{ {
FuConfig *self = FU_CONFIG(obj); FuConfig *self = FU_CONFIG(obj);
if (self->monitor != NULL) { for (guint i = 0; i < self->monitors->len; i++) {
g_file_monitor_cancel(self->monitor); GFileMonitor *monitor = g_ptr_array_index(self->monitors, i);
g_object_unref(self->monitor); 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_devices);
g_ptr_array_unref(self->disabled_plugins); g_ptr_array_unref(self->disabled_plugins);
g_ptr_array_unref(self->approved_firmware); g_ptr_array_unref(self->approved_firmware);
g_ptr_array_unref(self->blocked_firmware); g_ptr_array_unref(self->blocked_firmware);
g_ptr_array_unref(self->uri_schemes); g_ptr_array_unref(self->uri_schemes);
g_free(self->config_file);
g_free(self->host_bkc); g_free(self->host_bkc);
G_OBJECT_CLASS(fu_config_parent_class)->finalize(obj); G_OBJECT_CLASS(fu_config_parent_class)->finalize(obj);