summaryrefslogtreecommitdiff
path: root/src/nm-config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-config.c')
-rw-r--r--src/nm-config.c1191
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, &current, 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;
}