From 4da5ecb2da285260b72bf82e841d9332c811cc6d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 9 Jul 2014 18:54:47 +0200 Subject: config: add support for reloading of configuration Some configuration parameters can be set via command line. If a parameter is set from command line, the original value from command line will still be preserved after reloading. (heavily modified by dcbw) Signed-off-by: Thomas Haller --- src/main.c | 4 +- src/nm-config-data.c | 57 ++++++++++++++ src/nm-config-data.h | 2 + src/nm-config.c | 205 +++++++++++++++++++++++++++++++++++++-------------- src/nm-config.h | 7 ++ 5 files changed, 218 insertions(+), 57 deletions(-) diff --git a/src/main.c b/src/main.c index 3eeab13f5b..b5061ea293 100644 --- a/src/main.c +++ b/src/main.c @@ -225,7 +225,9 @@ _handle_signal (gpointer user_data) g_assert (signo == SIGHUP); - nm_log_info (LOGD_CORE, "caught signal %d, not supported yet.", signo); + nm_log_info (LOGD_CORE, "caught signal %d, reload configuration...", signo); + + nm_config_reload (nm_config_get ()); return G_SOURCE_REMOVE; } diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 57cfed8a07..824a481f56 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -290,6 +290,63 @@ nm_config_data_new_keyfile (GKeyFile *keyfile, return self; } +#define CHANGED(p) g_hash_table_insert (changes, NM_CONFIG_DATA_##p, GUINT_TO_POINTER (1)); + +GHashTable * +nm_config_data_diff (NMConfigData *self, NMConfigData *other) +{ + NMConfigDataPrivate *priv, *other_priv; + GHashTable *changes; + guint i, n; + + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL); + g_return_val_if_fail (NM_IS_CONFIG_DATA (other), NULL); + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + other_priv = NM_CONFIG_DATA_GET_PRIVATE (other); + + changes = g_hash_table_new (g_str_hash, g_str_equal); + + n = g_strv_length (priv->plugins); + if (n != g_strv_length (other_priv->plugins)) + CHANGED (PLUGINS); + for (i = 0; i < n; i++) { + if (g_strcmp0 (priv->plugins[i], other_priv->plugins[i])) { + CHANGED (PLUGINS); + break; + } + } + + if (priv->monitor_connection_files != other_priv->monitor_connection_files) + CHANGED (MONITOR_CONNECTION_FILES); + + if (g_strcmp0 (priv->dhcp_client, other_priv->dhcp_client)) + CHANGED (DHCP_CLIENT); + + if (g_strcmp0 (priv->dns_mode, other_priv->dns_mode)) + CHANGED (DNS_MODE); + + if (g_strcmp0 (priv->debug, other_priv->debug)) + CHANGED (DEBUG); + + if (g_strcmp0 (priv->log.level, other_priv->log.level)) + CHANGED (LOG_LEVEL); + + if (g_strcmp0 (priv->log.domains, other_priv->log.domains)) + CHANGED (LOG_LEVEL); + + if (g_strcmp0 (priv->connectivity.uri, other_priv->connectivity.uri)) + CHANGED (CONNECTIVITY_URI); + + if (priv->connectivity.interval != other_priv->connectivity.interval) + CHANGED (CONNECTIVITY_INTERVAL); + + if (g_strcmp0 (priv->connectivity.response, other_priv->connectivity.response)) + CHANGED (CONNECTIVITY_RESPONSE); + + return changes; +} + /************************************************************************/ static void diff --git a/src/nm-config-data.h b/src/nm-config-data.h index 31416f0f86..2ec41d140d 100644 --- a/src/nm-config-data.h +++ b/src/nm-config-data.h @@ -76,6 +76,8 @@ NMConfigData *nm_config_data_new_keyfile (GKeyFile *keyfile, NMConfigData *override, GError **error); +GHashTable *nm_config_data_diff (NMConfigData *self, NMConfigData *other); + G_END_DECLS #endif /* NM_CONFIG_DATA_H */ diff --git a/src/nm-config.c b/src/nm-config.c index bd87a0ce4c..4b5e9903ce 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -28,6 +28,7 @@ #include "nm-utils.h" #include "nm-glib-compat.h" #include "nm-device.h" +#include "NetworkManagerUtils.h" #include #include @@ -54,6 +55,14 @@ typedef struct { NMConfigData *config_data; } NMConfigPrivate; +enum { + SIGNAL_CONFIG_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + static NMConfig *singleton = NULL; G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT) @@ -300,9 +309,8 @@ nm_config_get_options (void) /************************************************************************/ static gboolean -read_config (NMConfig *config, const char *path, GError **error) +read_config (GKeyFile *keyfile, const char *path, GError **error) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); GKeyFile *kf; char **groups, **keys; gsize ngroups, nkeys; @@ -332,22 +340,22 @@ read_config (NMConfig *config, const char *path, GError **error) int len = strlen (keys[k]); if (keys[k][len - 1] == '+') { char *base_key = g_strndup (keys[k], len - 1); - const char *old_val = g_key_file_get_value (priv->keyfile, groups[g], base_key, NULL); + const char *old_val = g_key_file_get_value (keyfile, groups[g], base_key, NULL); const char *new_val = g_key_file_get_value (kf, groups[g], keys[k], NULL); if (old_val && *old_val) { char *combined = g_strconcat (old_val, ",", new_val, NULL); - g_key_file_set_value (priv->keyfile, groups[g], base_key, combined); + g_key_file_set_value (keyfile, groups[g], base_key, combined); g_free (combined); } else - g_key_file_set_value (priv->keyfile, groups[g], base_key, new_val); + g_key_file_set_value (keyfile, groups[g], base_key, new_val); g_free (base_key); continue; } - g_key_file_set_value (priv->keyfile, groups[g], keys[k], + g_key_file_set_value (keyfile, groups[g], keys[k], g_key_file_get_value (kf, groups[g], keys[k], NULL)); } } @@ -365,7 +373,7 @@ find_base_config (NMConfig *config, GError **error) /* Try a user-specified config file first */ if (cli_config_path) { /* Bad user-specific config file path is a hard error */ - if (read_config (config, cli_config_path, error)) { + if (read_config (priv->keyfile, cli_config_path, error)) { priv->nm_conf_path = g_strdup (cli_config_path); return TRUE; } else @@ -380,7 +388,7 @@ find_base_config (NMConfig *config, GError **error) */ /* Try deprecated nm-system-settings.conf first */ - if (read_config (config, NM_OLD_SYSTEM_CONF_FILE, &my_error)) { + if (read_config (priv->keyfile, NM_OLD_SYSTEM_CONF_FILE, &my_error)) { priv->nm_conf_path = g_strdup (NM_OLD_SYSTEM_CONF_FILE); return TRUE; } @@ -393,7 +401,7 @@ find_base_config (NMConfig *config, GError **error) g_clear_error (&my_error); /* Try the standard config file location next */ - if (read_config (config, NM_DEFAULT_SYSTEM_CONF_FILE, &my_error)) { + if (read_config (priv->keyfile, NM_DEFAULT_SYSTEM_CONF_FILE, &my_error)) { priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE); return TRUE; } @@ -418,13 +426,6 @@ find_base_config (NMConfig *config, GError **error) /************************************************************************/ -NMConfig * -nm_config_get (void) -{ - g_assert (singleton); - return singleton; -} - static int sort_asciibetically (gconstpointer a, gconstpointer b) { @@ -434,13 +435,14 @@ sort_asciibetically (gconstpointer a, gconstpointer b) return strcmp (s1, s2); } -/* call this function only once! */ -NMConfig * -nm_config_new (const char *cli_log_level, - const char *cli_log_domains, - GError **error) +/* Updates keyfile with new merged values from config files */ +static gboolean +reload_config_files (GKeyFile *keyfile, + const char *conf_path, + const char *config_dir, + char **out_config_description, + GError **error) { - NMConfigPrivate *priv = NULL; GFile *dir; GFileEnumerator *direnum; GFileInfo *info; @@ -448,35 +450,17 @@ nm_config_new (const char *cli_log_level, const char *name; int i; GString *config_description; - - g_assert (!singleton); - singleton = NM_CONFIG (g_object_new (NM_TYPE_CONFIG, NULL)); - priv = NM_CONFIG_GET_PRIVATE (singleton); - - /* First read the base config file */ - if (!find_base_config (singleton, error)) - goto fail; - - /* Read command-line overrides */ - priv->cli_data = nm_config_data_new_cli (cli_log_level, cli_log_domains, error); - if (!priv->cli_data) - goto fail; - - /* Now read the overrides in the config dir */ - if (cli_config_dir) - priv->config_dir = g_strdup (cli_config_dir); - else - priv->config_dir = g_strdup (NM_DEFAULT_SYSTEM_CONF_DIR); + gboolean success = TRUE; confs = g_ptr_array_new_with_free_func (g_free); - config_description = g_string_new (priv->nm_conf_path); - dir = g_file_new_for_path (priv->config_dir); + config_description = g_string_new (conf_path); + dir = g_file_new_for_path (config_dir); direnum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, NULL); if (direnum) { while ((info = g_file_enumerator_next_file (direnum, NULL, NULL))) { name = g_file_info_get_name (info); if (g_str_has_suffix (name, ".conf")) { - g_ptr_array_add (confs, g_build_filename (priv->config_dir, name, NULL)); + g_ptr_array_add (confs, g_build_filename (config_dir, name, NULL)); if (confs->len == 1) g_string_append (config_description, " and conf.d: "); else @@ -490,17 +474,117 @@ nm_config_new (const char *cli_log_level, g_object_unref (dir); g_ptr_array_sort (confs, sort_asciibetically); - priv->config_description = g_string_free (config_description, FALSE); + if (out_config_description) + *out_config_description = g_string_free (config_description, FALSE); + else + g_string_free (config_description, TRUE); + for (i = 0; i < confs->len; i++) { - if (!read_config (singleton, confs->pdata[i], error)) { - g_object_unref (singleton); - singleton = NULL; + if (!read_config (keyfile, confs->pdata[i], error)) { + success = FALSE; break; } } g_ptr_array_unref (confs); - if (!singleton) - return NULL; + return success; +} + +void +nm_config_reload (NMConfig *self) +{ + NMConfigPrivate *priv; + GError *error = NULL; + GHashTable *changes; + NMConfigData *new_data = NULL; + GKeyFile *new_kf; + char *config_desc = NULL; + + g_return_if_fail (NM_IS_CONFIG (self)); + + priv = NM_CONFIG_GET_PRIVATE (self); + + new_kf = g_key_file_new (); + g_key_file_set_list_separator (new_kf, ','); + if (!reload_config_files (new_kf, + priv->nm_conf_path, + priv->config_dir, + &config_desc, + &error)) + goto fail; + + new_data = nm_config_data_new_keyfile (new_kf, priv->cli_data, &error); + if (!new_data) + goto fail; + + changes = nm_config_data_diff (priv->config_data, new_data); + if (g_hash_table_size (changes)) { + NMConfigData *old_data = priv->config_data; + + g_object_unref (priv->keyfile); + priv->keyfile = new_kf; + g_free (priv->config_description); + priv->config_description = config_desc; + + priv->config_data = new_data; + g_signal_emit (self, signals[SIGNAL_CONFIG_CHANGED], 0, changes, old_data); + g_object_unref (old_data); + } else { + g_key_file_unref (new_kf); + g_object_unref (new_data); + g_free (config_desc); + } + g_hash_table_destroy (changes); + return; + +fail: + if (error) { + nm_log_warn (LOGD_SETTINGS, "failed to read configuation: (%s) %s", + g_quark_to_string (error->domain), error->message); + g_error_free (error); + } + g_key_file_unref (new_kf); + g_free (config_desc); +} + +/* call this function only once! */ +NMConfig * +nm_config_new (const char *cli_log_level, + const char *cli_log_domains, + GError **error) +{ + NMConfigPrivate *priv; + + g_assert (!singleton); + singleton = NM_CONFIG (g_object_new (NM_TYPE_CONFIG, NULL)); + priv = NM_CONFIG_GET_PRIVATE (singleton); + + /* First read the base config file */ + if (!find_base_config (singleton, error)) + goto fail; + + /* Read command-line overrides */ + priv->cli_data = nm_config_data_new_cli (cli_log_level, cli_log_domains, error); + if (!priv->cli_data) + goto fail; + + /* Now read the overrides in the config dir */ + if (cli_config_dir) + priv->config_dir = g_strdup (cli_config_dir); + else + priv->config_dir = g_strdup (NM_DEFAULT_SYSTEM_CONF_DIR); + + if (!reload_config_files (priv->keyfile, + priv->nm_conf_path, + priv->config_dir, + &priv->config_description, + error)) + goto fail; + + priv->config_data = nm_config_data_new_keyfile (priv->keyfile, + priv->cli_data, + error); + if (!priv->config_data) + goto fail; /* Handle no-auto-default key and state file */ priv->no_auto_default = g_key_file_get_string_list (priv->keyfile, "main", "no-auto-default", NULL, NULL); @@ -512,12 +596,6 @@ nm_config_new (const char *cli_log_level, priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL); - priv->config_data = nm_config_data_new_keyfile (priv->keyfile, - priv->cli_data, - error); - if (!priv->config_data) - goto fail; - return singleton; fail: @@ -526,6 +604,13 @@ fail: return NULL; } +NMConfig * +nm_config_get (void) +{ + g_assert (singleton); + return singleton; +} + static void nm_config_init (NMConfig *config) { @@ -576,5 +661,13 @@ nm_config_class_init (NMConfigClass *config_class) g_type_class_add_private (config_class, sizeof (NMConfigPrivate)); object_class->dispose = dispose; object_class->finalize = finalize; + + signals[SIGNAL_CONFIG_CHANGED] = + g_signal_new (NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMConfigClass, config_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_HASH_TABLE, NM_TYPE_CONFIG_DATA); } diff --git a/src/nm-config.h b/src/nm-config.h index 802e54ffe9..143edf597a 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -37,12 +37,18 @@ G_BEGIN_DECLS #define NM_IS_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONFIG)) #define NM_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONFIG, NMConfigClass)) +/* Signals */ +#define NM_CONFIG_SIGNAL_CONFIG_CHANGED "config-changed" + typedef struct { GObject parent; } NMConfig; typedef struct { GObjectClass parent; + + /* Signals */ + void (*config_changed) (NMConfig *config, GHashTable *changes, NMConfigData *old_data); } NMConfigClass; GType nm_config_get_type (void); @@ -74,6 +80,7 @@ GOptionEntry *nm_config_get_options (void); NMConfig *nm_config_new (const char *cli_log_level, const char *cli_log_domains, GError **error); +void nm_config_reload (NMConfig *config); G_END_DECLS -- cgit v1.2.1