diff options
Diffstat (limited to 'src/nm-config.c')
-rw-r--r-- | src/nm-config.c | 1191 |
1 files changed, 865 insertions, 326 deletions
diff --git a/src/nm-config.c b/src/nm-config.c index 4626ae264..86b3d9291 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -29,100 +29,213 @@ #include "nm-utils.h" #include "nm-glib-compat.h" #include "nm-device.h" +#include "NetworkManagerUtils.h" +#include "gsystem-local-alloc.h" +#include "nm-enum-types.h" +#include "nm-core-internal.h" #include <gio/gio.h> #include <glib/gi18n.h> -#define NM_DEFAULT_SYSTEM_CONF_FILE NMCONFDIR "/NetworkManager.conf" -#define NM_DEFAULT_SYSTEM_CONF_DIR NMCONFDIR "/conf.d" -#define NM_OLD_SYSTEM_CONF_FILE NMCONFDIR "/nm-system-settings.conf" -#define NM_NO_AUTO_DEFAULT_STATE_FILE NMSTATEDIR "/no-auto-default.state" +#define DEFAULT_CONFIG_MAIN_FILE NMCONFDIR "/NetworkManager.conf" +#define DEFAULT_CONFIG_DIR NMCONFDIR "/conf.d" +#define DEFAULT_CONFIG_MAIN_FILE_OLD NMCONFDIR "/nm-system-settings.conf" +#define DEFAULT_NO_AUTO_DEFAULT_FILE NMSTATEDIR "/no-auto-default.state" + +struct NMConfigCmdLineOptions { + char *config_main_file; + char *config_dir; + char *no_auto_default_file; + char *plugins; + gboolean configure_and_quit; + char *connectivity_uri; + + /* We store interval as signed internally to track whether it's + * set or not via GOptionEntry + */ + int connectivity_interval; + char *connectivity_response; +}; typedef struct { - char *nm_conf_path; + NMConfigCmdLineOptions cli; + + NMConfigData *config_data; + NMConfigData *config_data_orig; + char *config_dir; - char *config_description; char *no_auto_default_file; - GKeyFile *keyfile; char **plugins; gboolean monitor_connection_files; gboolean auth_polkit; char *dhcp_client; - char *dns_mode; char *log_level; char *log_domains; char *debug; - char *connectivity_uri; - gint connectivity_interval; - char *connectivity_response; - - char **no_auto_default; - char **ignore_carrier; - gboolean configure_and_quit; } NMConfigPrivate; -static NMConfig *singleton = NULL; +enum { + PROP_0, + PROP_CMD_LINE_OPTIONS, + LAST_PROP, +}; + +enum { + SIGNAL_CONFIG_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void nm_config_initable_iface_init (GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (NMConfig, nm_config, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_config_initable_iface_init); + ) -G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT) #define NM_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONFIG, NMConfigPrivate)) /************************************************************************/ -static gboolean -_get_bool_value (GKeyFile *keyfile, - const char *section, - const char *key, - gboolean default_value) +static void _set_config_data (NMConfig *self, NMConfigData *new_data, int signal); + +/************************************************************************/ + +gint +nm_config_parse_boolean (const char *str, + gint default_value) { - gboolean value = default_value; - char *str; + gsize len; + char *s = NULL; + + if (!str) + return default_value; + + while (str[0] && g_ascii_isspace (str[0])) + str++; + + if (!str[0]) + return default_value; + + len = strlen (str); + if (g_ascii_isspace (str[len - 1])) { + s = g_strdup (str); + g_strchomp (s); + str = s; + } + + if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1")) + default_value = TRUE; + else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0")) + default_value = FALSE; + if (s) + g_free (s); + return default_value; +} + +gint +nm_config_keyfile_get_boolean (GKeyFile *keyfile, + const char *section, + const char *key, + gint default_value) +{ + gs_free char *str = NULL; g_return_val_if_fail (keyfile != NULL, default_value); g_return_val_if_fail (section != NULL, default_value); g_return_val_if_fail (key != NULL, default_value); str = g_key_file_get_value (keyfile, section, key, NULL); - if (!str) - return default_value; + return nm_config_parse_boolean (str, default_value); +} - g_strstrip (str); - if (str[0]) { - if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1")) - value = TRUE; - else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0")) - value = FALSE; - else { - nm_log_warn (LOGD_CORE, "Unrecognized value for %s.%s: '%s'. Assuming '%s'", - section, key, str, default_value ? "true" : "false"); - } +char * +nm_config_keyfile_get_value (GKeyFile *keyfile, + const char *section, + const char *key, + NMConfigGetValueFlags flags) +{ + char *value; + + if (NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_RAW)) + value = g_key_file_get_value (keyfile, section, key, NULL); + else + value = g_key_file_get_string (keyfile, section, key, NULL); + + if (!value) + return NULL; + + if (NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_STRIP)) + g_strstrip (value); + + if ( NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_NO_EMPTY) + && !*value) { + g_free (value); + return NULL; } - g_free (str); return value; } +void +nm_config_keyfile_set_string_list (GKeyFile *keyfile, + const char *group, + const char *key, + const char *const* strv, + gssize len) +{ + gsize l; + char *new_value; + + if (len < 0) + len = strv ? g_strv_length ((char **) strv) : 0; + + g_key_file_set_string_list (keyfile, group, key, strv, len); + + /* g_key_file_set_string_list() appends a trailing separator to the value. + * We don't like that, get rid of it. */ + + new_value = g_key_file_get_value (keyfile, group, key, NULL); + if (!new_value) + return; + + l = strlen (new_value); + if (l > 0 && new_value[l - 1] == NM_CONFIG_KEYFILE_LIST_SEPARATOR) { + /* Maybe we should check that value doesn't end with "\\,", i.e. + * with an escaped separator. But the way g_key_file_set_string_list() + * is implemented (currently), it always adds a trailing separator. */ + new_value[l - 1] = '\0'; + g_key_file_set_value (keyfile, group, key, new_value); + } + g_free (new_value); +} + /************************************************************************/ -const char * -nm_config_get_path (NMConfig *config) +NMConfigData * +nm_config_get_data (NMConfig *config) { g_return_val_if_fail (config != NULL, NULL); - return NM_CONFIG_GET_PRIVATE (config)->nm_conf_path; + return NM_CONFIG_GET_PRIVATE (config)->config_data; } -const char * -nm_config_get_description (NMConfig *config) +/* The NMConfigData instance is reloadable and will be swapped on reload. + * nm_config_get_data_orig() returns the original configuration, when the NMConfig + * instance was created. */ +NMConfigData * +nm_config_get_data_orig (NMConfig *config) { g_return_val_if_fail (config != NULL, NULL); - return NM_CONFIG_GET_PRIVATE (config)->config_description; + return NM_CONFIG_GET_PRIVATE (config)->config_data_orig; } const char ** @@ -158,14 +271,6 @@ nm_config_get_dhcp_client (NMConfig *config) } const char * -nm_config_get_dns_mode (NMConfig *config) -{ - g_return_val_if_fail (config != NULL, NULL); - - return NM_CONFIG_GET_PRIVATE (config)->dns_mode; -} - -const char * nm_config_get_log_level (NMConfig *config) { g_return_val_if_fail (config != NULL, NULL); @@ -189,102 +294,32 @@ nm_config_get_debug (NMConfig *config) return NM_CONFIG_GET_PRIVATE (config)->debug; } -const char * -nm_config_get_connectivity_uri (NMConfig *config) -{ - g_return_val_if_fail (config != NULL, NULL); - - return NM_CONFIG_GET_PRIVATE (config)->connectivity_uri; -} - -guint -nm_config_get_connectivity_interval (NMConfig *config) -{ - g_return_val_if_fail (config != NULL, 0); - - /* We store interval as signed internally to track whether it's - * set or not, but report as unsigned to callers. - */ - return MAX (NM_CONFIG_GET_PRIVATE (config)->connectivity_interval, 0); -} - -const char * -nm_config_get_connectivity_response (NMConfig *config) -{ - g_return_val_if_fail (config != NULL, NULL); - - return NM_CONFIG_GET_PRIVATE (config)->connectivity_response; -} - gboolean nm_config_get_configure_and_quit (NMConfig *config) { return NM_CONFIG_GET_PRIVATE (config)->configure_and_quit; } -char * -nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error) -{ - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - - return g_key_file_get_string (priv->keyfile, group, key, error); -} - -gboolean -nm_config_get_ignore_carrier (NMConfig *config, NMDevice *device) -{ - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - GSList *specs = NULL; - int i; - gboolean match; - - if (!priv->ignore_carrier) - return FALSE; - - for (i = 0; priv->ignore_carrier[i]; i++) - specs = g_slist_prepend (specs, priv->ignore_carrier[i]); - - match = nm_device_spec_match_list (device, specs); - - g_slist_free (specs); - return match; -} - /************************************************************************/ -static void -merge_no_auto_default_state (NMConfig *config) +static char ** +no_auto_default_from_file (const char *no_auto_default_file) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - GPtrArray *updated; + GPtrArray *no_auto_default_new; char **list; - int i, j; + guint i; char *data; - /* If the config already matches everything, we don't need to do anything else. */ - if (priv->no_auto_default && !g_strcmp0 (priv->no_auto_default[0], "*")) - return; + no_auto_default_new = g_ptr_array_new (); - updated = g_ptr_array_new (); - if (priv->no_auto_default) { - for (i = 0; priv->no_auto_default[i]; i++) - g_ptr_array_add (updated, priv->no_auto_default[i]); - g_free (priv->no_auto_default); - } - - if (g_file_get_contents (priv->no_auto_default_file, &data, NULL, NULL)) { + if ( no_auto_default_file + && g_file_get_contents (no_auto_default_file, &data, NULL, NULL)) { list = g_strsplit (data, "\n", -1); for (i = 0; list[i]; i++) { - if (!*list[i]) { - g_free (list[i]); - continue; - } - for (j = 0; j < updated->len; j++) { - if (!strcmp (list[i], updated->pdata[j])) - break; - } - if (j == updated->len) - g_ptr_array_add (updated, list[i]); + if ( *list[i] + && nm_utils_hwaddr_valid (list[i], -1) + && _nm_utils_strv_find_first (list, i, list[i]) < 0) + g_ptr_array_add (no_auto_default_new, list[i]); else g_free (list[i]); } @@ -292,99 +327,240 @@ merge_no_auto_default_state (NMConfig *config) g_free (data); } - g_ptr_array_add (updated, NULL); - priv->no_auto_default = (char **) g_ptr_array_free (updated, FALSE); + g_ptr_array_add (no_auto_default_new, NULL); + return (char **) g_ptr_array_free (no_auto_default_new, FALSE); } -gboolean -nm_config_get_ethernet_can_auto_default (NMConfig *config, NMDevice *device) +static gboolean +no_auto_default_to_file (const char *no_auto_default_file, const char *const*no_auto_default, GError **error) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - GSList *specs = NULL; - int i; - gboolean match; - - for (i = 0; priv->no_auto_default[i]; i++) - specs = g_slist_prepend (specs, priv->no_auto_default[i]); + GString *data; + gboolean success; + guint i; + + data = g_string_new (""); + for (i = 0; no_auto_default && no_auto_default[i]; i++) { + g_string_append (data, no_auto_default[i]); + g_string_append_c (data, '\n'); + } + success = g_file_set_contents (no_auto_default_file, data->str, data->len, error); + g_string_free (data, TRUE); + return success; +} - match = nm_device_spec_match_list (device, specs); +gboolean +nm_config_get_no_auto_default_for_device (NMConfig *self, NMDevice *device) +{ + g_return_val_if_fail (NM_IS_CONFIG (self), FALSE); - g_slist_free (specs); - return !match; + return nm_config_data_get_no_auto_default_for_device (NM_CONFIG_GET_PRIVATE (self)->config_data, device); } void -nm_config_set_ethernet_no_auto_default (NMConfig *config, NMDevice *device) +nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - char *current; - GString *updated; + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); GError *error = NULL; + NMConfigData *new_data = NULL; + const char *hw_address; + const char *const*no_auto_default_current; + GPtrArray *no_auto_default_new = NULL; + guint i; - if (!nm_config_get_ethernet_can_auto_default (config, device)) - return; + g_return_if_fail (NM_IS_CONFIG (self)); + g_return_if_fail (NM_IS_DEVICE (device)); + + hw_address = nm_device_get_hw_address (device); - updated = g_string_new (NULL); - if (g_file_get_contents (priv->no_auto_default_file, ¤t, NULL, NULL)) { - g_string_append (updated, current); - g_free (current); - if (updated->str[updated->len - 1] != '\n') - g_string_append_c (updated, '\n'); + no_auto_default_current = nm_config_data_get_no_auto_default (priv->config_data); + + if (_nm_utils_strv_find_first ((char **) no_auto_default_current, -1, hw_address) >= 0) { + /* @hw_address is already blocked. We don't have to update our in-memory representation. + * Maybe we should write to no_auto_default_file anew, but let's save that too. */ + return; } - g_string_append (updated, nm_device_get_hw_address (device)); - g_string_append_c (updated, '\n'); + no_auto_default_new = g_ptr_array_new (); + for (i = 0; no_auto_default_current && no_auto_default_current[i]; i++) + g_ptr_array_add (no_auto_default_new, (char *) no_auto_default_current[i]); + g_ptr_array_add (no_auto_default_new, (char *) hw_address); + g_ptr_array_add (no_auto_default_new, NULL); - if (!g_file_set_contents (priv->no_auto_default_file, updated->str, updated->len, &error)) { + if (!no_auto_default_to_file (priv->no_auto_default_file, (const char *const*) no_auto_default_new->pdata, &error)) { nm_log_warn (LOGD_SETTINGS, "Could not update no-auto-default.state file: %s", error->message); g_error_free (error); } - g_string_free (updated, TRUE); + new_data = nm_config_data_new_update_no_auto_default (priv->config_data, (const char *const*) no_auto_default_new->pdata); - merge_no_auto_default_state (config); + /* unref no_auto_default_set here. Note that _set_config_data() probably invalidates the content of the array. */ + g_ptr_array_unref (no_auto_default_new); + + _set_config_data (self, new_data, 0); } /************************************************************************/ -static char *cli_config_path; -static char *cli_config_dir; -static char *cli_no_auto_default_file; -static char *cli_plugins; -static char *cli_connectivity_uri; -static int cli_connectivity_interval = -1; -static char *cli_connectivity_response; - -static GOptionEntry config_options[] = { - { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_path, N_("Config file location"), N_("/path/to/config.file") }, - { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_dir, N_("Config directory location"), N_("/path/to/config/dir") }, - { "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli_no_auto_default_file, "no-auto-default.state location", NULL }, - { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli_plugins, N_("List of plugins separated by ','"), N_("plugin1,plugin2") }, - - /* These three are hidden for now, and should eventually just go away. */ - { "connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli_connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com" }, - { "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli_connectivity_interval, N_("The interval between connectivity checks (in seconds)"), "60" }, - { "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli_connectivity_response, N_("The expected start of the response"), N_("Bingo!") }, - {NULL} -}; -GOptionEntry * -nm_config_get_options (void) +static void +_nm_config_cmd_line_options_clear (NMConfigCmdLineOptions *cli) +{ + g_clear_pointer (&cli->config_main_file, g_free); + g_clear_pointer (&cli->config_dir, g_free); + g_clear_pointer (&cli->no_auto_default_file, g_free); + g_clear_pointer (&cli->plugins, g_free); + cli->configure_and_quit = FALSE; + g_clear_pointer (&cli->connectivity_uri, g_free); + g_clear_pointer (&cli->connectivity_response, g_free); + cli->connectivity_interval = -1; +} + +static void +_nm_config_cmd_line_options_copy (const NMConfigCmdLineOptions *cli, NMConfigCmdLineOptions *dst) +{ + g_return_if_fail (cli); + g_return_if_fail (dst); + g_return_if_fail (cli != dst); + + _nm_config_cmd_line_options_clear (dst); + dst->config_dir = g_strdup (cli->config_dir); + dst->config_main_file = g_strdup (cli->config_main_file); + dst->no_auto_default_file = g_strdup (cli->no_auto_default_file); + dst->plugins = g_strdup (cli->plugins); + dst->configure_and_quit = cli->configure_and_quit; + dst->connectivity_uri = g_strdup (cli->connectivity_uri); + dst->connectivity_response = g_strdup (cli->connectivity_response); + dst->connectivity_interval = cli->connectivity_interval; +} + +NMConfigCmdLineOptions * +nm_config_cmd_line_options_new () +{ + NMConfigCmdLineOptions *cli = g_new0 (NMConfigCmdLineOptions, 1); + + _nm_config_cmd_line_options_clear (cli); + return cli; +} + +void +nm_config_cmd_line_options_free (NMConfigCmdLineOptions *cli) +{ + g_return_if_fail (cli); + + _nm_config_cmd_line_options_clear (cli); + g_free (cli); +} + +void +nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli, + GOptionContext *opt_ctx) { - return config_options; + g_return_if_fail (opt_ctx); + g_return_if_fail (cli); + + { + GOptionEntry config_options[] = { + { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), N_(DEFAULT_CONFIG_MAIN_FILE) }, + { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), N_(DEFAULT_CONFIG_DIR) }, + { "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), N_(DEFAULT_NO_AUTO_DEFAULT_FILE) }, + { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), N_(CONFIG_PLUGINS_DEFAULT) }, + { "configure-and-quit", 0, 0, G_OPTION_ARG_NONE, &cli->configure_and_quit, N_("Quit after initial configuration"), NULL }, + + /* These three are hidden for now, and should eventually just go away. */ + { "connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com" }, + { "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli->connectivity_interval, N_("The interval between connectivity checks (in seconds)"), G_STRINGIFY (NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL) }, + { "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_response, N_("The expected start of the response"), N_(NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE) }, + { 0 }, + }; + + g_option_context_add_main_entries (opt_ctx, config_options, NULL); + } } /************************************************************************/ +GKeyFile * +nm_config_create_keyfile () +{ + GKeyFile *keyfile; + + keyfile = g_key_file_new (); + g_key_file_set_list_separator (keyfile, NM_CONFIG_KEYFILE_LIST_SEPARATOR); + return keyfile; +} + +static int +_sort_groups_cmp (const char **pa, const char **pb, gpointer dummy) +{ + const char *a, *b; + gboolean a_is_connection, b_is_connection; + + /* basic NULL checking... */ + if (pa == pb) + return 0; + if (!pa) + return -1; + if (!pb) + return 1; + + a = *pa; + b = *pb; + + a_is_connection = g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); + b_is_connection = g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); + + if (a_is_connection != b_is_connection) { + /* one is a [connection*] entry, the other not. We sort [connection*] entires + * after. */ + if (a_is_connection) + return 1; + return -1; + } + if (!a_is_connection) { + /* both are non-connection entries. Don't reorder. */ + return 0; + } + + /* both are [connection.\+] entires. Reverse their order. + * One of the sections might be literally [connection]. That section + * is special and it's order will be fixed later. It doesn't actually + * matter here how it compares with [connection.\+] sections. */ + return pa > pb ? -1 : 1; +} + static gboolean -read_config (NMConfig *config, const char *path, GError **error) +_setting_is_device_spec (const char *group, const char *key) +{ +#define _IS(group_v, key_v) (strcmp (group, (""group_v)) == 0 && strcmp (key, (""key_v)) == 0) + return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default") + || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier") + || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only") + || _IS (NM_CONFIG_KEYFILE_GROUP_KEYFILE, "unmanaged-devices") + || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && !strcmp (key, "match-device")); +} + +static gboolean +_setting_is_string_list (const char *group, const char *key) +{ + return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins") + || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "debug") + || _IS (NM_CONFIG_KEYFILE_GROUP_LOGGING, "domains") + || g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST); +#undef _IS +} + +static gboolean +read_config (GKeyFile *keyfile, const char *path, GError **error) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); GKeyFile *kf; char **groups, **keys; gsize ngroups, nkeys; int g, k; + g_return_val_if_fail (keyfile, FALSE); + g_return_val_if_fail (path, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + if (g_file_test (path, G_FILE_TEST_EXISTS) == FALSE) { g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND, "file %s not found", path); return FALSE; @@ -392,8 +568,7 @@ read_config (NMConfig *config, const char *path, GError **error) nm_log_dbg (LOGD_SETTINGS, "Reading config file '%s'", path); - kf = g_key_file_new (); - g_key_file_set_list_separator (kf, ','); + kf = nm_config_create_keyfile (); if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, error)) { g_key_file_free (kf); return FALSE; @@ -401,36 +576,116 @@ read_config (NMConfig *config, const char *path, GError **error) /* Override the current settings with the new ones */ groups = g_key_file_get_groups (kf, &ngroups); - for (g = 0; groups[g]; g++) { - keys = g_key_file_get_keys (kf, groups[g], &nkeys, NULL); + if (!groups) + ngroups = 0; + + /* Within one file we reverse the order of the '[connection.\+] sections. + * Here we merge the current file (@kf) into @keyfile. As we merge multiple + * files, earlier sections (with lower priority) will be added first. + * But within one file, we want a top-to-bottom order. This means we + * must reverse the order within each file. + * At the very end, we will revert the order of all sections again and + * get thus the right behavior. This final reversing is done in + * NMConfigData:_get_connection_infos(). */ + if (ngroups > 1) { + g_qsort_with_data (groups, + ngroups, + sizeof (char *), + (GCompareDataFunc) _sort_groups_cmp, + NULL); + } + + for (g = 0; groups && groups[g]; g++) { + const char *group = groups[g]; + + keys = g_key_file_get_keys (kf, group, &nkeys, NULL); if (!keys) continue; for (k = 0; keys[k]; k++) { - int len = strlen (keys[k]); - char *v; - - if (keys[k][len - 1] == '+') { - char *base_key = g_strndup (keys[k], len - 1); - char *old_val = g_key_file_get_value (priv->keyfile, groups[g], base_key, NULL); - 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_free (combined); - } else - g_key_file_set_value (priv->keyfile, groups[g], base_key, new_val); - - g_free (base_key); - g_free (old_val); - g_free (new_val); + const char *key; + char *new_value; + char last_char; + gsize key_len; + + key = keys[k]; + g_assert (key && *key); + key_len = strlen (key); + last_char = key[key_len - 1]; + if ( key_len > 1 + && (last_char == '+' || last_char == '-')) { + gs_free char *base_key = g_strndup (key, key_len - 1); + gboolean is_string_list; + + is_string_list = _setting_is_string_list (group, base_key); + + if ( is_string_list + || _setting_is_device_spec (group, base_key)) { + gs_unref_ptrarray GPtrArray *new = g_ptr_array_new_with_free_func (g_free); + char **iter_val; + gs_strfreev char **old_val = NULL; + gs_free char **new_val = NULL; + + if (is_string_list) { + old_val = g_key_file_get_string_list (keyfile, group, base_key, NULL, NULL); + new_val = g_key_file_get_string_list (kf, group, key, NULL, NULL); + } else { + gs_free char *old_sval = nm_config_keyfile_get_value (keyfile, group, base_key, NM_CONFIG_GET_VALUE_TYPE_SPEC); + gs_free char *new_sval = nm_config_keyfile_get_value (kf, group, key, NM_CONFIG_GET_VALUE_TYPE_SPEC); + gs_free_slist GSList *old_specs = nm_match_spec_split (old_sval); + gs_free_slist GSList *new_specs = nm_match_spec_split (new_sval); + + /* the key is a device spec. This is a special kind of string-list, that + * we must split differently. */ + old_val = _nm_utils_slist_to_strv (old_specs, FALSE); + new_val = _nm_utils_slist_to_strv (new_specs, FALSE); + } + + /* merge the string lists, by omiting duplicates. */ + + for (iter_val = old_val; iter_val && *iter_val; iter_val++) { + if ( last_char != '-' + || _nm_utils_strv_find_first (new_val, -1, *iter_val) < 0) + g_ptr_array_add (new, g_strdup (*iter_val)); + } + for (iter_val = new_val; iter_val && *iter_val; iter_val++) { + /* don't add duplicates. That means an "option=a,b"; "option+=a,c" results in "option=a,b,c" */ + if ( last_char == '+' + && _nm_utils_strv_find_first (old_val, -1, *iter_val) < 0) + g_ptr_array_add (new, *iter_val); + else + g_free (*iter_val); + } + + if (new->len > 0) { + if (is_string_list) + nm_config_keyfile_set_string_list (keyfile, group, base_key, (const char *const*) new->pdata, new->len); + else { + gs_free_slist GSList *specs = NULL; + gs_free char *specs_joined = NULL; + + g_ptr_array_add (new, NULL); + specs = _nm_utils_strv_to_slist ((char **) new->pdata, FALSE); + + specs_joined = nm_match_spec_join (specs); + + g_key_file_set_value (keyfile, group, base_key, specs_joined); + } + } else { + if (is_string_list) + g_key_file_remove_key (keyfile, group, base_key, NULL); + else + g_key_file_set_value (keyfile, group, base_key, ""); + } + } else { + /* For any other settings we don't support extending the option with +/-. + * Just drop the key. */ + } continue; } - g_key_file_set_value (priv->keyfile, groups[g], keys[k], - v = g_key_file_get_value (kf, groups[g], keys[k], NULL)); - g_free (v); + new_value = g_key_file_get_value (kf, group, key, NULL); + g_key_file_set_value (keyfile, group, key, new_value); + g_free (new_value); } g_strfreev (keys); } @@ -441,16 +696,22 @@ read_config (NMConfig *config, const char *path, GError **error) } static gboolean -find_base_config (NMConfig *config, GError **error) +read_base_config (GKeyFile *keyfile, + const char *cli_config_main_file, + char **out_config_main_file, + GError **error) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); GError *my_error = NULL; + g_return_val_if_fail (keyfile, FALSE); + g_return_val_if_fail (out_config_main_file && !*out_config_main_file, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + /* Try a user-specified config file first */ - if (cli_config_path) { + if (cli_config_main_file) { /* Bad user-specific config file path is a hard error */ - if (read_config (config, cli_config_path, error)) { - priv->nm_conf_path = g_strdup (cli_config_path); + if (read_config (keyfile, cli_config_main_file, error)) { + *out_config_main_file = g_strdup (cli_config_main_file); return TRUE; } else return FALSE; @@ -464,27 +725,27 @@ 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)) { - priv->nm_conf_path = g_strdup (NM_OLD_SYSTEM_CONF_FILE); + if (read_config (keyfile, DEFAULT_CONFIG_MAIN_FILE_OLD, &my_error)) { + *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE_OLD); return TRUE; } if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { nm_log_warn (LOGD_CORE, "Old default config file %s invalid: %s\n", - NM_OLD_SYSTEM_CONF_FILE, + DEFAULT_CONFIG_MAIN_FILE_OLD, my_error->message); } g_clear_error (&my_error); /* Try the standard config file location next */ - if (read_config (config, NM_DEFAULT_SYSTEM_CONF_FILE, &my_error)) { - priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE); + if (read_config (keyfile, DEFAULT_CONFIG_MAIN_FILE, &my_error)) { + *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE); return TRUE; } if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { nm_log_warn (LOGD_CORE, "Default config file %s invalid: %s\n", - NM_DEFAULT_SYSTEM_CONF_FILE, + DEFAULT_CONFIG_MAIN_FILE, my_error->message); g_propagate_error (error, my_error); return FALSE; @@ -494,21 +755,12 @@ find_base_config (NMConfig *config, GError **error) /* If for some reason no config file exists, use the default * config file path. */ - priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE); + *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE); nm_log_info (LOGD_CORE, "No config file found or given; using %s\n", - NM_DEFAULT_SYSTEM_CONF_FILE); + DEFAULT_CONFIG_MAIN_FILE); return TRUE; } -/************************************************************************/ - -NMConfig * -nm_config_get (void) -{ - g_assert (singleton); - return singleton; -} - static int sort_asciibetically (gconstpointer a, gconstpointer b) { @@ -518,114 +770,374 @@ sort_asciibetically (gconstpointer a, gconstpointer b) return strcmp (s1, s2); } -/* call this function only once! */ -NMConfig * -nm_config_new (GError **error) +static GPtrArray * +_get_config_dir_files (const char *config_main_file, + const char *config_dir, + char **out_config_description) { - NMConfigPrivate *priv = NULL; GFile *dir; GFileEnumerator *direnum; GFileInfo *info; GPtrArray *confs; - const char *name; - int i; GString *config_description; + const char *name; + guint i; - 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)) { - g_object_unref (singleton); - singleton = NULL; - return NULL; - } - - /* 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); + g_return_val_if_fail (config_main_file, NULL); + g_return_val_if_fail (config_dir, NULL); + g_return_val_if_fail (out_config_description && !*out_config_description, NULL); 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 (config_main_file); + 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)); - if (confs->len == 1) - g_string_append (config_description, " and conf.d: "); - else - g_string_append (config_description, ", "); - g_string_append (config_description, name); - } + if (g_str_has_suffix (name, ".conf")) + g_ptr_array_add (confs, g_strdup (name)); g_object_unref (info); } g_object_unref (direnum); } g_object_unref (dir); - g_ptr_array_sort (confs, sort_asciibetically); - priv->config_description = g_string_free (config_description, FALSE); + if (confs->len > 0) { + g_ptr_array_sort (confs, sort_asciibetically); + g_string_append (config_description, " and conf.d: "); + for (i = 0; i < confs->len; i++) { + char *n = confs->pdata[i]; + + if (i > 0) + g_string_append (config_description, ", "); + g_string_append (config_description, n); + confs->pdata[i] = g_build_filename (config_dir, n, NULL); + g_free (n); + } + } + + *out_config_description = g_string_free (config_description, FALSE); + return confs; +} + +static GKeyFile * +read_entire_config (const NMConfigCmdLineOptions *cli, + const char *config_dir, + char **out_config_main_file, + char **out_config_description, + GError **error) +{ + GKeyFile *keyfile = nm_config_create_keyfile (); + GPtrArray *confs; + guint i; + char *o_config_main_file = NULL; + char *o_config_description = NULL; + char **plugins_tmp; + + g_return_val_if_fail (config_dir, NULL); + g_return_val_if_fail (out_config_main_file && !*out_config_main_file, FALSE); + g_return_val_if_fail (out_config_description && !*out_config_description, NULL); + g_return_val_if_fail (!error || !*error, FALSE); + + /* First read the base config file */ + if (!read_base_config (keyfile, cli ? cli->config_main_file : NULL, &o_config_main_file, error)) { + g_key_file_free (keyfile); + return NULL; + } + + g_assert (o_config_main_file); + + confs = _get_config_dir_files (o_config_main_file, config_dir, &o_config_description); for (i = 0; i < confs->len; i++) { - if (!read_config (singleton, confs->pdata[i], error)) { - g_object_unref (singleton); - singleton = NULL; - break; + if (!read_config (keyfile, confs->pdata[i], error)) { + g_key_file_free (keyfile); + g_free (o_config_main_file); + g_free (o_config_description); + g_ptr_array_unref (confs); + return NULL; } } g_ptr_array_unref (confs); - if (!singleton) + + /* Merge settings from command line. They overwrite everything read from + * config files. */ + + if (cli && cli->plugins && cli->plugins[0]) + g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", cli->plugins); + plugins_tmp = g_key_file_get_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NULL, NULL); + if (!plugins_tmp) { + if (STRLEN (CONFIG_PLUGINS_DEFAULT) > 0) + g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", CONFIG_PLUGINS_DEFAULT); + } else + g_strfreev (plugins_tmp); + + if (cli && cli->configure_and_quit) + g_key_file_set_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", TRUE); + + if (cli && cli->connectivity_uri && cli->connectivity_uri[0]) + g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", cli->connectivity_uri); + if (cli && cli->connectivity_interval >= 0) + g_key_file_set_integer (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", cli->connectivity_interval); + if (cli && cli->connectivity_response && cli->connectivity_response[0]) + g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", cli->connectivity_response); + + *out_config_main_file = o_config_main_file; + *out_config_description = o_config_description; + return keyfile; +} + +GSList * +nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key, gboolean *out_has_key) +{ + gs_free char *value = NULL; + + /* nm_match_spec_split() already supports full escaping and is basically + * a modified version of g_key_file_parse_value_as_string(). So we first read + * the raw value (g_key_file_get_value()), and do the parsing ourselves. */ + value = g_key_file_get_value ((GKeyFile *) keyfile, group, key, NULL); + if (out_has_key) + *out_has_key = !!value; + return nm_match_spec_split (value); +} + +/************************************************************************/ + +void +nm_config_reload (NMConfig *self, int signal) +{ + NMConfigPrivate *priv; + GError *error = NULL; + GKeyFile *keyfile; + NMConfigData *new_data = NULL; + char *config_main_file = NULL; + char *config_description = NULL; + gs_strfreev char **no_auto_default = NULL; + + g_return_if_fail (NM_IS_CONFIG (self)); + + priv = NM_CONFIG_GET_PRIVATE (self); + + if (signal != SIGHUP) { + _set_config_data (self, NULL, signal); + return; + } + + /* pass on the original command line options. This means, that + * options specified at command line cannot ever be reloaded from + * file. That seems desirable. + */ + keyfile = read_entire_config (&priv->cli, + priv->config_dir, + &config_main_file, + &config_description, + &error); + if (!keyfile) { + nm_log_err (LOGD_CORE, "Failed to reload the configuration: %s", error->message); + g_clear_error (&error); + _set_config_data (self, NULL, signal); + return; + } + no_auto_default = no_auto_default_from_file (priv->no_auto_default_file); + + new_data = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile); + g_free (config_main_file); + g_free (config_description); + g_key_file_unref (keyfile); + + _set_config_data (self, new_data, signal); +} + +static const char * +_change_flags_one_to_string (NMConfigChangeFlags flag) +{ + switch (flag) { + case NM_CONFIG_CHANGE_SIGHUP: + return "SIGHUP"; + case NM_CONFIG_CHANGE_SIGUSR1: + return "SIGUSR1"; + case NM_CONFIG_CHANGE_SIGUSR2: + return "SIGUSR2"; + case NM_CONFIG_CHANGE_CONFIG_FILES: + return "config-files"; + case NM_CONFIG_CHANGE_VALUES: + return "values"; + case NM_CONFIG_CHANGE_CONNECTIVITY: + return "connectivity"; + case NM_CONFIG_CHANGE_NO_AUTO_DEFAULT: + return "no-auto-default"; + case NM_CONFIG_CHANGE_DNS_MODE: + return "dns-mode"; + default: + g_return_val_if_reached ("unknown"); + } +} + +char * +nm_config_change_flags_to_string (NMConfigChangeFlags flags) +{ + GString *str = g_string_new (""); + NMConfigChangeFlags s = 0x01; + + while (flags) { + if (NM_FLAGS_HAS (flags, s)) { + if (str->len) + g_string_append_c (str, ','); + g_string_append (str, _change_flags_one_to_string (s)); + } + flags = flags & ~s; + s <<= 1; + } + return g_string_free (str, FALSE); +} + +static void +_set_config_data (NMConfig *self, NMConfigData *new_data, int signal) +{ + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); + NMConfigData *old_data = priv->config_data; + NMConfigChangeFlags changes, changes_diff; + gs_free char *log_str = NULL; + gboolean had_new_data = !!new_data; + + switch (signal) { + case SIGHUP: + changes = NM_CONFIG_CHANGE_SIGHUP; + break; + case SIGUSR1: + changes = NM_CONFIG_CHANGE_SIGUSR1; + break; + case SIGUSR2: + changes = NM_CONFIG_CHANGE_SIGUSR2; + break; + default: + changes = NM_CONFIG_CHANGE_NONE; + break; + } + + if (new_data) { + changes_diff = nm_config_data_diff (old_data, new_data); + if (changes_diff == NM_CONFIG_CHANGE_NONE) + g_clear_object (&new_data); + else + changes |= changes_diff; + } + + if (changes == NM_CONFIG_CHANGE_NONE) + return; + + if (new_data) { + nm_log_info (LOGD_CORE, "config: update %s (%s)", nm_config_data_get_config_description (new_data), + (log_str = nm_config_change_flags_to_string (changes))); + nm_config_data_log (new_data, "CONFIG: "); + priv->config_data = new_data; + } else if (had_new_data) + nm_log_info (LOGD_CORE, "config: signal %s (no changes from disk)", (log_str = nm_config_change_flags_to_string (changes))); + else + nm_log_info (LOGD_CORE, "config: signal %s", (log_str = nm_config_change_flags_to_string (changes))); + g_signal_emit (self, signals[SIGNAL_CONFIG_CHANGED], 0, + new_data ? new_data : old_data, + changes, old_data); + if (new_data) + g_object_unref (old_data); +} + +NM_DEFINE_SINGLETON_DESTRUCTOR (NMConfig); +NM_DEFINE_SINGLETON_WEAK_REF (NMConfig); + +NMConfig * +nm_config_get (void) +{ + g_assert (singleton_instance); + return singleton_instance; +} + +NMConfig * +nm_config_setup (const NMConfigCmdLineOptions *cli, GError **error) +{ + g_assert (!singleton_instance); + + singleton_instance = nm_config_new (cli, error); + if (singleton_instance) + nm_singleton_instance_weak_ref_register (); + return singleton_instance; +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMConfig *self = NM_CONFIG (initable); + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); + GKeyFile *keyfile; + char *config_main_file = NULL; + char *config_description = NULL; + gs_strfreev char **no_auto_default = NULL; + + if (priv->config_dir) { + /* Object is already initialized. */ + if (priv->config_data) + return TRUE; + g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND, "unspecified error"); + return FALSE; + } + + if (priv->cli.config_dir) + priv->config_dir = g_strdup (priv->cli.config_dir); + else + priv->config_dir = g_strdup (DEFAULT_CONFIG_DIR); + + keyfile = read_entire_config (&priv->cli, + priv->config_dir, + &config_main_file, + &config_description, + error); + if (!keyfile) return FALSE; - /* 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); - if (cli_no_auto_default_file) - priv->no_auto_default_file = g_strdup (cli_no_auto_default_file); + /* Initialize read only private members */ + + if (priv->cli.no_auto_default_file) + priv->no_auto_default_file = g_strdup (priv->cli.no_auto_default_file); else - priv->no_auto_default_file = g_strdup (NM_NO_AUTO_DEFAULT_STATE_FILE); - merge_no_auto_default_state (singleton); + priv->no_auto_default_file = g_strdup (DEFAULT_NO_AUTO_DEFAULT_FILE); - /* Now let command-line options override the config files, and fill in priv. */ - if (cli_plugins && cli_plugins[0]) - g_key_file_set_value (priv->keyfile, "main", "plugins", cli_plugins); - priv->plugins = g_key_file_get_string_list (priv->keyfile, "main", "plugins", NULL, NULL); - if (!priv->plugins && STRLEN (CONFIG_PLUGINS_DEFAULT) > 0) - priv->plugins = g_strsplit (CONFIG_PLUGINS_DEFAULT, ",", -1); + priv->plugins = _nm_utils_strv_cleanup (g_key_file_get_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NULL, NULL), + TRUE, TRUE, TRUE); + if (!priv->plugins) + priv->plugins = g_new0 (char *, 1); - priv->monitor_connection_files = _get_bool_value (priv->keyfile, "main", "monitor-connection-files", FALSE); + priv->monitor_connection_files = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "monitor-connection-files", FALSE); - priv->auth_polkit = _get_bool_value (priv->keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT); + priv->auth_polkit = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT); - priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL); - priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL); + priv->dhcp_client = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "dhcp", NULL)); - priv->log_level = g_key_file_get_value (priv->keyfile, "logging", "level", NULL); - priv->log_domains = g_key_file_get_value (priv->keyfile, "logging", "domains", NULL); + priv->log_level = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "level", NULL)); + priv->log_domains = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "domains", NULL)); - priv->debug = g_key_file_get_value (priv->keyfile, "main", "debug", NULL); + priv->debug = g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "debug", NULL); - if (cli_connectivity_uri && cli_connectivity_uri[0]) - g_key_file_set_value (priv->keyfile, "connectivity", "uri", cli_connectivity_uri); - priv->connectivity_uri = g_key_file_get_value (priv->keyfile, "connectivity", "uri", NULL); + priv->configure_and_quit = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", FALSE); - if (cli_connectivity_interval >= 0) - g_key_file_set_integer (priv->keyfile, "connectivity", "interval", cli_connectivity_interval); - priv->connectivity_interval = g_key_file_get_integer (priv->keyfile, "connectivity", "interval", NULL); + no_auto_default = no_auto_default_from_file (priv->no_auto_default_file); - if (cli_connectivity_response && cli_connectivity_response[0]) - g_key_file_set_value (priv->keyfile, "connectivity", "response", cli_connectivity_response); - priv->connectivity_response = g_key_file_get_value (priv->keyfile, "connectivity", "response", NULL); + priv->config_data_orig = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile); - priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL); + priv->config_data = g_object_ref (priv->config_data_orig); - priv->configure_and_quit = _get_bool_value (priv->keyfile, "main", "configure-and-quit", FALSE); + g_free (config_main_file); + g_free (config_description); + g_key_file_unref (keyfile); + return TRUE; +} - return singleton; +NMConfig * +nm_config_new (const NMConfigCmdLineOptions *cli, GError **error) +{ + return NM_CONFIG (g_initable_new (NM_TYPE_CONFIG, + NULL, + error, + NM_CONFIG_CMD_LINE_OPTIONS, cli, + NULL)); } static void @@ -634,11 +1146,6 @@ nm_config_init (NMConfig *config) NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); priv->auth_polkit = NM_CONFIG_DEFAULT_AUTH_POLKIT; - - priv->keyfile = g_key_file_new (); - g_key_file_set_list_separator (priv->keyfile, ','); - - priv->connectivity_interval = -1; } static void @@ -646,34 +1153,44 @@ finalize (GObject *gobject) { NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject); - g_free (priv->nm_conf_path); g_free (priv->config_dir); - g_free (priv->config_description); g_free (priv->no_auto_default_file); - g_clear_pointer (&priv->keyfile, g_key_file_unref); g_strfreev (priv->plugins); g_free (priv->dhcp_client); - g_free (priv->dns_mode); g_free (priv->log_level); g_free (priv->log_domains); g_free (priv->debug); - g_free (priv->connectivity_uri); - g_free (priv->connectivity_response); - g_strfreev (priv->no_auto_default); - g_strfreev (priv->ignore_carrier); - singleton = NULL; + _nm_config_cmd_line_options_clear (&priv->cli); - g_clear_pointer (&cli_config_path, g_free); - g_clear_pointer (&cli_config_dir, g_free); - g_clear_pointer (&cli_no_auto_default_file, g_free); - g_clear_pointer (&cli_plugins, g_free); - g_clear_pointer (&cli_connectivity_uri, g_free); - g_clear_pointer (&cli_connectivity_response, g_free); + g_clear_object (&priv->config_data); + g_clear_object (&priv->config_data_orig); G_OBJECT_CLASS (nm_config_parent_class)->finalize (gobject); } +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMConfig *self = NM_CONFIG (object); + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); + NMConfigCmdLineOptions *cli; + + switch (prop_id) { + case PROP_CMD_LINE_OPTIONS: + /* construct only */ + cli = g_value_get_pointer (value); + if (!cli) + _nm_config_cmd_line_options_clear (&priv->cli); + else + _nm_config_cmd_line_options_copy (cli, &priv->cli); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} static void nm_config_class_init (NMConfigClass *config_class) @@ -682,5 +1199,27 @@ nm_config_class_init (NMConfigClass *config_class) g_type_class_add_private (config_class, sizeof (NMConfigPrivate)); object_class->finalize = finalize; + object_class->set_property = set_property; + + g_object_class_install_property + (object_class, PROP_CMD_LINE_OPTIONS, + g_param_spec_pointer (NM_CONFIG_CMD_LINE_OPTIONS, "", "", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + 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, 3, NM_TYPE_CONFIG_DATA, NM_TYPE_CONFIG_CHANGE_FLAGS, NM_TYPE_CONFIG_DATA); +} + +static void +nm_config_initable_iface_init (GInitableIface *iface) +{ + iface->init = init_sync; } |