diff options
Diffstat (limited to 'src/nm-config.c')
| -rw-r--r-- | src/nm-config.c | 4981 |
1 files changed, 2585 insertions, 2396 deletions
diff --git a/src/nm-config.c b/src/nm-config.c index 4fa85319..1d751343 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2011 Red Hat, Inc. * Copyright (C) 2013 Thomas Bechtold <thomasbechtold@jpberlin.de> @@ -17,1404 +17,1506 @@ #include "nm-keyfile/nm-keyfile-internal.h" #include "nm-keyfile/nm-keyfile-utils.h" -#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_SYSTEM_CONFIG_DIR NMLIBDIR "/conf.d" -#define RUN_CONFIG_DIR NMRUNDIR "/conf.d" -#define DEFAULT_NO_AUTO_DEFAULT_FILE NMSTATEDIR "/no-auto-default.state" -#define DEFAULT_INTERN_CONFIG_FILE NMSTATEDIR "/NetworkManager-intern.conf" -#define DEFAULT_STATE_FILE NMSTATEDIR "/NetworkManager.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_SYSTEM_CONFIG_DIR NMLIBDIR "/conf.d" +#define RUN_CONFIG_DIR NMRUNDIR "/conf.d" +#define DEFAULT_NO_AUTO_DEFAULT_FILE NMSTATEDIR "/no-auto-default.state" +#define DEFAULT_INTERN_CONFIG_FILE NMSTATEDIR "/NetworkManager-intern.conf" +#define DEFAULT_STATE_FILE NMSTATEDIR "/NetworkManager.state" /*****************************************************************************/ struct NMConfigCmdLineOptions { - char *config_main_file; - char *intern_config_file; - char *config_dir; - char *system_config_dir; - char *state_file; - char *no_auto_default_file; - char *plugins; - NMConfigConfigureAndQuitType configure_and_quit; - - gboolean is_debug; - 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; - - /* @first_start is not provided by command line. It is a convenient hack - * to pass in an argument to NMConfig. This makes NMConfigCmdLineOptions a - * misnomer. - * - * It is true, if NM is started the first time -- contrary to a restart - * during the same boot up. That is determined by the content of the - * /run/NetworManager state directory. */ - bool first_start; + char * config_main_file; + char * intern_config_file; + char * config_dir; + char * system_config_dir; + char * state_file; + char * no_auto_default_file; + char * plugins; + NMConfigConfigureAndQuitType configure_and_quit; + + gboolean is_debug; + 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; + + /* @first_start is not provided by command line. It is a convenient hack + * to pass in an argument to NMConfig. This makes NMConfigCmdLineOptions a + * misnomer. + * + * It is true, if NM is started the first time -- contrary to a restart + * during the same boot up. That is determined by the content of the + * /run/NetworManager state directory. */ + bool first_start; }; typedef struct { - NMConfigState p; + NMConfigState p; } State; /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_CMD_LINE_OPTIONS, - PROP_ATOMIC_SECTION_PREFIXES, -); +NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_CMD_LINE_OPTIONS, PROP_ATOMIC_SECTION_PREFIXES, ); enum { - SIGNAL_CONFIG_CHANGED, - LAST_SIGNAL, + SIGNAL_CONFIG_CHANGED, + LAST_SIGNAL, }; -static guint signals[LAST_SIGNAL] = { 0 }; +static guint signals[LAST_SIGNAL] = {0}; typedef struct { - NMConfigCmdLineOptions cli; - - NMConfigData *config_data; - NMConfigData *config_data_orig; - - char *config_dir; - char *system_config_dir; - char *no_auto_default_file; - char *intern_config_file; - - char *log_level; - char *log_domains; - - NMConfigConfigureAndQuitType configure_and_quit; - - char **atomic_section_prefixes; - - /* The state. This is actually a mutable data member and it makes sense: - * The regular config is immutable (NMConfigData) and can old be swapped - * as a whole (via nm_config_set_values() or during reload). Thus, it can - * be changed, but it is still immutable and is swapped atomically as a - * whole. Also, we emit a config-changed signal on that occasion. - * - * For state, there are no events. You can query it and set it. - * It only gets read *once* at startup, and later is cached and only - * written out to disk. Hence, no need for the immutable dance here - * because the state changes only on explicit actions from the daemon - * itself. */ - State *state; - - /* the hash table of device states. It is only loaded from disk - * once and kept immutable afterwards. - * - * We also read all state file at once. We don't want to support - * that they are changed outside of NM (at least not while NM is running). - * Hence, we read them once, that's it. */ - GHashTable *device_states; - - char **warnings; + NMConfigCmdLineOptions cli; + + NMConfigData *config_data; + NMConfigData *config_data_orig; + + char *config_dir; + char *system_config_dir; + char *no_auto_default_file; + char *intern_config_file; + + char *log_level; + char *log_domains; + + NMConfigConfigureAndQuitType configure_and_quit; + + char **atomic_section_prefixes; + + /* The state. This is actually a mutable data member and it makes sense: + * The regular config is immutable (NMConfigData) and can old be swapped + * as a whole (via nm_config_set_values() or during reload). Thus, it can + * be changed, but it is still immutable and is swapped atomically as a + * whole. Also, we emit a config-changed signal on that occasion. + * + * For state, there are no events. You can query it and set it. + * It only gets read *once* at startup, and later is cached and only + * written out to disk. Hence, no need for the immutable dance here + * because the state changes only on explicit actions from the daemon + * itself. */ + State *state; + + /* the hash table of device states. It is only loaded from disk + * once and kept immutable afterwards. + * + * We also read all state file at once. We don't want to support + * that they are changed outside of NM (at least not while NM is running). + * Hence, we read them once, that's it. */ + GHashTable *device_states; + + char **warnings; } NMConfigPrivate; struct _NMConfig { - GObject parent; - NMConfigPrivate _priv; + GObject parent; + NMConfigPrivate _priv; }; struct _NMConfigClass { - GObjectClass parent; + GObjectClass parent; }; -static void nm_config_initable_iface_init (GInitableIface *iface); +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_WITH_CODE(NMConfig, + nm_config, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, nm_config_initable_iface_init);) -#define NM_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMConfig, NM_IS_CONFIG) +#define NM_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMConfig, NM_IS_CONFIG) /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_CORE -#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "config", __VA_ARGS__) +#define _NMLOG(level, ...) __NMLOG_DEFAULT(level, _NMLOG_DOMAIN, "config", __VA_ARGS__) /*****************************************************************************/ -static void _set_config_data (NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags reload_flags); +static void +_set_config_data(NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags reload_flags); /*****************************************************************************/ int -nm_config_parse_boolean (const char *str, - int default_value) +nm_config_parse_boolean(const char *str, int default_value) { - return _nm_utils_ascii_str_to_bool (str, default_value); + return _nm_utils_ascii_str_to_bool(str, default_value); } int -nm_config_keyfile_get_boolean (const GKeyFile *keyfile, - const char *section, - const char *key, - int default_value) +nm_config_keyfile_get_boolean(const GKeyFile *keyfile, + const char * section, + const char * key, + int default_value) { - gs_free char *str = NULL; + 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); + 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 ((GKeyFile *) keyfile, section, key, NULL); - return nm_config_parse_boolean (str, default_value); + str = g_key_file_get_value((GKeyFile *) keyfile, section, key, NULL); + return nm_config_parse_boolean(str, default_value); } gint64 -nm_config_keyfile_get_int64 (const GKeyFile *keyfile, - const char *section, - const char *key, - guint base, - gint64 min, - gint64 max, - gint64 fallback) -{ - gint64 v; - int errsv; - char *str; - - g_return_val_if_fail (keyfile, fallback); - g_return_val_if_fail (section, fallback); - g_return_val_if_fail (key, fallback); - - str = g_key_file_get_value ((GKeyFile *) keyfile, section, key, NULL); - v = _nm_utils_ascii_str_to_int64 (str, base, min, max, fallback); - if (str) { - errsv = errno; - g_free (str); - errno = errsv; - } - return v; +nm_config_keyfile_get_int64(const GKeyFile *keyfile, + const char * section, + const char * key, + guint base, + gint64 min, + gint64 max, + gint64 fallback) +{ + gint64 v; + int errsv; + char * str; + + g_return_val_if_fail(keyfile, fallback); + g_return_val_if_fail(section, fallback); + g_return_val_if_fail(key, fallback); + + str = g_key_file_get_value((GKeyFile *) keyfile, section, key, NULL); + v = _nm_utils_ascii_str_to_int64(str, base, min, max, fallback); + if (str) { + errsv = errno; + g_free(str); + errno = errsv; + } + return v; } char * -nm_config_keyfile_get_value (const GKeyFile *keyfile, - const char *section, - const char *key, - NMConfigGetValueFlags flags) +nm_config_keyfile_get_value(const GKeyFile * keyfile, + const char * section, + const char * key, + NMConfigGetValueFlags flags) { - char *value; + char *value; - if (NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_RAW)) - value = g_key_file_get_value ((GKeyFile *) keyfile, section, key, NULL); - else - value = g_key_file_get_string ((GKeyFile *) keyfile, section, key, NULL); + if (NM_FLAGS_HAS(flags, NM_CONFIG_GET_VALUE_RAW)) + value = g_key_file_get_value((GKeyFile *) keyfile, section, key, NULL); + else + value = g_key_file_get_string((GKeyFile *) keyfile, section, key, NULL); - if (!value) - return 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_STRIP)) + g_strstrip(value); - if ( NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_NO_EMPTY) - && !*value) { - g_free (value); - return NULL; - } + if (NM_FLAGS_HAS(flags, NM_CONFIG_GET_VALUE_NO_EMPTY) && !*value) { + g_free(value); + return NULL; + } - return value; + return value; } void -nm_config_keyfile_set_string_list (GKeyFile *keyfile, - const char *group, - const char *key, - const char *const* strv, - gssize len) +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; + gsize l; + char *new_value; - if (len < 0) - len = strv ? g_strv_length ((char **) strv) : 0; + 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(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. */ + /* 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; + 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); + 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 *const* -nm_config_get_warnings (NMConfig *config) +const char *const * +nm_config_get_warnings(NMConfig *config) { - return (const char *const *) NM_CONFIG_GET_PRIVATE (config)->warnings; + return (const char *const *) NM_CONFIG_GET_PRIVATE(config)->warnings; } void -nm_config_clear_warnings (NMConfig *config) +nm_config_clear_warnings(NMConfig *config) { - nm_clear_pointer (&NM_CONFIG_GET_PRIVATE (config)->warnings, g_strfreev); + nm_clear_pointer(&NM_CONFIG_GET_PRIVATE(config)->warnings, g_strfreev); } NMConfigData * -nm_config_get_data (NMConfig *config) +nm_config_get_data(NMConfig *config) { - g_return_val_if_fail (config != NULL, NULL); + g_return_val_if_fail(config != NULL, NULL); - return NM_CONFIG_GET_PRIVATE (config)->config_data; + return NM_CONFIG_GET_PRIVATE(config)->config_data; } /* 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) +nm_config_get_data_orig(NMConfig *config) { - g_return_val_if_fail (config != NULL, NULL); + g_return_val_if_fail(config != NULL, NULL); - return NM_CONFIG_GET_PRIVATE (config)->config_data_orig; + return NM_CONFIG_GET_PRIVATE(config)->config_data_orig; } const char * -nm_config_get_log_level (NMConfig *config) +nm_config_get_log_level(NMConfig *config) { - g_return_val_if_fail (config != NULL, NULL); + g_return_val_if_fail(config != NULL, NULL); - return NM_CONFIG_GET_PRIVATE (config)->log_level; + return NM_CONFIG_GET_PRIVATE(config)->log_level; } const char * -nm_config_get_log_domains (NMConfig *config) +nm_config_get_log_domains(NMConfig *config) { - g_return_val_if_fail (config != NULL, NULL); + g_return_val_if_fail(config != NULL, NULL); - return NM_CONFIG_GET_PRIVATE (config)->log_domains; + return NM_CONFIG_GET_PRIVATE(config)->log_domains; } NMConfigConfigureAndQuitType -nm_config_get_configure_and_quit (NMConfig *config) +nm_config_get_configure_and_quit(NMConfig *config) { - return NM_CONFIG_GET_PRIVATE (config)->configure_and_quit; + return NM_CONFIG_GET_PRIVATE(config)->configure_and_quit; } gboolean -nm_config_get_is_debug (NMConfig *config) +nm_config_get_is_debug(NMConfig *config) { - return NM_CONFIG_GET_PRIVATE (config)->cli.is_debug; + return NM_CONFIG_GET_PRIVATE(config)->cli.is_debug; } gboolean -nm_config_get_first_start (NMConfig *config) +nm_config_get_first_start(NMConfig *config) { - return NM_CONFIG_GET_PRIVATE (config)->cli.first_start; + return NM_CONFIG_GET_PRIVATE(config)->cli.first_start; } const char * -nm_config_get_no_auto_default_file (NMConfig *config) +nm_config_get_no_auto_default_file(NMConfig *config) { - return NM_CONFIG_GET_PRIVATE (config)->no_auto_default_file; + return NM_CONFIG_GET_PRIVATE(config)->no_auto_default_file; } /*****************************************************************************/ static char ** -no_auto_default_from_file (const char *no_auto_default_file) +no_auto_default_from_file(const char *no_auto_default_file) { - gs_free char *data = NULL; - const char **list = NULL; - gsize i; + gs_free char *data = NULL; + const char ** list = NULL; + gsize i; - if ( no_auto_default_file - && g_file_get_contents (no_auto_default_file, &data, NULL, NULL)) - list = nm_utils_strsplit_set (data, "\n"); + if (no_auto_default_file && g_file_get_contents(no_auto_default_file, &data, NULL, NULL)) + list = nm_utils_strsplit_set(data, "\n"); - if (list) { - for (i = 0; list[i]; i++) - list[i] = nm_utils_str_utf8safe_unescape_cp (list[i], NM_UTILS_STR_UTF8_SAFE_FLAG_NONE); - } + if (list) { + for (i = 0; list[i]; i++) + list[i] = nm_utils_str_utf8safe_unescape_cp(list[i], NM_UTILS_STR_UTF8_SAFE_FLAG_NONE); + } - /* The returned buffer here is not at all compact. That means, it has additional - * memory allocations and is larger than needed. That means, you should not keep - * this result around, only process it further and free it. */ - return (char **) list; + /* The returned buffer here is not at all compact. That means, it has additional + * memory allocations and is larger than needed. That means, you should not keep + * this result around, only process it further and free it. */ + return (char **) list; } static gboolean -no_auto_default_to_file (const char *no_auto_default_file, const char *const*no_auto_default, GError **error) +no_auto_default_to_file(const char * no_auto_default_file, + const char *const *no_auto_default, + GError ** error) { - nm_auto_free_gstring GString *data = NULL; - gsize i; + nm_auto_free_gstring GString *data = NULL; + gsize i; - data = g_string_new (""); - for (i = 0; no_auto_default && no_auto_default[i]; i++) { - gs_free char *s_to_free = NULL; - const char *s = no_auto_default[i]; + data = g_string_new(""); + for (i = 0; no_auto_default && no_auto_default[i]; i++) { + gs_free char *s_to_free = NULL; + const char * s = no_auto_default[i]; - s = nm_utils_str_utf8safe_escape (s, - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL - | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII, - &s_to_free); - g_string_append (data, s); - g_string_append_c (data, '\n'); - } - return g_file_set_contents (no_auto_default_file, data->str, data->len, error); + s = nm_utils_str_utf8safe_escape(s, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL + | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII, + &s_to_free); + g_string_append(data, s); + g_string_append_c(data, '\n'); + } + return g_file_set_contents(no_auto_default_file, data->str, data->len, error); } gboolean -nm_config_get_no_auto_default_for_device (NMConfig *self, NMDevice *device) +nm_config_get_no_auto_default_for_device(NMConfig *self, NMDevice *device) { - NMConfigPrivate *priv; + NMConfigPrivate *priv; - g_return_val_if_fail (NM_IS_CONFIG (self), FALSE); + g_return_val_if_fail(NM_IS_CONFIG(self), FALSE); - priv = NM_CONFIG_GET_PRIVATE (self); + priv = NM_CONFIG_GET_PRIVATE(self); - if (priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) - return TRUE; + if (priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) + return TRUE; - return nm_config_data_get_no_auto_default_for_device (priv->config_data, device); + return nm_config_data_get_no_auto_default_for_device(priv->config_data, device); } void -nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device) -{ - NMConfigPrivate *priv; - GError *error = NULL; - NMConfigData *new_data = NULL; - gs_free char *spec_to_free = NULL; - const char *ifname; - const char *hw_address; - const char *spec; - const char *const*no_auto_default_current; - gs_free const char **no_auto_default_new = NULL; - gboolean is_fake; - gsize len; - gssize idx; - - g_return_if_fail (NM_IS_CONFIG (self)); - g_return_if_fail (NM_IS_DEVICE (device)); - - priv = NM_CONFIG_GET_PRIVATE (self); - - hw_address = nm_device_get_permanent_hw_address_full (device, TRUE, &is_fake); - - if (!hw_address) { - /* No MAC address, not even a fake one. We don't do anything for this device. */ - return; - } - - if (is_fake) { - /* A fake MAC address, no point in storing it to the file. - * Also, nm_match_spec_device() would ignore fake MAC addresses. - * - * Instead, try the interface-name... */ - ifname = nm_device_get_ip_iface (device); - if (!nm_utils_ifname_valid_kernel (ifname, NULL)) - return; - - spec_to_free = g_strdup_printf (NM_MATCH_SPEC_INTERFACE_NAME_TAG"=%s", ifname); - spec = spec_to_free; - } else - spec = hw_address; - - no_auto_default_current = nm_config_data_get_no_auto_default (priv->config_data); - - len = NM_PTRARRAY_LEN (no_auto_default_current); - - idx = nm_utils_ptrarray_find_binary_search ((gconstpointer *) no_auto_default_current, - len, - spec, - nm_strcmp_with_data, - NULL, - NULL, - NULL); - if (idx >= 0) { - /* @spec 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; - } - - idx = ~idx; - - no_auto_default_new = g_new (const char *, len + 2); - if (idx > 0) - memcpy (no_auto_default_new, no_auto_default_current, sizeof (const char *) * idx); - no_auto_default_new[idx] = spec; - if (idx < len) - memcpy (&no_auto_default_new[idx + 1], &no_auto_default_current[idx], sizeof (const char *) * (len - idx)); - no_auto_default_new[len + 1] = NULL; - - if (!no_auto_default_to_file (priv->no_auto_default_file, no_auto_default_new, &error)) { - _LOGW ("Could not update no-auto-default.state file: %s", - error->message); - g_error_free (error); - } - - new_data = nm_config_data_new_update_no_auto_default (priv->config_data, no_auto_default_new); - - _set_config_data (self, new_data, NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT); +nm_config_set_no_auto_default_for_device(NMConfig *self, NMDevice *device) +{ + NMConfigPrivate * priv; + GError * error = NULL; + NMConfigData * new_data = NULL; + gs_free char * spec_to_free = NULL; + const char * ifname; + const char * hw_address; + const char * spec; + const char *const * no_auto_default_current; + gs_free const char **no_auto_default_new = NULL; + gboolean is_fake; + gsize len; + gssize idx; + + g_return_if_fail(NM_IS_CONFIG(self)); + g_return_if_fail(NM_IS_DEVICE(device)); + + priv = NM_CONFIG_GET_PRIVATE(self); + + hw_address = nm_device_get_permanent_hw_address_full(device, TRUE, &is_fake); + + if (!hw_address) { + /* No MAC address, not even a fake one. We don't do anything for this device. */ + return; + } + + if (is_fake) { + /* A fake MAC address, no point in storing it to the file. + * Also, nm_match_spec_device() would ignore fake MAC addresses. + * + * Instead, try the interface-name... */ + ifname = nm_device_get_ip_iface(device); + if (!nm_utils_ifname_valid_kernel(ifname, NULL)) + return; + + spec_to_free = g_strdup_printf(NM_MATCH_SPEC_INTERFACE_NAME_TAG "=%s", ifname); + spec = spec_to_free; + } else + spec = hw_address; + + no_auto_default_current = nm_config_data_get_no_auto_default(priv->config_data); + + len = NM_PTRARRAY_LEN(no_auto_default_current); + + idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) no_auto_default_current, + len, + spec, + nm_strcmp_with_data, + NULL, + NULL, + NULL); + if (idx >= 0) { + /* @spec 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; + } + + idx = ~idx; + + no_auto_default_new = g_new(const char *, len + 2); + if (idx > 0) + memcpy(no_auto_default_new, no_auto_default_current, sizeof(const char *) * idx); + no_auto_default_new[idx] = spec; + if (idx < len) + memcpy(&no_auto_default_new[idx + 1], + &no_auto_default_current[idx], + sizeof(const char *) * (len - idx)); + no_auto_default_new[len + 1] = NULL; + + if (!no_auto_default_to_file(priv->no_auto_default_file, no_auto_default_new, &error)) { + _LOGW("Could not update no-auto-default.state file: %s", error->message); + g_error_free(error); + } + + new_data = nm_config_data_new_update_no_auto_default(priv->config_data, no_auto_default_new); + + _set_config_data(self, new_data, NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT); } /*****************************************************************************/ static void -_nm_config_cmd_line_options_clear (NMConfigCmdLineOptions *cli) -{ - nm_clear_g_free (&cli->config_main_file); - nm_clear_g_free (&cli->config_dir); - nm_clear_g_free (&cli->system_config_dir); - nm_clear_g_free (&cli->no_auto_default_file); - nm_clear_g_free (&cli->intern_config_file); - nm_clear_g_free (&cli->state_file); - nm_clear_g_free (&cli->plugins); - cli->configure_and_quit = NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED; - cli->is_debug = FALSE; - nm_clear_g_free (&cli->connectivity_uri); - nm_clear_g_free (&cli->connectivity_response); - cli->connectivity_interval = -1; - cli->first_start = FALSE; +_nm_config_cmd_line_options_clear(NMConfigCmdLineOptions *cli) +{ + nm_clear_g_free(&cli->config_main_file); + nm_clear_g_free(&cli->config_dir); + nm_clear_g_free(&cli->system_config_dir); + nm_clear_g_free(&cli->no_auto_default_file); + nm_clear_g_free(&cli->intern_config_file); + nm_clear_g_free(&cli->state_file); + nm_clear_g_free(&cli->plugins); + cli->configure_and_quit = NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED; + cli->is_debug = FALSE; + nm_clear_g_free(&cli->connectivity_uri); + nm_clear_g_free(&cli->connectivity_response); + cli->connectivity_interval = -1; + cli->first_start = FALSE; } 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->system_config_dir = g_strdup (cli->system_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->intern_config_file = g_strdup (cli->intern_config_file); - dst->state_file = g_strdup (cli->state_file); - dst->plugins = g_strdup (cli->plugins); - dst->configure_and_quit = cli->configure_and_quit; - dst->is_debug = cli->is_debug; - dst->connectivity_uri = g_strdup (cli->connectivity_uri); - dst->connectivity_response = g_strdup (cli->connectivity_response); - dst->connectivity_interval = cli->connectivity_interval; - dst->first_start = cli->first_start; +_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->system_config_dir = g_strdup(cli->system_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->intern_config_file = g_strdup(cli->intern_config_file); + dst->state_file = g_strdup(cli->state_file); + dst->plugins = g_strdup(cli->plugins); + dst->configure_and_quit = cli->configure_and_quit; + dst->is_debug = cli->is_debug; + dst->connectivity_uri = g_strdup(cli->connectivity_uri); + dst->connectivity_response = g_strdup(cli->connectivity_response); + dst->connectivity_interval = cli->connectivity_interval; + dst->first_start = cli->first_start; } NMConfigCmdLineOptions * -nm_config_cmd_line_options_new (gboolean first_start) +nm_config_cmd_line_options_new(gboolean first_start) { - NMConfigCmdLineOptions *cli = g_new0 (NMConfigCmdLineOptions, 1); + NMConfigCmdLineOptions *cli = g_new0(NMConfigCmdLineOptions, 1); - _nm_config_cmd_line_options_clear (cli); + _nm_config_cmd_line_options_clear(cli); - cli->first_start = first_start; + cli->first_start = first_start; - return cli; + return cli; } void -nm_config_cmd_line_options_free (NMConfigCmdLineOptions *cli) +nm_config_cmd_line_options_free(NMConfigCmdLineOptions *cli) { - g_return_if_fail (cli); + g_return_if_fail(cli); - _nm_config_cmd_line_options_clear (cli); - g_free (cli); + _nm_config_cmd_line_options_clear(cli); + g_free(cli); } static NMConfigConfigureAndQuitType -string_to_configure_and_quit (const char *value, GError **error) +string_to_configure_and_quit(const char *value, GError **error) { - NMConfigConfigureAndQuitType ret; + NMConfigConfigureAndQuitType ret; - if (value == NULL) - return NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED; + if (value == NULL) + return NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED; - if (nm_streq (value, "initrd")) - return NM_CONFIG_CONFIGURE_AND_QUIT_INITRD; + if (nm_streq(value, "initrd")) + return NM_CONFIG_CONFIGURE_AND_QUIT_INITRD; - ret = nm_config_parse_boolean (value, NM_CONFIG_CONFIGURE_AND_QUIT_INVALID); - if (ret == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) - g_set_error (error, 1, 0, N_("'%s' is not valid"), value); + ret = nm_config_parse_boolean(value, NM_CONFIG_CONFIGURE_AND_QUIT_INVALID); + if (ret == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) + g_set_error(error, 1, 0, N_("'%s' is not valid"), value); - return ret; + return ret; } static gboolean -parse_configure_and_quit (const char *option_name, const char *value, gpointer user_data, GError **error) +parse_configure_and_quit(const char *option_name, + const char *value, + gpointer user_data, + GError ** error) { - NMConfigCmdLineOptions *cli = user_data; + NMConfigCmdLineOptions *cli = user_data; - if (value == NULL) - cli->configure_and_quit = NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED; - else - cli->configure_and_quit = string_to_configure_and_quit (value, error); + if (value == NULL) + cli->configure_and_quit = NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED; + else + cli->configure_and_quit = string_to_configure_and_quit(value, error); - if (cli->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) { - g_prefix_error (error, N_("Bad '%s' option: "), option_name); - return FALSE; - } + if (cli->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) { + g_prefix_error(error, N_("Bad '%s' option: "), option_name); + return FALSE; + } - return TRUE; + return TRUE; } void -nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli, - GOptionContext *opt_ctx) -{ - GOptionGroup *group; - GOptionEntry config_options[] = { - { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), DEFAULT_CONFIG_MAIN_FILE }, - { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), DEFAULT_CONFIG_DIR }, - { "system-config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->system_config_dir, N_("System config directory location"), DEFAULT_SYSTEM_CONFIG_DIR }, - { "intern-config", 0, 0, G_OPTION_ARG_FILENAME, &cli->intern_config_file, N_("Internal config file location"), DEFAULT_INTERN_CONFIG_FILE }, - { "state-file", 0, 0, G_OPTION_ARG_FILENAME, &cli->state_file, N_("State file location"), DEFAULT_STATE_FILE }, - { "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"), DEFAULT_NO_AUTO_DEFAULT_FILE }, - { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), NM_CONFIG_DEFAULT_MAIN_PLUGINS }, - { "configure-and-quit", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_configure_and_quit, N_("Quit after initial configuration"), NULL }, - { "debug", 'd', 0, G_OPTION_ARG_NONE, &cli->is_debug, N_("Don't become a daemon, and log to stderr"), 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"), NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE }, - { 0 }, - }; - - g_return_if_fail (opt_ctx); - g_return_if_fail (cli); - - group = g_option_group_new ("nm", N_("NetworkManager options" ), N_("Show NetworkManager options"), cli, NULL); - - g_option_group_add_entries (group, config_options); - g_option_context_add_group (opt_ctx, group); +nm_config_cmd_line_options_add_to_entries(NMConfigCmdLineOptions *cli, GOptionContext *opt_ctx) +{ + GOptionGroup *group; + GOptionEntry config_options[] = { + {"config", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->config_main_file, + N_("Config file location"), + DEFAULT_CONFIG_MAIN_FILE}, + {"config-dir", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->config_dir, + N_("Config directory location"), + DEFAULT_CONFIG_DIR}, + {"system-config-dir", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->system_config_dir, + N_("System config directory location"), + DEFAULT_SYSTEM_CONFIG_DIR}, + {"intern-config", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->intern_config_file, + N_("Internal config file location"), + DEFAULT_INTERN_CONFIG_FILE}, + {"state-file", + 0, + 0, + G_OPTION_ARG_FILENAME, + &cli->state_file, + N_("State file location"), + DEFAULT_STATE_FILE}, + {"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"), + DEFAULT_NO_AUTO_DEFAULT_FILE}, + {"plugins", + 0, + 0, + G_OPTION_ARG_STRING, + &cli->plugins, + N_("List of plugins separated by ','"), + NM_CONFIG_DEFAULT_MAIN_PLUGINS}, + {"configure-and-quit", + 0, + G_OPTION_FLAG_OPTIONAL_ARG, + G_OPTION_ARG_CALLBACK, + parse_configure_and_quit, + N_("Quit after initial configuration"), + NULL}, + {"debug", + 'd', + 0, + G_OPTION_ARG_NONE, + &cli->is_debug, + N_("Don't become a daemon, and log to stderr"), + 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"), + NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE}, + {0}, + }; + + g_return_if_fail(opt_ctx); + g_return_if_fail(cli); + + group = g_option_group_new("nm", + N_("NetworkManager options"), + N_("Show NetworkManager options"), + cli, + NULL); + + g_option_group_add_entries(group, config_options); + g_option_context_add_group(opt_ctx, group); } /*****************************************************************************/ GKeyFile * -nm_config_create_keyfile () +nm_config_create_keyfile() { - GKeyFile *keyfile; + GKeyFile *keyfile; - keyfile = g_key_file_new (); - g_key_file_set_list_separator (keyfile, NM_CONFIG_KEYFILE_LIST_SEPARATOR); - return keyfile; + keyfile = g_key_file_new(); + g_key_file_set_list_separator(keyfile, NM_CONFIG_KEYFILE_LIST_SEPARATOR); + return keyfile; } /* this is an external variable, to make loading testable. Other then that, * no code is supposed to change this. */ guint _nm_config_match_nm_version = NM_VERSION; -char *_nm_config_match_env = NULL; +char *_nm_config_match_env = NULL; static gboolean -ignore_config_snippet (GKeyFile *keyfile, gboolean is_base_config) +ignore_config_snippet(GKeyFile *keyfile, gboolean is_base_config) { - GSList *specs; - gboolean as_bool; - NMMatchSpecMatchType match_type; + GSList * specs; + gboolean as_bool; + NMMatchSpecMatchType match_type; - if (is_base_config) - return FALSE; + if (is_base_config) + return FALSE; - if (!g_key_file_has_key (keyfile, NM_CONFIG_KEYFILE_GROUP_CONFIG, NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE, NULL)) - return FALSE; + if (!g_key_file_has_key(keyfile, + NM_CONFIG_KEYFILE_GROUP_CONFIG, + NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE, + NULL)) + return FALSE; - /* first, let's try to parse the value as plain boolean. If that is possible, we don't treat - * the value as match-spec. */ - as_bool = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_CONFIG, NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE, -1); - if (as_bool != -1) - return !as_bool; + /* first, let's try to parse the value as plain boolean. If that is possible, we don't treat + * the value as match-spec. */ + as_bool = nm_config_keyfile_get_boolean(keyfile, + NM_CONFIG_KEYFILE_GROUP_CONFIG, + NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE, + -1); + if (as_bool != -1) + return !as_bool; - if (G_UNLIKELY (!_nm_config_match_env)) { - const char *e; + if (G_UNLIKELY(!_nm_config_match_env)) { + const char *e; - e = g_getenv ("NM_CONFIG_ENABLE_TAG"); - _nm_config_match_env = g_strdup (e ?: ""); - } + e = g_getenv("NM_CONFIG_ENABLE_TAG"); + _nm_config_match_env = g_strdup(e ?: ""); + } - /* second, interpret the value as match-spec. */ - specs = nm_config_get_match_spec (keyfile, NM_CONFIG_KEYFILE_GROUP_CONFIG, NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE, NULL); - match_type = nm_match_spec_config (specs, - _nm_config_match_nm_version, - _nm_config_match_env); - g_slist_free_full (specs, g_free); + /* second, interpret the value as match-spec. */ + specs = nm_config_get_match_spec(keyfile, + NM_CONFIG_KEYFILE_GROUP_CONFIG, + NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE, + NULL); + match_type = nm_match_spec_config(specs, _nm_config_match_nm_version, _nm_config_match_env); + g_slist_free_full(specs, g_free); - return match_type != NM_MATCH_SPEC_MATCH; + return match_type != NM_MATCH_SPEC_MATCH; } static int -_sort_groups_cmp (const char **pa, const char **pb, gpointer dummy) -{ - const char *a = *pa; - const char *b = *pb; - gboolean a_is_connection, b_is_connection; - gboolean a_is_device, b_is_device; - - a_is_connection = NM_STR_HAS_PREFIX (a, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); - b_is_connection = NM_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*] entries - * after. */ - if (a_is_connection) - return 1; - return -1; - } - if (a_is_connection) { - /* both are [connection.\+] entries. Reverse their order. - * One of the sections might be literally [connection]. That section - * is special and its order will be fixed later. It doesn't actually - * matter here how it compares with [connection.\+] sections. */ - return pa > pb ? -1 : 1; - } - - a_is_device = NM_STR_HAS_PREFIX (a, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); - b_is_device = NM_STR_HAS_PREFIX (b, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); - - if (a_is_device != b_is_device) { - /* one is a [device*] entry, the other not. We sort [device*] entries - * after. */ - if (a_is_device) - return 1; - return -1; - } - if (a_is_device) { - /* both are [device.\+] entries. Reverse their order. - * One of the sections might be literally [device]. That section - * is special and its order will be fixed later. It doesn't actually - * matter here how it compares with [device.\+] sections. */ - return pa > pb ? -1 : 1; - } - - /* don't reorder the rest. */ - return 0; +_sort_groups_cmp(const char **pa, const char **pb, gpointer dummy) +{ + const char *a = *pa; + const char *b = *pb; + gboolean a_is_connection, b_is_connection; + gboolean a_is_device, b_is_device; + + a_is_connection = NM_STR_HAS_PREFIX(a, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); + b_is_connection = NM_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*] entries + * after. */ + if (a_is_connection) + return 1; + return -1; + } + if (a_is_connection) { + /* both are [connection.\+] entries. Reverse their order. + * One of the sections might be literally [connection]. That section + * is special and its order will be fixed later. It doesn't actually + * matter here how it compares with [connection.\+] sections. */ + return pa > pb ? -1 : 1; + } + + a_is_device = NM_STR_HAS_PREFIX(a, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); + b_is_device = NM_STR_HAS_PREFIX(b, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); + + if (a_is_device != b_is_device) { + /* one is a [device*] entry, the other not. We sort [device*] entries + * after. */ + if (a_is_device) + return 1; + return -1; + } + if (a_is_device) { + /* both are [device.\+] entries. Reverse their order. + * One of the sections might be literally [device]. That section + * is special and its order will be fixed later. It doesn't actually + * matter here how it compares with [device.\+] sections. */ + return pa > pb ? -1 : 1; + } + + /* don't reorder the rest. */ + return 0; } void -_nm_config_sort_groups (char **groups, gsize ngroups) +_nm_config_sort_groups(char **groups, gsize ngroups) { - if (ngroups > 1) { - g_qsort_with_data (groups, - ngroups, - sizeof (char *), - (GCompareDataFunc) _sort_groups_cmp, - NULL); - } + if (ngroups > 1) { + g_qsort_with_data(groups, + ngroups, + sizeof(char *), + (GCompareDataFunc) _sort_groups_cmp, + NULL); + } } static gboolean -_setting_is_device_spec (const char *group, const char *key) +_setting_is_device_spec(const char *group, const char *key) { -#define _IS(group_v, key_v) (nm_streq (group, ""group_v"") && nm_streq (key, ""key_v"")) - return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT) - || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER) - || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY) - || _IS (NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES) - || (NM_STR_HAS_PREFIX (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && nm_streq (key, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE)) - || (NM_STR_HAS_PREFIX (group, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE ) && nm_streq (key, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE)); +#define _IS(group_v, key_v) (nm_streq(group, "" group_v "") && nm_streq(key, "" key_v "")) + return _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT) + || _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER) + || _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY) + || _IS(NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES) + || (NM_STR_HAS_PREFIX(group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) + && nm_streq(key, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE)) + || (NM_STR_HAS_PREFIX(group, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE) + && nm_streq(key, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE)); } static gboolean -_setting_is_string_list (const char *group, const char *key) +_setting_is_string_list(const char *group, const char *key) { - return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS) - || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG) - || _IS (NM_CONFIG_KEYFILE_GROUP_LOGGING, NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS) - || NM_STR_HAS_PREFIX (group, NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST); + return _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS) + || _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG) + || _IS(NM_CONFIG_KEYFILE_GROUP_LOGGING, NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS) + || NM_STR_HAS_PREFIX(group, NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST); #undef _IS } typedef struct { - char *group; - const char *const *keys; - bool is_prefix:1; - bool is_connection:1; + char * group; + const char *const *keys; + bool is_prefix : 1; + bool is_connection : 1; } ConfigGroup; /* The following comment is used by check-config-options.sh, don't remove it. */ /* START OPTION LIST */ static const ConfigGroup config_groups[] = { - { - .group = NM_CONFIG_KEYFILE_GROUP_MAIN, - .keys = NM_MAKE_STRV ( - NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY, - NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT, - NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT, - NM_CONFIG_KEYFILE_KEY_MAIN_CONFIGURE_AND_QUIT, - NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG, - NM_CONFIG_KEYFILE_KEY_MAIN_DHCP, - NM_CONFIG_KEYFILE_KEY_MAIN_DNS, - NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE, - NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER, - NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES, - NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT, - NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS, - NM_CONFIG_KEYFILE_KEY_MAIN_RC_MANAGER, - NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, - NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED, - ), - }, - { - .group = NM_CONFIG_KEYFILE_GROUP_LOGGING, - .keys = NM_MAKE_STRV ( - NM_CONFIG_KEYFILE_KEY_LOGGING_AUDIT, - NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND, - NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS, - NM_CONFIG_KEYFILE_KEY_LOGGING_LEVEL, - ), - }, - { - .group = NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, - .keys = NM_MAKE_STRV ( - NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_ENABLED, - NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_INTERVAL, - NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_RESPONSE, - NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_URI, - ), - }, - { - .group = NM_CONFIG_KEYFILE_GROUP_KEYFILE, - .keys = NM_MAKE_STRV ( - NM_CONFIG_KEYFILE_KEY_KEYFILE_HOSTNAME, - NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, - NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, - ), - }, - { - .group = NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, - .keys = NM_MAKE_STRV ( - NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED, - ), - }, - { - .group = NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE, - .is_prefix = TRUE, - .keys = NM_MAKE_STRV ( - NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT, - NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER, - NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, - NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS, - NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND, - NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS, - NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, - NM_CONFIG_KEYFILE_KEY_STOP_MATCH, - ), - }, - { - .group = NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS, - .keys = NM_MAKE_STRV ( - NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS, - NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES, - ), - }, - { - .group = NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN, - .is_prefix = TRUE, - .keys = NM_MAKE_STRV ( - NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS, - NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS, - ), - }, - { - .group = NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION, - .is_prefix = TRUE, - .is_connection = TRUE, - .keys = NM_MAKE_STRV ( - NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, - NM_CONFIG_KEYFILE_KEY_STOP_MATCH, - ), - }, - { } /* sentinel */ + { + .group = NM_CONFIG_KEYFILE_GROUP_MAIN, + .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY, + NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT, + NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT, + NM_CONFIG_KEYFILE_KEY_MAIN_CONFIGURE_AND_QUIT, + NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG, + NM_CONFIG_KEYFILE_KEY_MAIN_DHCP, + NM_CONFIG_KEYFILE_KEY_MAIN_DNS, + NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE, + NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER, + NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES, + NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT, + NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS, + NM_CONFIG_KEYFILE_KEY_MAIN_RC_MANAGER, + NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, + NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED, ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_LOGGING, + .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_LOGGING_AUDIT, + NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND, + NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS, + NM_CONFIG_KEYFILE_KEY_LOGGING_LEVEL, ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_ENABLED, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_INTERVAL, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_RESPONSE, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_URI, ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_KEYFILE, + .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_KEYFILE_HOSTNAME, + NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, + NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, + .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED, ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE, + .is_prefix = TRUE, + .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT, + NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER, + NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, + NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS, + NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND, + NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS, + NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, + NM_CONFIG_KEYFILE_KEY_STOP_MATCH, ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS, + .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES, ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN, + .is_prefix = TRUE, + .keys = NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS, ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION, + .is_prefix = TRUE, + .is_connection = TRUE, + .keys = + NM_MAKE_STRV(NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, NM_CONFIG_KEYFILE_KEY_STOP_MATCH, ), + }, + {} /* sentinel */ }; /* The following comment is used by check-config-options.sh, don't remove it. */ /* END OPTION LIST */ static gboolean -check_config_key (const char *group, const char *key) +check_config_key(const char *group, const char *key) { - const ConfigGroup *g; - const char *const *k; - const char **ptr; + const ConfigGroup *g; + const char *const *k; + const char ** ptr; #if NM_MORE_ASSERTS > 10 - { - static gboolean checked = FALSE; - const char **ptr1, **ptr2; - - /* check for duplicate elements in the static list */ - - if (!checked) { - for (ptr1 = __start_connection_defaults; ptr1 < __stop_connection_defaults; ptr1++) { - for (ptr2 = ptr1 + 1; ptr2 < __stop_connection_defaults; ptr2++) - nm_assert (!nm_streq (*ptr1, *ptr2)); - } - checked = TRUE; - } - } + { + static gboolean checked = FALSE; + const char ** ptr1, **ptr2; + + /* check for duplicate elements in the static list */ + + if (!checked) { + for (ptr1 = __start_connection_defaults; ptr1 < __stop_connection_defaults; ptr1++) { + for (ptr2 = ptr1 + 1; ptr2 < __stop_connection_defaults; ptr2++) + nm_assert(!nm_streq(*ptr1, *ptr2)); + } + checked = TRUE; + } + } #endif - for (g = config_groups; g->group; g++) { - if ( (!g->is_prefix && nm_streq (group, g->group)) - || (g->is_prefix && g_str_has_prefix (group, g->group))) - break; - } + for (g = config_groups; g->group; g++) { + if ((!g->is_prefix && nm_streq(group, g->group)) + || (g->is_prefix && g_str_has_prefix(group, g->group))) + break; + } - if (!g->group) - return FALSE; + if (!g->group) + return FALSE; - for (k = g->keys; *k; k++) { - if (nm_streq (key, *k)) - return TRUE; - } + for (k = g->keys; *k; k++) { + if (nm_streq(key, *k)) + return TRUE; + } - if (g->is_connection) { - for (ptr = __start_connection_defaults; ptr < __stop_connection_defaults; ptr++) { - if (nm_streq (key, *ptr)) - return TRUE; - } - return FALSE; - } + if (g->is_connection) { + for (ptr = __start_connection_defaults; ptr < __stop_connection_defaults; ptr++) { + if (nm_streq(key, *ptr)) + return TRUE; + } + return FALSE; + } - return FALSE; + return FALSE; } static gboolean -read_config (GKeyFile *keyfile, - gboolean is_base_config, - const char *dirname, - const char *path, - GPtrArray *warnings, - GError **error) -{ - gs_unref_keyfile GKeyFile *kf = NULL; - gs_strfreev char **groups = NULL; - gs_free char *path_free = NULL; - gsize ngroups; - gsize nkeys; - int g; - int k; - - g_return_val_if_fail (keyfile, FALSE); - g_return_val_if_fail (path, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - - if (dirname) { - path_free = g_build_filename (dirname, path, NULL); - path = path_free; - } - - 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; - } - - _LOGD ("Reading config file '%s'", path); - - kf = nm_config_create_keyfile (); - if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, error)) { - g_prefix_error (error, "%s: ", path); - return FALSE; - } - - if (ignore_config_snippet (kf, is_base_config)) - return TRUE; - - /* the config-group is internal to every configuration snippets. It doesn't make sense - * to merge it into the global configuration, and it doesn't make sense to preserve the - * group beyond this point. */ - g_key_file_remove_group (kf, NM_CONFIG_KEYFILE_GROUP_CONFIG, NULL); - - /* Override the current settings with the new ones */ - groups = g_key_file_get_groups (kf, &ngroups); - 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(). */ - _nm_config_sort_groups (groups, ngroups); - - for (g = 0; groups && groups[g]; g++) { - const char *group = groups[g]; - gs_strfreev char **keys = NULL; - - if (NM_STR_HAS_PREFIX (group, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN)) { - /* internal groups cannot be set by user configuration. */ - continue; - } - keys = g_key_file_get_keys (kf, group, &nkeys, NULL); - if (!keys) - continue; - for (k = 0; keys[k]; k++) { - gs_free char *new_value = NULL; - const char *key; - char last_char; - gsize key_len; - - key = keys[k]; - nm_assert (key && *key); - - if ( NM_STR_HAS_PREFIX_WITH_MORE (key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS) - || NM_STR_HAS_PREFIX_WITH_MORE (key, NM_CONFIG_KEYFILE_KEYPREFIX_SET)) { - /* these keys are protected. We ignore them if the user sets them. */ - continue; - } - - if (nm_streq (key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS)) { - /* the "was" key is protected and it cannot be set by user configuration. */ - continue; - } - - 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; - gboolean old_val_was_set = FALSE; - - 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) { - gs_free_error GError *old_error = NULL; - - old_val = g_key_file_get_string_list (keyfile, group, base_key, NULL, &old_error); - new_val = g_key_file_get_string_list (kf, group, key, NULL, NULL); - if ( nm_streq (group, NM_CONFIG_KEYFILE_GROUP_MAIN) - && nm_streq (base_key, "plugins")) { - old_val_was_set = !nm_keyfile_error_is_not_found (old_error); - if ( !old_val - && !old_val_was_set) { - /* we must fill the unspecified value with the compile-time default. */ - g_key_file_set_value (keyfile, group, base_key, NM_CONFIG_DEFAULT_MAIN_PLUGINS); - old_val = g_key_file_get_string_list (keyfile, group, base_key, NULL, NULL); - old_val_was_set = TRUE; - } - } - } 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 omitting 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 && !old_val_was_set) - 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; - } - - new_value = g_key_file_get_value (kf, group, key, NULL); - g_key_file_set_value (keyfile, group, key, new_value); - - if (!check_config_key (group, key)) { - g_ptr_array_add (warnings, - g_strdup_printf ("unknown key '%s' in section [%s] of file '%s'", - key, group, path)); - } - } - } - - return TRUE; +read_config(GKeyFile * keyfile, + gboolean is_base_config, + const char *dirname, + const char *path, + GPtrArray * warnings, + GError ** error) +{ + nm_auto_unref_keyfile GKeyFile *kf = NULL; + gs_strfreev char ** groups = NULL; + gs_free char * path_free = NULL; + gsize ngroups; + gsize nkeys; + int g; + int k; + + g_return_val_if_fail(keyfile, FALSE); + g_return_val_if_fail(path, FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + if (dirname) { + path_free = g_build_filename(dirname, path, NULL); + path = path_free; + } + + 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; + } + + _LOGD("Reading config file '%s'", path); + + kf = nm_config_create_keyfile(); + if (!g_key_file_load_from_file(kf, path, G_KEY_FILE_NONE, error)) { + g_prefix_error(error, "%s: ", path); + return FALSE; + } + + if (ignore_config_snippet(kf, is_base_config)) + return TRUE; + + /* the config-group is internal to every configuration snippets. It doesn't make sense + * to merge it into the global configuration, and it doesn't make sense to preserve the + * group beyond this point. */ + g_key_file_remove_group(kf, NM_CONFIG_KEYFILE_GROUP_CONFIG, NULL); + + /* Override the current settings with the new ones */ + groups = g_key_file_get_groups(kf, &ngroups); + 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(). */ + _nm_config_sort_groups(groups, ngroups); + + for (g = 0; groups && groups[g]; g++) { + const char * group = groups[g]; + gs_strfreev char **keys = NULL; + + if (NM_STR_HAS_PREFIX(group, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN)) { + /* internal groups cannot be set by user configuration. */ + continue; + } + keys = g_key_file_get_keys(kf, group, &nkeys, NULL); + if (!keys) + continue; + for (k = 0; keys[k]; k++) { + gs_free char *new_value = NULL; + const char * key; + char last_char; + gsize key_len; + + key = keys[k]; + nm_assert(key && *key); + + if (NM_STR_HAS_PREFIX_WITH_MORE(key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS) + || NM_STR_HAS_PREFIX_WITH_MORE(key, NM_CONFIG_KEYFILE_KEYPREFIX_SET)) { + /* these keys are protected. We ignore them if the user sets them. */ + continue; + } + + if (nm_streq(key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS)) { + /* the "was" key is protected and it cannot be set by user configuration. */ + continue; + } + + 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; + gboolean old_val_was_set = FALSE; + + 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) { + gs_free_error GError *old_error = NULL; + + old_val = + g_key_file_get_string_list(keyfile, group, base_key, NULL, &old_error); + new_val = g_key_file_get_string_list(kf, group, key, NULL, NULL); + if (nm_streq(group, NM_CONFIG_KEYFILE_GROUP_MAIN) + && nm_streq(base_key, "plugins")) { + old_val_was_set = !nm_keyfile_error_is_not_found(old_error); + if (!old_val && !old_val_was_set) { + /* we must fill the unspecified value with the compile-time default. */ + g_key_file_set_value(keyfile, + group, + base_key, + NM_CONFIG_DEFAULT_MAIN_PLUGINS); + old_val = g_key_file_get_string_list(keyfile, + group, + base_key, + NULL, + NULL); + old_val_was_set = TRUE; + } + } + } 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); + nm_auto_free_slist GSList *old_specs = nm_match_spec_split(old_sval); + nm_auto_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 omitting 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 { + nm_auto_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 && !old_val_was_set) + 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; + } + + new_value = g_key_file_get_value(kf, group, key, NULL); + g_key_file_set_value(keyfile, group, key, new_value); + + if (!check_config_key(group, key)) { + g_ptr_array_add(warnings, + g_strdup_printf("unknown key '%s' in section [%s] of file '%s'", + key, + group, + path)); + } + } + } + + return TRUE; } static gboolean -read_base_config (GKeyFile *keyfile, - const char *cli_config_main_file, - char **out_config_main_file, - GPtrArray *warnings, - GError **error) -{ - 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_main_file) { - /* Bad user-specific config file path is a hard error */ - if (read_config (keyfile, TRUE, NULL, cli_config_main_file, warnings, error)) { - *out_config_main_file = g_strdup (cli_config_main_file); - return TRUE; - } else - return FALSE; - } - - /* Even though we prefer NetworkManager.conf, we need to check the - * old nm-system-settings.conf first to preserve compat with older - * setups. In package managed systems dropping a NetworkManager.conf - * onto the system would make NM use it instead of nm-system-settings.conf, - * changing behavior during an upgrade. We don't want that. - */ - - /* Try deprecated nm-system-settings.conf first */ - if (read_config (keyfile, TRUE, NULL, DEFAULT_CONFIG_MAIN_FILE_OLD, warnings, &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)) { - _LOGW ("Old default config file invalid: %s", - my_error->message); - } - g_clear_error (&my_error); - - /* Try the standard config file location next */ - if (read_config (keyfile, TRUE, NULL, DEFAULT_CONFIG_MAIN_FILE, warnings, &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)) { - _LOGW ("Default config file invalid: %s", - my_error->message); - g_propagate_error (error, my_error); - return FALSE; - } - g_clear_error (&my_error); - - /* If for some reason no config file exists, use the default - * config file path. - */ - *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE); - _LOGI ("No config file found or given; using %s", - DEFAULT_CONFIG_MAIN_FILE); - return TRUE; +read_base_config(GKeyFile * keyfile, + const char *cli_config_main_file, + char ** out_config_main_file, + GPtrArray * warnings, + GError ** error) +{ + 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_main_file) { + /* Bad user-specific config file path is a hard error */ + if (read_config(keyfile, TRUE, NULL, cli_config_main_file, warnings, error)) { + *out_config_main_file = g_strdup(cli_config_main_file); + return TRUE; + } else + return FALSE; + } + + /* Even though we prefer NetworkManager.conf, we need to check the + * old nm-system-settings.conf first to preserve compat with older + * setups. In package managed systems dropping a NetworkManager.conf + * onto the system would make NM use it instead of nm-system-settings.conf, + * changing behavior during an upgrade. We don't want that. + */ + + /* Try deprecated nm-system-settings.conf first */ + if (read_config(keyfile, TRUE, NULL, DEFAULT_CONFIG_MAIN_FILE_OLD, warnings, &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)) { + _LOGW("Old default config file invalid: %s", my_error->message); + } + g_clear_error(&my_error); + + /* Try the standard config file location next */ + if (read_config(keyfile, TRUE, NULL, DEFAULT_CONFIG_MAIN_FILE, warnings, &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)) { + _LOGW("Default config file invalid: %s", my_error->message); + g_propagate_error(error, my_error); + return FALSE; + } + g_clear_error(&my_error); + + /* If for some reason no config file exists, use the default + * config file path. + */ + *out_config_main_file = g_strdup(DEFAULT_CONFIG_MAIN_FILE); + _LOGI("No config file found or given; using %s", DEFAULT_CONFIG_MAIN_FILE); + return TRUE; } static GPtrArray * -_get_config_dir_files (const char *config_dir) +_get_config_dir_files(const char *config_dir) { - GFile *dir; - GFileEnumerator *direnum; - GFileInfo *info; - GPtrArray *confs; - const char *name; + GFile * dir; + GFileEnumerator *direnum; + GFileInfo * info; + GPtrArray * confs; + const char * name; - g_return_val_if_fail (config_dir, NULL); + g_return_val_if_fail(config_dir, NULL); - confs = g_ptr_array_new_with_free_func (g_free); - if (!*config_dir) - return confs; + confs = g_ptr_array_new_with_free_func(g_free); + if (!*config_dir) + return confs; - 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 (NM_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); + 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 (NM_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, nm_strcmp_p); - return confs; + g_ptr_array_sort(confs, nm_strcmp_p); + return confs; } static void -_confs_to_description (GString *str, const GPtrArray *confs, const char *name) +_confs_to_description(GString *str, const GPtrArray *confs, const char *name) { - guint i; + guint i; - if (!confs->len) - return; + if (!confs->len) + return; - for (i = 0; i < confs->len; i++) { - if (i == 0) - g_string_append_printf (str, " (%s: ", name); - else - g_string_append (str, ", "); - g_string_append (str, confs->pdata[i]); - } - g_string_append (str, ")"); + for (i = 0; i < confs->len; i++) { + if (i == 0) + g_string_append_printf(str, " (%s: ", name); + else + g_string_append(str, ", "); + g_string_append(str, confs->pdata[i]); + } + g_string_append(str, ")"); } static GKeyFile * -read_entire_config (const NMConfigCmdLineOptions *cli, - const char *config_dir, - const char *system_config_dir, - char **out_config_main_file, - char **out_config_description, - GPtrArray *warnings, - GError **error) -{ - gs_unref_keyfile GKeyFile *keyfile = NULL; - gs_unref_ptrarray GPtrArray *system_confs = NULL; - gs_unref_ptrarray GPtrArray *confs = NULL; - gs_unref_ptrarray GPtrArray *run_confs = NULL; - guint i; - gs_free char *o_config_main_file = NULL; - const char *run_config_dir = ""; - - nm_assert (config_dir); - nm_assert (system_config_dir); - nm_assert (!out_config_main_file || !*out_config_main_file); - nm_assert (!out_config_description || !*out_config_description); - nm_assert (!error || !*error); - nm_assert (warnings); - - if ( (""RUN_CONFIG_DIR)[0] == '/' - && !nm_streq (RUN_CONFIG_DIR, system_config_dir) - && !nm_streq (RUN_CONFIG_DIR, config_dir)) - run_config_dir = RUN_CONFIG_DIR; - - /* create a default configuration file. */ - keyfile = nm_config_create_keyfile (); - - system_confs = _get_config_dir_files (system_config_dir); - confs = _get_config_dir_files (config_dir); - run_confs = _get_config_dir_files (run_config_dir); - - for (i = 0; i < system_confs->len; ) { - const char *filename = system_confs->pdata[i]; - - /* if a same named file exists in config_dir or run_config_dir, skip it. */ - if (nm_utils_strv_find_first ((char **) confs->pdata, confs->len, filename) >= 0 || - nm_utils_strv_find_first ((char **) run_confs->pdata, run_confs->len, filename) >= 0) { - g_ptr_array_remove_index (system_confs, i); - continue; - } - - if (!read_config (keyfile, FALSE, system_config_dir, filename, warnings, error)) - return NULL; - i++; - } - - for (i = 0; i < run_confs->len; ) { - const char *filename = run_confs->pdata[i]; - - /* if a same named file exists in config_dir, skip it. */ - if (nm_utils_strv_find_first ((char **) confs->pdata, confs->len, filename) >= 0) { - g_ptr_array_remove_index (run_confs, i); - continue; - } - - if (!read_config (keyfile, FALSE, run_config_dir, filename, warnings, error)) - return NULL; - i++; - } - - /* First read the base config file */ - if (!read_base_config (keyfile, cli ? cli->config_main_file : NULL, &o_config_main_file, warnings, error)) - return NULL; - - g_assert (o_config_main_file); - - for (i = 0; i < confs->len; i++) { - if (!read_config (keyfile, FALSE, config_dir, confs->pdata[i], warnings, error)) - return NULL; - } - - /* Merge settings from command line. They overwrite everything read from - * config files. */ - - if (cli) { - if (cli->plugins) { - /* plugins is a string list. Set the value directly, so the user has to do proper escaping - * on the command line. */ - g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", cli->plugins); - } - - switch (cli->configure_and_quit) { - case NM_CONFIG_CONFIGURE_AND_QUIT_INVALID: - g_assert_not_reached (); - break; - case NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED: - /* do nothing */ - break; - case NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED: - g_key_file_set_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", TRUE); - break; - case NM_CONFIG_CONFIGURE_AND_QUIT_INITRD: - g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", "initrd"); - break; - } - - if (cli->connectivity_uri && cli->connectivity_uri[0]) - g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", cli->connectivity_uri); - if (cli->connectivity_interval >= 0) - g_key_file_set_integer (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", cli->connectivity_interval); - if (cli->connectivity_response && cli->connectivity_response[0]) - g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", cli->connectivity_response); - } - - if (out_config_description) { - GString *str; - - str = g_string_new (o_config_main_file); - _confs_to_description (str, system_confs, "lib"); - _confs_to_description (str, run_confs, "run"); - _confs_to_description (str, confs, "etc"); - *out_config_description = g_string_free (str, FALSE); - } - NM_SET_OUT (out_config_main_file, g_steal_pointer (&o_config_main_file)); - - return g_steal_pointer (&keyfile); +read_entire_config(const NMConfigCmdLineOptions *cli, + const char * config_dir, + const char * system_config_dir, + char ** out_config_main_file, + char ** out_config_description, + GPtrArray * warnings, + GError ** error) +{ + nm_auto_unref_keyfile GKeyFile *keyfile = NULL; + gs_unref_ptrarray GPtrArray *system_confs = NULL; + gs_unref_ptrarray GPtrArray *confs = NULL; + gs_unref_ptrarray GPtrArray *run_confs = NULL; + guint i; + gs_free char * o_config_main_file = NULL; + const char * run_config_dir = ""; + + nm_assert(config_dir); + nm_assert(system_config_dir); + nm_assert(!out_config_main_file || !*out_config_main_file); + nm_assert(!out_config_description || !*out_config_description); + nm_assert(!error || !*error); + nm_assert(warnings); + + if (("" RUN_CONFIG_DIR)[0] == '/' && !nm_streq(RUN_CONFIG_DIR, system_config_dir) + && !nm_streq(RUN_CONFIG_DIR, config_dir)) + run_config_dir = RUN_CONFIG_DIR; + + /* create a default configuration file. */ + keyfile = nm_config_create_keyfile(); + + system_confs = _get_config_dir_files(system_config_dir); + confs = _get_config_dir_files(config_dir); + run_confs = _get_config_dir_files(run_config_dir); + + for (i = 0; i < system_confs->len;) { + const char *filename = system_confs->pdata[i]; + + /* if a same named file exists in config_dir or run_config_dir, skip it. */ + if (nm_utils_strv_find_first((char **) confs->pdata, confs->len, filename) >= 0 + || nm_utils_strv_find_first((char **) run_confs->pdata, run_confs->len, filename) + >= 0) { + g_ptr_array_remove_index(system_confs, i); + continue; + } + + if (!read_config(keyfile, FALSE, system_config_dir, filename, warnings, error)) + return NULL; + i++; + } + + for (i = 0; i < run_confs->len;) { + const char *filename = run_confs->pdata[i]; + + /* if a same named file exists in config_dir, skip it. */ + if (nm_utils_strv_find_first((char **) confs->pdata, confs->len, filename) >= 0) { + g_ptr_array_remove_index(run_confs, i); + continue; + } + + if (!read_config(keyfile, FALSE, run_config_dir, filename, warnings, error)) + return NULL; + i++; + } + + /* First read the base config file */ + if (!read_base_config(keyfile, + cli ? cli->config_main_file : NULL, + &o_config_main_file, + warnings, + error)) + return NULL; + + g_assert(o_config_main_file); + + for (i = 0; i < confs->len; i++) { + if (!read_config(keyfile, FALSE, config_dir, confs->pdata[i], warnings, error)) + return NULL; + } + + /* Merge settings from command line. They overwrite everything read from + * config files. */ + + if (cli) { + if (cli->plugins) { + /* plugins is a string list. Set the value directly, so the user has to do proper escaping + * on the command line. */ + g_key_file_set_value(keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", cli->plugins); + } + + switch (cli->configure_and_quit) { + case NM_CONFIG_CONFIGURE_AND_QUIT_INVALID: + g_assert_not_reached(); + break; + case NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED: + /* do nothing */ + break; + case NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED: + g_key_file_set_boolean(keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + "configure-and-quit", + TRUE); + break; + case NM_CONFIG_CONFIGURE_AND_QUIT_INITRD: + g_key_file_set_string(keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + "configure-and-quit", + "initrd"); + break; + } + + if (cli->connectivity_uri && cli->connectivity_uri[0]) + g_key_file_set_string(keyfile, + NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + "uri", + cli->connectivity_uri); + if (cli->connectivity_interval >= 0) + g_key_file_set_integer(keyfile, + NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + "interval", + cli->connectivity_interval); + if (cli->connectivity_response && cli->connectivity_response[0]) + g_key_file_set_string(keyfile, + NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + "response", + cli->connectivity_response); + } + + if (out_config_description) { + GString *str; + + str = g_string_new(o_config_main_file); + _confs_to_description(str, system_confs, "lib"); + _confs_to_description(str, run_confs, "run"); + _confs_to_description(str, confs, "etc"); + *out_config_description = g_string_free(str, FALSE); + } + NM_SET_OUT(out_config_main_file, g_steal_pointer(&o_config_main_file)); + + return g_steal_pointer(&keyfile); } static gboolean -_is_atomic_section (const char *const*atomic_section_prefixes, const char *group) +_is_atomic_section(const char *const *atomic_section_prefixes, const char *group) { - if (atomic_section_prefixes) { - for (; *atomic_section_prefixes; atomic_section_prefixes++) { - if ( **atomic_section_prefixes - && g_str_has_prefix (group, *atomic_section_prefixes)) - return TRUE; - } - } - return FALSE; + if (atomic_section_prefixes) { + for (; *atomic_section_prefixes; atomic_section_prefixes++) { + if (**atomic_section_prefixes && g_str_has_prefix(group, *atomic_section_prefixes)) + return TRUE; + } + } + return FALSE; } static void -_string_append_val (GString *str, const char *value) -{ - if (!value) - return; - g_string_append_c (str, '+'); - while (TRUE) { - switch (*value) { - case '\0': - return; - case '\\': - case '+': - case '#': - case ':': - g_string_append_c (str, '+'); - /* fall-through */ - default: - g_string_append_c (str, *value); - } - value++; - } +_string_append_val(GString *str, const char *value) +{ + if (!value) + return; + g_string_append_c(str, '+'); + while (TRUE) { + switch (*value) { + case '\0': + return; + case '\\': + case '+': + case '#': + case ':': + g_string_append_c(str, '+'); + /* fall-through */ + default: + g_string_append_c(str, *value); + } + value++; + } } static char * -_keyfile_serialize_section (GKeyFile *keyfile, const char *group) +_keyfile_serialize_section(GKeyFile *keyfile, const char *group) { - gs_strfreev char **keys = NULL; - GString *str; - guint k; + gs_strfreev char **keys = NULL; + GString * str; + guint k; - if (keyfile) - keys = g_key_file_get_keys (keyfile, group, NULL, NULL); - if (!keys) - return g_strdup ("0#"); + if (keyfile) + keys = g_key_file_get_keys(keyfile, group, NULL, NULL); + if (!keys) + return g_strdup("0#"); - /* prepend a version. */ - str = g_string_new ("1#"); + /* prepend a version. */ + str = g_string_new("1#"); - for (k = 0; keys[k]; k++) { - const char *key = keys[k]; - gs_free char *value = NULL; + for (k = 0; keys[k]; k++) { + const char * key = keys[k]; + gs_free char *value = NULL; - _string_append_val (str, key); - g_string_append_c (str, ':'); + _string_append_val(str, key); + g_string_append_c(str, ':'); - value = g_key_file_get_value (keyfile, group, key, NULL); - _string_append_val (str, value); - g_string_append_c (str, '#'); - } - return g_string_free (str, FALSE); + value = g_key_file_get_value(keyfile, group, key, NULL); + _string_append_val(str, value); + g_string_append_c(str, '#'); + } + return g_string_free(str, FALSE); } gboolean -nm_config_keyfile_has_global_dns_config (GKeyFile *keyfile, gboolean internal) +nm_config_keyfile_has_global_dns_config(GKeyFile *keyfile, gboolean internal) { - gs_strfreev char **groups = NULL; - guint g; - const char *prefix; + gs_strfreev char **groups = NULL; + guint g; + const char * prefix; - if (!keyfile) - return FALSE; - if (g_key_file_has_group (keyfile, - internal - ? NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS - : NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS)) - return TRUE; + if (!keyfile) + return FALSE; + if (g_key_file_has_group(keyfile, + internal ? NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS + : NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS)) + return TRUE; - groups = g_key_file_get_groups (keyfile, NULL); - if (!groups) - return FALSE; + groups = g_key_file_get_groups(keyfile, NULL); + if (!groups) + return FALSE; - prefix = internal ? NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN : NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN; + prefix = internal ? NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN + : NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN; - for (g = 0; groups[g]; g++) { - if (g_str_has_prefix (groups[g], prefix)) - return TRUE; - } - return FALSE; + for (g = 0; groups[g]; g++) { + if (g_str_has_prefix(groups[g], prefix)) + return TRUE; + } + return FALSE; } /** @@ -1435,468 +1537,514 @@ nm_config_keyfile_has_global_dns_config (GKeyFile *keyfile, gboolean internal) * Returns: a #GKeyFile instance with the internal configuration. */ static GKeyFile * -intern_config_read (const char *filename, - GKeyFile *keyfile_conf, - const char *const*atomic_section_prefixes, - gboolean *out_needs_rewrite) -{ - GKeyFile *keyfile_intern; - GKeyFile *keyfile; - gboolean needs_rewrite = FALSE; - gs_strfreev char **groups = NULL; - guint g, k; - gboolean has_intern = FALSE; - - g_return_val_if_fail (filename, NULL); - - if (!*filename) { - if (out_needs_rewrite) - *out_needs_rewrite = FALSE; - return NULL; - } - - keyfile_intern = nm_config_create_keyfile (); - - keyfile = nm_config_create_keyfile (); - if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, NULL)) { - needs_rewrite = TRUE; - goto out; - } - - groups = g_key_file_get_groups (keyfile, NULL); - for (g = 0; groups && groups[g]; g++) { - gs_strfreev char **keys = NULL; - const char *group = groups[g]; - gboolean is_intern, is_atomic; - - if (nm_streq (group, NM_CONFIG_KEYFILE_GROUP_CONFIG)) - continue; - - keys = g_key_file_get_keys (keyfile, group, NULL, NULL); - if (!keys) - continue; - - is_intern = NM_STR_HAS_PREFIX (group, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); - is_atomic = !is_intern && _is_atomic_section (atomic_section_prefixes, group); - - if (is_atomic) { - gs_free char *conf_section_was = NULL; - gs_free char *conf_section_is = NULL; - - conf_section_is = _keyfile_serialize_section (keyfile_conf, group); - conf_section_was = g_key_file_get_string (keyfile, group, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, NULL); - - if (!nm_streq0 (conf_section_was, conf_section_is)) { - /* the section no longer matches. Skip it entirely. */ - needs_rewrite = TRUE; - continue; - } - /* we must set the "was" marker in our keyfile, so that we know that the section - * from user config is overwritten. The value doesn't matter, it's just a marker - * that this section is present. */ - g_key_file_set_value (keyfile_intern, group, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, ""); - } - - for (k = 0; keys[k]; k++) { - gs_free char *value_set = NULL; - const char *key = keys[k]; - - value_set = g_key_file_get_value (keyfile, group, key, NULL); - - if (is_intern) { - has_intern = TRUE; - g_key_file_set_value (keyfile_intern, group, key, value_set); - } else if (is_atomic) { - if (nm_streq (key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS)) - continue; - g_key_file_set_value (keyfile_intern, group, key, value_set); - } else if (NM_STR_HAS_PREFIX_WITH_MORE (key, NM_CONFIG_KEYFILE_KEYPREFIX_SET)) { - const char *key_base = &key[NM_STRLEN (NM_CONFIG_KEYFILE_KEYPREFIX_SET)]; - gs_free char *value_was = NULL; - gs_free char *value_conf = NULL; - gs_free char *key_was = g_strdup_printf (NM_CONFIG_KEYFILE_KEYPREFIX_WAS"%s", key_base); - - if (keyfile_conf) - value_conf = g_key_file_get_value (keyfile_conf, group, key_base, NULL); - value_was = g_key_file_get_value (keyfile, group, key_was, NULL); - - if (!nm_streq0 (value_conf, value_was)) { - /* if value_was is no longer the same as @value_conf, it means the user - * changed the configuration since the last write. In this case, we - * drop the value. It also means our file is out-of-date, and we should - * rewrite it. */ - needs_rewrite = TRUE; - continue; - } - has_intern = TRUE; - g_key_file_set_value (keyfile_intern, group, key_base, value_set); - } else if (NM_STR_HAS_PREFIX_WITH_MORE (key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) { - const char *key_base = &key[NM_STRLEN (NM_CONFIG_KEYFILE_KEYPREFIX_WAS)]; - gs_free char *key_set = g_strdup_printf (NM_CONFIG_KEYFILE_KEYPREFIX_SET"%s", key_base); - gs_free char *value_was = NULL; - gs_free char *value_conf = NULL; - - if (g_key_file_has_key (keyfile, group, key_set, NULL)) { - /* we have a matching "set" key too. Handle the "was" key there. */ - continue; - } - - if (keyfile_conf) - value_conf = g_key_file_get_value (keyfile_conf, group, key_base, NULL); - value_was = g_key_file_get_value (keyfile, group, key, NULL); - - if (!nm_streq0 (value_conf, value_was)) { - /* if value_was is no longer the same as @value_conf, it means the user - * changed the configuration since the last write. In this case, we - * don't overwrite the user-provided value. It also means our file is - * out-of-date, and we should rewrite it. */ - needs_rewrite = TRUE; - continue; - } - has_intern = TRUE; - /* signal the absence of the value. That means, we must propagate the - * "was" key to NMConfigData, so that it knows to hide the corresponding - * user key. */ - g_key_file_set_value (keyfile_intern, group, key, ""); - } else - needs_rewrite = TRUE; - } - } +intern_config_read(const char * filename, + GKeyFile * keyfile_conf, + const char *const *atomic_section_prefixes, + gboolean * out_needs_rewrite) +{ + GKeyFile * keyfile_intern; + GKeyFile * keyfile; + gboolean needs_rewrite = FALSE; + gs_strfreev char **groups = NULL; + guint g, k; + gboolean has_intern = FALSE; + + g_return_val_if_fail(filename, NULL); + + if (!*filename) { + if (out_needs_rewrite) + *out_needs_rewrite = FALSE; + return NULL; + } + + keyfile_intern = nm_config_create_keyfile(); + + keyfile = nm_config_create_keyfile(); + if (!g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, NULL)) { + needs_rewrite = TRUE; + goto out; + } + + groups = g_key_file_get_groups(keyfile, NULL); + for (g = 0; groups && groups[g]; g++) { + gs_strfreev char **keys = NULL; + const char * group = groups[g]; + gboolean is_intern, is_atomic; + + if (nm_streq(group, NM_CONFIG_KEYFILE_GROUP_CONFIG)) + continue; + + keys = g_key_file_get_keys(keyfile, group, NULL, NULL); + if (!keys) + continue; + + is_intern = NM_STR_HAS_PREFIX(group, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); + is_atomic = !is_intern && _is_atomic_section(atomic_section_prefixes, group); + + if (is_atomic) { + gs_free char *conf_section_was = NULL; + gs_free char *conf_section_is = NULL; + + conf_section_is = _keyfile_serialize_section(keyfile_conf, group); + conf_section_was = g_key_file_get_string(keyfile, + group, + NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, + NULL); + + if (!nm_streq0(conf_section_was, conf_section_is)) { + /* the section no longer matches. Skip it entirely. */ + needs_rewrite = TRUE; + continue; + } + /* we must set the "was" marker in our keyfile, so that we know that the section + * from user config is overwritten. The value doesn't matter, it's just a marker + * that this section is present. */ + g_key_file_set_value(keyfile_intern, + group, + NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, + ""); + } + + for (k = 0; keys[k]; k++) { + gs_free char *value_set = NULL; + const char * key = keys[k]; + + value_set = g_key_file_get_value(keyfile, group, key, NULL); + + if (is_intern) { + has_intern = TRUE; + g_key_file_set_value(keyfile_intern, group, key, value_set); + } else if (is_atomic) { + if (nm_streq(key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS)) + continue; + g_key_file_set_value(keyfile_intern, group, key, value_set); + } else if (NM_STR_HAS_PREFIX_WITH_MORE(key, NM_CONFIG_KEYFILE_KEYPREFIX_SET)) { + const char * key_base = &key[NM_STRLEN(NM_CONFIG_KEYFILE_KEYPREFIX_SET)]; + gs_free char *value_was = NULL; + gs_free char *value_conf = NULL; + gs_free char *key_was = + g_strdup_printf(NM_CONFIG_KEYFILE_KEYPREFIX_WAS "%s", key_base); + + if (keyfile_conf) + value_conf = g_key_file_get_value(keyfile_conf, group, key_base, NULL); + value_was = g_key_file_get_value(keyfile, group, key_was, NULL); + + if (!nm_streq0(value_conf, value_was)) { + /* if value_was is no longer the same as @value_conf, it means the user + * changed the configuration since the last write. In this case, we + * drop the value. It also means our file is out-of-date, and we should + * rewrite it. */ + needs_rewrite = TRUE; + continue; + } + has_intern = TRUE; + g_key_file_set_value(keyfile_intern, group, key_base, value_set); + } else if (NM_STR_HAS_PREFIX_WITH_MORE(key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) { + const char * key_base = &key[NM_STRLEN(NM_CONFIG_KEYFILE_KEYPREFIX_WAS)]; + gs_free char *key_set = + g_strdup_printf(NM_CONFIG_KEYFILE_KEYPREFIX_SET "%s", key_base); + gs_free char *value_was = NULL; + gs_free char *value_conf = NULL; + + if (g_key_file_has_key(keyfile, group, key_set, NULL)) { + /* we have a matching "set" key too. Handle the "was" key there. */ + continue; + } + + if (keyfile_conf) + value_conf = g_key_file_get_value(keyfile_conf, group, key_base, NULL); + value_was = g_key_file_get_value(keyfile, group, key, NULL); + + if (!nm_streq0(value_conf, value_was)) { + /* if value_was is no longer the same as @value_conf, it means the user + * changed the configuration since the last write. In this case, we + * don't overwrite the user-provided value. It also means our file is + * out-of-date, and we should rewrite it. */ + needs_rewrite = TRUE; + continue; + } + has_intern = TRUE; + /* signal the absence of the value. That means, we must propagate the + * "was" key to NMConfigData, so that it knows to hide the corresponding + * user key. */ + g_key_file_set_value(keyfile_intern, group, key, ""); + } else + needs_rewrite = TRUE; + } + } out: - /* - * If user configuration specifies global DNS options, the DNS - * options in internal configuration must be deleted. Otherwise a - * deletion of options from user configuration may cause the - * internal options to appear again. - */ - if (nm_config_keyfile_has_global_dns_config (keyfile_conf, FALSE)) { - if (g_key_file_remove_group (keyfile_intern, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, NULL)) - needs_rewrite = TRUE; - for (g = 0; groups && groups[g]; g++) { - if ( NM_STR_HAS_PREFIX (groups[g], NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN) - && groups[g][NM_STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN)]) { - g_key_file_remove_group (keyfile_intern, groups[g], NULL); - needs_rewrite = TRUE; - } - } - } - - g_key_file_unref (keyfile); - - if (out_needs_rewrite) - *out_needs_rewrite = needs_rewrite; - - _LOGD ("intern config file \"%s\"", filename); - - if (!has_intern) { - g_key_file_unref (keyfile_intern); - return NULL; - } - return keyfile_intern; + /* + * If user configuration specifies global DNS options, the DNS + * options in internal configuration must be deleted. Otherwise, a + * deletion of options from user configuration may cause the + * internal options to appear again. + */ + if (nm_config_keyfile_has_global_dns_config(keyfile_conf, FALSE)) { + if (g_key_file_remove_group(keyfile_intern, + NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, + NULL)) + needs_rewrite = TRUE; + for (g = 0; groups && groups[g]; g++) { + if (NM_STR_HAS_PREFIX(groups[g], NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN) + && groups[g][NM_STRLEN(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN)]) { + g_key_file_remove_group(keyfile_intern, groups[g], NULL); + needs_rewrite = TRUE; + } + } + } + + g_key_file_unref(keyfile); + + if (out_needs_rewrite) + *out_needs_rewrite = needs_rewrite; + + _LOGD("intern config file \"%s\"", filename); + + if (!has_intern) { + g_key_file_unref(keyfile_intern); + return NULL; + } + return keyfile_intern; } static int -_intern_config_write_sort_fcn (const char **a, const char **b, const char *const*atomic_section_prefixes) -{ - const char *g_a = *a; - const char *g_b = *b; - gboolean a_is, b_is; - - a_is = NM_STR_HAS_PREFIX (g_a, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); - b_is = NM_STR_HAS_PREFIX (g_b, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); - - if (a_is != b_is) { - if (a_is) - return 1; - return -1; - } - if (!a_is) { - a_is = _is_atomic_section (atomic_section_prefixes, g_a); - b_is = _is_atomic_section (atomic_section_prefixes, g_b); - - if (a_is != b_is) { - if (a_is) - return 1; - return -1; - } - } - return g_strcmp0 (g_a, g_b); +_intern_config_write_sort_fcn(const char ** a, + const char ** b, + const char *const *atomic_section_prefixes) +{ + const char *g_a = *a; + const char *g_b = *b; + gboolean a_is, b_is; + + a_is = NM_STR_HAS_PREFIX(g_a, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); + b_is = NM_STR_HAS_PREFIX(g_b, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); + + if (a_is != b_is) { + if (a_is) + return 1; + return -1; + } + if (!a_is) { + a_is = _is_atomic_section(atomic_section_prefixes, g_a); + b_is = _is_atomic_section(atomic_section_prefixes, g_b); + + if (a_is != b_is) { + if (a_is) + return 1; + return -1; + } + } + return g_strcmp0(g_a, g_b); } static gboolean -intern_config_write (const char *filename, - GKeyFile *keyfile_intern, - GKeyFile *keyfile_conf, - const char *const*atomic_section_prefixes, - GError **error) -{ - GKeyFile *keyfile; - gs_strfreev char **groups = NULL; - guint g, k; - gboolean success = FALSE; - GError *local = NULL; - - g_return_val_if_fail (filename, FALSE); - - if (!*filename) { - g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND, "no filename to write (use --intern-config?)"); - return FALSE; - } - - keyfile = nm_config_create_keyfile (); - - if (keyfile_intern) { - groups = g_key_file_get_groups (keyfile_intern, NULL); - if (groups && groups[0]) { - g_qsort_with_data (groups, - g_strv_length (groups), - sizeof (char *), - (GCompareDataFunc) _intern_config_write_sort_fcn, - (gpointer) atomic_section_prefixes); - } - } - for (g = 0; groups && groups[g]; g++) { - gs_strfreev char **keys = NULL; - const char *group = groups[g]; - gboolean is_intern, is_atomic; - - keys = g_key_file_get_keys (keyfile_intern, group, NULL, NULL); - if (!keys) - continue; - - is_intern = NM_STR_HAS_PREFIX (group, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); - is_atomic = !is_intern && _is_atomic_section (atomic_section_prefixes, group); - - if (is_atomic) { - if ( (!keys[0] || (!keys[1] && strcmp (keys[0], NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS) == 0)) - && !g_key_file_has_group (keyfile_conf, group)) { - /* we are about to save an atomic section. However, we don't have any additional - * keys on our own and there is no user-provided (overlapping) section either. - * We don't have to write an empty section (i.e. skip the useless ".was=0#"). */ - continue; - } else { - gs_free char *conf_section_is = NULL; - - conf_section_is = _keyfile_serialize_section (keyfile_conf, group); - g_key_file_set_string (keyfile, group, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, conf_section_is); - g_key_file_set_comment (keyfile, group, NULL, - " Overwrites entire section from 'NetworkManager.conf'", - NULL); - } - } - - for (k = 0; keys[k]; k++) { - const char *key = keys[k]; - gs_free char *value_set = NULL; - gs_free char *key_set = NULL; - - if ( !is_intern - && strcmp (key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS) == 0) { - g_warn_if_fail (is_atomic); - continue; - } - - value_set = g_key_file_get_value (keyfile_intern, group, key, NULL); - - if (is_intern || is_atomic) - g_key_file_set_value (keyfile, group, key, value_set); - else { - gs_free char *value_was = NULL; - - if (NM_STR_HAS_PREFIX_WITH_MORE (key, NM_CONFIG_KEYFILE_KEYPREFIX_SET)) { - /* Setting a key with .set prefix has no meaning, as these keys - * are protected. Just set the value you want to set instead. - * Why did this happen?? */ - g_warn_if_reached (); - } else if (NM_STR_HAS_PREFIX_WITH_MORE (key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) { - const char *key_base = &key[NM_STRLEN (NM_CONFIG_KEYFILE_KEYPREFIX_WAS)]; - - if ( NM_STR_HAS_PREFIX_WITH_MORE (key_base, NM_CONFIG_KEYFILE_KEYPREFIX_SET) - || NM_STR_HAS_PREFIX_WITH_MORE (key_base, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) { - g_warn_if_reached (); - continue; - } - - if (g_key_file_has_key (keyfile_intern, group, key_base, NULL)) { - /* There is also a matching key_base entry. Skip processing - * the .was. key ad handle the key_base in the other else branch. */ - continue; - } - - if (keyfile_conf) { - value_was = g_key_file_get_value (keyfile_conf, group, key_base, NULL); - if (value_was) - g_key_file_set_value (keyfile, group, key, value_was); - } - } else { - if (keyfile_conf) { - value_was = g_key_file_get_value (keyfile_conf, group, key, NULL); - if (nm_streq0 (value_set, value_was)) { - /* there is no point in storing the identical value as we have via - * user configuration. Skip it. */ - continue; - } - if (value_was) { - gs_free char *key_was = NULL; - - key_was = g_strdup_printf (NM_CONFIG_KEYFILE_KEYPREFIX_WAS"%s", key); - g_key_file_set_value (keyfile, group, key_was, value_was); - } - } - key = key_set = g_strdup_printf (NM_CONFIG_KEYFILE_KEYPREFIX_SET"%s", key); - g_key_file_set_value (keyfile, group, key, value_set); - } - } - } - if ( is_intern - && g_key_file_has_group (keyfile, group)) { - g_key_file_set_comment (keyfile, group, NULL, - " Internal section. Not overwritable via user configuration in 'NetworkManager.conf'", - NULL); - } - } - - g_key_file_set_comment (keyfile, NULL, NULL, - " Internal configuration file. This file is written and read\n" - " by NetworkManager and its configuration values are merged\n" - " with the configuration from 'NetworkManager.conf'.\n" - "\n" - " Keys with a \""NM_CONFIG_KEYFILE_KEYPREFIX_SET"\" prefix specify the value to set.\n" - " A corresponding key with a \""NM_CONFIG_KEYFILE_KEYPREFIX_WAS"\" prefix records the value\n" - " of the user configuration at the time of storing the file.\n" - " The value from internal configuration is rejected if the corresponding\n" - " \""NM_CONFIG_KEYFILE_KEYPREFIX_WAS"\" key no longer matches the configuration from 'NetworkManager.conf'.\n" - " That means, if you modify a value in 'NetworkManager.conf', the internal\n" - " overwrite no longer matches and is ignored.\n" - "\n" - " Certain sections can only be overwritten whole, not on a per key basis.\n" - " Such sections are marked with a \""NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS"\" key that records the user configuration\n" - " at the time of writing.\n" - "\n" - " Internal sections of the form [" NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "*] cannot\n" - " be set by user configuration.\n" - "\n" - " CHANGES TO THIS FILE WILL BE OVERWRITTEN", - NULL); - - success = g_key_file_save_to_file (keyfile, filename, &local); - - _LOGD ("write intern config file \"%s\"%s%s", filename, success ? "" : ": ", success ? "" : local->message); - g_key_file_unref (keyfile); - if (!success) - g_propagate_error (error, local); - return success; +intern_config_write(const char * filename, + GKeyFile * keyfile_intern, + GKeyFile * keyfile_conf, + const char *const *atomic_section_prefixes, + GError ** error) +{ + GKeyFile * keyfile; + gs_strfreev char **groups = NULL; + guint g, k; + gboolean success = FALSE; + GError * local = NULL; + + g_return_val_if_fail(filename, FALSE); + + if (!*filename) { + g_set_error(error, + G_KEY_FILE_ERROR, + G_KEY_FILE_ERROR_NOT_FOUND, + "no filename to write (use --intern-config?)"); + return FALSE; + } + + keyfile = nm_config_create_keyfile(); + + if (keyfile_intern) { + groups = g_key_file_get_groups(keyfile_intern, NULL); + if (groups && groups[0]) { + g_qsort_with_data(groups, + g_strv_length(groups), + sizeof(char *), + (GCompareDataFunc) _intern_config_write_sort_fcn, + (gpointer) atomic_section_prefixes); + } + } + for (g = 0; groups && groups[g]; g++) { + gs_strfreev char **keys = NULL; + const char * group = groups[g]; + gboolean is_intern, is_atomic; + + keys = g_key_file_get_keys(keyfile_intern, group, NULL, NULL); + if (!keys) + continue; + + is_intern = NM_STR_HAS_PREFIX(group, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); + is_atomic = !is_intern && _is_atomic_section(atomic_section_prefixes, group); + + if (is_atomic) { + if ((!keys[0] + || (!keys[1] && strcmp(keys[0], NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS) == 0)) + && !g_key_file_has_group(keyfile_conf, group)) { + /* we are about to save an atomic section. However, we don't have any additional + * keys on our own and there is no user-provided (overlapping) section either. + * We don't have to write an empty section (i.e. skip the useless ".was=0#"). */ + continue; + } else { + gs_free char *conf_section_is = NULL; + + conf_section_is = _keyfile_serialize_section(keyfile_conf, group); + g_key_file_set_string(keyfile, + group, + NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, + conf_section_is); + g_key_file_set_comment(keyfile, + group, + NULL, + " Overwrites entire section from 'NetworkManager.conf'", + NULL); + } + } + + for (k = 0; keys[k]; k++) { + const char * key = keys[k]; + gs_free char *value_set = NULL; + gs_free char *key_set = NULL; + + if (!is_intern && strcmp(key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS) == 0) { + g_warn_if_fail(is_atomic); + continue; + } + + value_set = g_key_file_get_value(keyfile_intern, group, key, NULL); + + if (is_intern || is_atomic) + g_key_file_set_value(keyfile, group, key, value_set); + else { + gs_free char *value_was = NULL; + + if (NM_STR_HAS_PREFIX_WITH_MORE(key, NM_CONFIG_KEYFILE_KEYPREFIX_SET)) { + /* Setting a key with .set prefix has no meaning, as these keys + * are protected. Just set the value you want to set instead. + * Why did this happen?? */ + g_warn_if_reached(); + } else if (NM_STR_HAS_PREFIX_WITH_MORE(key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) { + const char *key_base = &key[NM_STRLEN(NM_CONFIG_KEYFILE_KEYPREFIX_WAS)]; + + if (NM_STR_HAS_PREFIX_WITH_MORE(key_base, NM_CONFIG_KEYFILE_KEYPREFIX_SET) + || NM_STR_HAS_PREFIX_WITH_MORE(key_base, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) { + g_warn_if_reached(); + continue; + } + + if (g_key_file_has_key(keyfile_intern, group, key_base, NULL)) { + /* There is also a matching key_base entry. Skip processing + * the .was. key ad handle the key_base in the other else branch. */ + continue; + } + + if (keyfile_conf) { + value_was = g_key_file_get_value(keyfile_conf, group, key_base, NULL); + if (value_was) + g_key_file_set_value(keyfile, group, key, value_was); + } + } else { + if (keyfile_conf) { + value_was = g_key_file_get_value(keyfile_conf, group, key, NULL); + if (nm_streq0(value_set, value_was)) { + /* there is no point in storing the identical value as we have via + * user configuration. Skip it. */ + continue; + } + if (value_was) { + gs_free char *key_was = NULL; + + key_was = g_strdup_printf(NM_CONFIG_KEYFILE_KEYPREFIX_WAS "%s", key); + g_key_file_set_value(keyfile, group, key_was, value_was); + } + } + key = key_set = g_strdup_printf(NM_CONFIG_KEYFILE_KEYPREFIX_SET "%s", key); + g_key_file_set_value(keyfile, group, key, value_set); + } + } + } + if (is_intern && g_key_file_has_group(keyfile, group)) { + g_key_file_set_comment(keyfile, + group, + NULL, + " Internal section. Not overwritable via user configuration in " + "'NetworkManager.conf'", + NULL); + } + } + + g_key_file_set_comment( + keyfile, + NULL, + NULL, + " Internal configuration file. This file is written and read\n" + " by NetworkManager and its configuration values are merged\n" + " with the configuration from 'NetworkManager.conf'.\n" + "\n" + " Keys with a \"" NM_CONFIG_KEYFILE_KEYPREFIX_SET "\" prefix specify the value to set.\n" + " A corresponding key with a \"" NM_CONFIG_KEYFILE_KEYPREFIX_WAS + "\" prefix records the value\n" + " of the user configuration at the time of storing the file.\n" + " The value from internal configuration is rejected if the corresponding\n" + " \"" NM_CONFIG_KEYFILE_KEYPREFIX_WAS + "\" key no longer matches the configuration from 'NetworkManager.conf'.\n" + " That means, if you modify a value in 'NetworkManager.conf', the internal\n" + " overwrite no longer matches and is ignored.\n" + "\n" + " Certain sections can only be overwritten whole, not on a per key basis.\n" + " Such sections are marked with a \"" NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS + "\" key that records the user configuration\n" + " at the time of writing.\n" + "\n" + " Internal sections of the form [" NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "*] cannot\n" + " be set by user configuration.\n" + "\n" + " CHANGES TO THIS FILE WILL BE OVERWRITTEN", + NULL); + + success = g_key_file_save_to_file(keyfile, filename, &local); + + _LOGD("write intern config file \"%s\"%s%s", + filename, + success ? "" : ": ", + success ? "" : local->message); + g_key_file_unref(keyfile); + if (!success) + g_propagate_error(error, local); + return success; } /*****************************************************************************/ GSList * -nm_config_get_match_spec (const GKeyFile *keyfile, const char *group, const char *key, gboolean *out_has_key) +nm_config_get_match_spec(const GKeyFile *keyfile, + const char * group, + const char * key, + gboolean * out_has_key) { - gs_free char *value = NULL; + 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); + /* 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); } /*****************************************************************************/ gboolean -nm_config_set_global_dns (NMConfig *self, NMGlobalDnsConfig *global_dns, GError **error) -{ - NMConfigPrivate *priv; - GKeyFile *keyfile; - char **groups; - const NMGlobalDnsConfig *old_global_dns; - guint i; - - g_return_val_if_fail (NM_IS_CONFIG (self), FALSE); - - priv = NM_CONFIG_GET_PRIVATE (self); - g_return_val_if_fail (priv->config_data, FALSE); - - old_global_dns = nm_config_data_get_global_dns_config (priv->config_data); - if (old_global_dns && !nm_global_dns_config_is_internal (old_global_dns)) { - g_set_error_literal (error, 1, 0, - "Global DNS configuration already set via configuration file"); - return FALSE; - } - - keyfile = nm_config_data_clone_keyfile_intern (priv->config_data); - - /* Remove existing groups */ - g_key_file_remove_group (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, NULL); - groups = g_key_file_get_groups (keyfile, NULL); - for (i = 0; groups[i]; i++) { - if (NM_STR_HAS_PREFIX (groups[i], NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN)) - g_key_file_remove_group (keyfile, groups[i], NULL); - } - g_strfreev (groups); - - /* An empty configuration removes everything from internal configuration file */ - if (nm_global_dns_config_is_empty (global_dns)) - goto done; - - /* Set new values */ - nm_config_keyfile_set_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, - NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES, - nm_global_dns_config_get_searches (global_dns), - -1); - - nm_config_keyfile_set_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, - NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS, - nm_global_dns_config_get_options (global_dns), - -1); - - for (i = 0; i < nm_global_dns_config_get_num_domains (global_dns); i++) { - NMGlobalDnsDomain *domain = nm_global_dns_config_get_domain (global_dns, i); - gs_free char *group_name = NULL; - - group_name = g_strdup_printf (NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN "%s", - nm_global_dns_domain_get_name (domain)); - - nm_config_keyfile_set_string_list (keyfile, group_name, NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS, - nm_global_dns_domain_get_servers (domain), -1); - nm_config_keyfile_set_string_list (keyfile, group_name, NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS, - nm_global_dns_domain_get_options (domain), -1); - } +nm_config_set_global_dns(NMConfig *self, NMGlobalDnsConfig *global_dns, GError **error) +{ + NMConfigPrivate * priv; + GKeyFile * keyfile; + char ** groups; + const NMGlobalDnsConfig *old_global_dns; + guint i; + + g_return_val_if_fail(NM_IS_CONFIG(self), FALSE); + + priv = NM_CONFIG_GET_PRIVATE(self); + g_return_val_if_fail(priv->config_data, FALSE); + + old_global_dns = nm_config_data_get_global_dns_config(priv->config_data); + if (old_global_dns && !nm_global_dns_config_is_internal(old_global_dns)) { + g_set_error_literal(error, + 1, + 0, + "Global DNS configuration already set via configuration file"); + return FALSE; + } + + keyfile = nm_config_data_clone_keyfile_intern(priv->config_data); + + /* Remove existing groups */ + g_key_file_remove_group(keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, NULL); + groups = g_key_file_get_groups(keyfile, NULL); + for (i = 0; groups[i]; i++) { + if (NM_STR_HAS_PREFIX(groups[i], NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN)) + g_key_file_remove_group(keyfile, groups[i], NULL); + } + g_strfreev(groups); + + /* An empty configuration removes everything from internal configuration file */ + if (nm_global_dns_config_is_empty(global_dns)) + goto done; + + /* Set new values */ + nm_config_keyfile_set_string_list(keyfile, + NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES, + nm_global_dns_config_get_searches(global_dns), + -1); + + nm_config_keyfile_set_string_list(keyfile, + NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS, + nm_global_dns_config_get_options(global_dns), + -1); + + for (i = 0; i < nm_global_dns_config_get_num_domains(global_dns); i++) { + NMGlobalDnsDomain *domain = nm_global_dns_config_get_domain(global_dns, i); + gs_free char * group_name = NULL; + + group_name = g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN "%s", + nm_global_dns_domain_get_name(domain)); + + nm_config_keyfile_set_string_list(keyfile, + group_name, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS, + nm_global_dns_domain_get_servers(domain), + -1); + nm_config_keyfile_set_string_list(keyfile, + group_name, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS, + nm_global_dns_domain_get_options(domain), + -1); + } done: - nm_config_set_values (self, keyfile, TRUE, FALSE); - g_key_file_unref (keyfile); + nm_config_set_values(self, keyfile, TRUE, FALSE); + g_key_file_unref(keyfile); - return TRUE; + return TRUE; } /*****************************************************************************/ -void nm_config_set_connectivity_check_enabled (NMConfig *self, - gboolean enabled) +void +nm_config_set_connectivity_check_enabled(NMConfig *self, gboolean enabled) { - NMConfigPrivate *priv; - GKeyFile *keyfile; + NMConfigPrivate *priv; + GKeyFile * keyfile; - g_return_if_fail (NM_IS_CONFIG (self)); + g_return_if_fail(NM_IS_CONFIG(self)); - priv = NM_CONFIG_GET_PRIVATE (self); - g_return_if_fail (priv->config_data); + priv = NM_CONFIG_GET_PRIVATE(self); + g_return_if_fail(priv->config_data); - keyfile = nm_config_data_clone_keyfile_intern (priv->config_data); + keyfile = nm_config_data_clone_keyfile_intern(priv->config_data); - /* Remove existing groups */ - g_key_file_remove_group (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, NULL); + /* Remove existing groups */ + g_key_file_remove_group(keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, NULL); - g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, - "enabled", enabled ? "true" : "false"); + g_key_file_set_value(keyfile, + NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + "enabled", + enabled ? "true" : "false"); - nm_config_set_values (self, keyfile, TRUE, FALSE); - g_key_file_unref (keyfile); + nm_config_set_values(self, keyfile, TRUE, FALSE); + g_key_file_unref(keyfile); } /** @@ -1931,65 +2079,72 @@ void nm_config_set_connectivity_check_enabled (NMConfig *self, * NM_CONFIG_KEYFILE_KEYPREFIX_WAS"keyname" into the keyfile. */ void -nm_config_set_values (NMConfig *self, - GKeyFile *keyfile_intern_new, - gboolean allow_write, - gboolean force_rewrite) -{ - NMConfigPrivate *priv; - GKeyFile *keyfile_intern_current; - GKeyFile *keyfile_user; - GKeyFile *keyfile_new; - GError *local = NULL; - NMConfigData *new_data = NULL; - gs_strfreev char **groups = NULL; - int g; - - g_return_if_fail (NM_IS_CONFIG (self)); - - priv = NM_CONFIG_GET_PRIVATE (self); - - keyfile_intern_current = _nm_config_data_get_keyfile_intern (priv->config_data); - - keyfile_new = nm_config_create_keyfile (); - if (keyfile_intern_new) - _nm_keyfile_copy (keyfile_new, keyfile_intern_new); - - /* ensure that every atomic section has a .was entry. */ - groups = g_key_file_get_groups (keyfile_new, NULL); - for (g = 0; groups && groups[g]; g++) { - if (_is_atomic_section ((const char *const*) priv->atomic_section_prefixes, groups[g])) - g_key_file_set_value (keyfile_new, groups[g], NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, ""); - } - - if (!_nm_keyfile_equals (keyfile_intern_current, keyfile_new, TRUE)) - new_data = nm_config_data_new_update_keyfile_intern (priv->config_data, keyfile_new); - - _LOGD ("set values(): %s", new_data ? "has changes" : "no changes"); - - if (allow_write - && (new_data || force_rewrite)) { - /* We write the internal config file based on the user configuration from - * the last load/reload. That is correct, because the intern properties might - * be in accordance to what NM thinks is currently configured. Even if the files - * on disk changed in the meantime. - * But if they changed, on the next reload with might throw away our just - * written data. That is correct, because from NM's point of view, those - * changes on disk happened in any case *after* now. */ - if (*priv->intern_config_file) { - keyfile_user = _nm_config_data_get_keyfile_user (priv->config_data); - if (!intern_config_write (priv->intern_config_file, keyfile_new, keyfile_user, - (const char *const*) priv->atomic_section_prefixes, &local)) { - _LOGW ("error saving internal configuration \"%s\": %s", priv->intern_config_file, local->message); - g_clear_error (&local); - } - } else - _LOGD ("don't persist internal configuration (no file set, use --intern-config?)"); - } - if (new_data) - _set_config_data (self, new_data, NM_CONFIG_CHANGE_CAUSE_SET_VALUES); - - g_key_file_unref (keyfile_new); +nm_config_set_values(NMConfig *self, + GKeyFile *keyfile_intern_new, + gboolean allow_write, + gboolean force_rewrite) +{ + NMConfigPrivate * priv; + GKeyFile * keyfile_intern_current; + GKeyFile * keyfile_user; + GKeyFile * keyfile_new; + GError * local = NULL; + NMConfigData * new_data = NULL; + gs_strfreev char **groups = NULL; + int g; + + g_return_if_fail(NM_IS_CONFIG(self)); + + priv = NM_CONFIG_GET_PRIVATE(self); + + keyfile_intern_current = _nm_config_data_get_keyfile_intern(priv->config_data); + + keyfile_new = nm_config_create_keyfile(); + if (keyfile_intern_new) + _nm_keyfile_copy(keyfile_new, keyfile_intern_new); + + /* ensure that every atomic section has a .was entry. */ + groups = g_key_file_get_groups(keyfile_new, NULL); + for (g = 0; groups && groups[g]; g++) { + if (_is_atomic_section((const char *const *) priv->atomic_section_prefixes, groups[g])) + g_key_file_set_value(keyfile_new, + groups[g], + NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, + ""); + } + + if (!_nm_keyfile_equals(keyfile_intern_current, keyfile_new, TRUE)) + new_data = nm_config_data_new_update_keyfile_intern(priv->config_data, keyfile_new); + + _LOGD("set values(): %s", new_data ? "has changes" : "no changes"); + + if (allow_write && (new_data || force_rewrite)) { + /* We write the internal config file based on the user configuration from + * the last load/reload. That is correct, because the intern properties might + * be in accordance to what NM thinks is currently configured. Even if the files + * on disk changed in the meantime. + * But if they changed, on the next reload with might throw away our just + * written data. That is correct, because from NM's point of view, those + * changes on disk happened in any case *after* now. */ + if (*priv->intern_config_file) { + keyfile_user = _nm_config_data_get_keyfile_user(priv->config_data); + if (!intern_config_write(priv->intern_config_file, + keyfile_new, + keyfile_user, + (const char *const *) priv->atomic_section_prefixes, + &local)) { + _LOGW("error saving internal configuration \"%s\": %s", + priv->intern_config_file, + local->message); + g_clear_error(&local); + } + } else + _LOGD("don't persist internal configuration (no file set, use --intern-config?)"); + } + if (new_data) + _set_config_data(self, new_data, NM_CONFIG_CHANGE_CAUSE_SET_VALUES); + + g_key_file_unref(keyfile_new); } /****************************************************************************** @@ -1997,303 +2152,314 @@ nm_config_set_values (NMConfig *self, ******************************************************************************/ static const char * -state_get_filename (const NMConfigCmdLineOptions *cli) +state_get_filename(const NMConfigCmdLineOptions *cli) { - /* For an empty filename, we assume the user wants to disable - * state. NMConfig will not try to read it nor write it out. */ - if (!cli->state_file) - return DEFAULT_STATE_FILE; - return cli->state_file[0] ? cli->state_file : NULL; + /* For an empty filename, we assume the user wants to disable + * state. NMConfig will not try to read it nor write it out. */ + if (!cli->state_file) + return DEFAULT_STATE_FILE; + return cli->state_file[0] ? cli->state_file : NULL; } static State * -state_new (void) +state_new(void) { - State *state; + State *state; - state = g_slice_new0 (State); - state->p.net_enabled = TRUE; - state->p.wifi_enabled = TRUE; - state->p.wwan_enabled = TRUE; + state = g_slice_new0(State); + state->p.net_enabled = TRUE; + state->p.wifi_enabled = TRUE; + state->p.wwan_enabled = TRUE; - return state; + return state; } static void -state_free (State *state) +state_free(State *state) { - if (!state) - return; - g_slice_free (State, state); + if (!state) + return; + g_slice_free(State, state); } static State * -state_new_from_file (const char *filename) +state_new_from_file(const char *filename) { - GKeyFile *keyfile; - gs_free_error GError *error = NULL; - State *state; + GKeyFile * keyfile; + gs_free_error GError *error = NULL; + State * state; - state = state_new (); + state = state_new(); - if (!filename) - return state; + if (!filename) + return state; - keyfile = g_key_file_new (); - g_key_file_set_list_separator (keyfile, ','); - if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error)) { - if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) - _LOGD ("state: missing state file \"%s\": %s", filename, error->message); - else - _LOGW ("state: error reading state file \"%s\": %s", filename, error->message); - goto out; - } + keyfile = g_key_file_new(); + g_key_file_set_list_separator(keyfile, ','); + if (!g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, &error)) { + if (g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + _LOGD("state: missing state file \"%s\": %s", filename, error->message); + else + _LOGW("state: error reading state file \"%s\": %s", filename, error->message); + goto out; + } - _LOGD ("state: successfully read state file \"%s\"", filename); + _LOGD("state: successfully read state file \"%s\"", filename); - state->p.net_enabled = nm_config_keyfile_get_boolean (keyfile, "main", "NetworkingEnabled", state->p.net_enabled); - state->p.wifi_enabled = nm_config_keyfile_get_boolean (keyfile, "main", "WirelessEnabled", state->p.wifi_enabled); - state->p.wwan_enabled = nm_config_keyfile_get_boolean (keyfile, "main", "WWANEnabled", state->p.wwan_enabled); + state->p.net_enabled = + nm_config_keyfile_get_boolean(keyfile, "main", "NetworkingEnabled", state->p.net_enabled); + state->p.wifi_enabled = + nm_config_keyfile_get_boolean(keyfile, "main", "WirelessEnabled", state->p.wifi_enabled); + state->p.wwan_enabled = + nm_config_keyfile_get_boolean(keyfile, "main", "WWANEnabled", state->p.wwan_enabled); out: - g_key_file_unref (keyfile); - return state; + g_key_file_unref(keyfile); + return state; } const NMConfigState * -nm_config_state_get (NMConfig *self) +nm_config_state_get(NMConfig *self) { - NMConfigPrivate *priv; + NMConfigPrivate *priv; - g_return_val_if_fail (NM_IS_CONFIG (self), NULL); + g_return_val_if_fail(NM_IS_CONFIG(self), NULL); - priv = NM_CONFIG_GET_PRIVATE (self); + priv = NM_CONFIG_GET_PRIVATE(self); - if (G_UNLIKELY (!priv->state)) { - /* read the state from file lazy on first access. The reason is that - * we want to log a failure to read the file via nm-logging. - * - * So we cannot read the state during construction of NMConfig, - * because at that time nm-logging is not yet configured. - */ - priv->state = state_new_from_file (state_get_filename (&priv->cli)); - } + if (G_UNLIKELY(!priv->state)) { + /* read the state from file lazy on first access. The reason is that + * we want to log a failure to read the file via nm-logging. + * + * So we cannot read the state during construction of NMConfig, + * because at that time nm-logging is not yet configured. + */ + priv->state = state_new_from_file(state_get_filename(&priv->cli)); + } - return &priv->state->p; + return &priv->state->p; } static void -state_write (NMConfig *self) +state_write(NMConfig *self) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); - const char *filename; - GString *str; - GError *error = NULL; + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE(self); + const char * filename; + GString * str; + GError * error = NULL; - if (priv->configure_and_quit != NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED) - return; + if (priv->configure_and_quit != NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED) + return; - filename = state_get_filename (&priv->cli); + filename = state_get_filename(&priv->cli); - if (!filename) { - priv->state->p.dirty = FALSE; - return; - } + if (!filename) { + priv->state->p.dirty = FALSE; + return; + } - str = g_string_sized_new (256); + str = g_string_sized_new(256); - /* Let's construct the keyfile data by hand. */ + /* Let's construct the keyfile data by hand. */ - g_string_append (str, "[main]\n"); - g_string_append_printf (str, "NetworkingEnabled=%s\n", priv->state->p.net_enabled ? "true" : "false"); - g_string_append_printf (str, "WirelessEnabled=%s\n", priv->state->p.wifi_enabled ? "true" : "false"); - g_string_append_printf (str, "WWANEnabled=%s\n", priv->state->p.wwan_enabled ? "true" : "false"); + g_string_append(str, "[main]\n"); + g_string_append_printf(str, + "NetworkingEnabled=%s\n", + priv->state->p.net_enabled ? "true" : "false"); + g_string_append_printf(str, + "WirelessEnabled=%s\n", + priv->state->p.wifi_enabled ? "true" : "false"); + g_string_append_printf(str, "WWANEnabled=%s\n", priv->state->p.wwan_enabled ? "true" : "false"); - if (!g_file_set_contents (filename, - str->str, str->len, - &error)) { - _LOGD ("state: error writing state file \"%s\": %s", filename, error->message); - g_clear_error (&error); - /* we leave the state dirty. That potentially means, that we try to - * write the file over and over again, although it isn't possible. */ - priv->state->p.dirty = TRUE; - } else - priv->state->p.dirty = FALSE; + if (!g_file_set_contents(filename, str->str, str->len, &error)) { + _LOGD("state: error writing state file \"%s\": %s", filename, error->message); + g_clear_error(&error); + /* we leave the state dirty. That potentially means, that we try to + * write the file over and over again, although it isn't possible. */ + priv->state->p.dirty = TRUE; + } else + priv->state->p.dirty = FALSE; - _LOGT ("state: success writing state file \"%s\"", filename); + _LOGT("state: success writing state file \"%s\"", filename); - g_string_free (str, TRUE); + g_string_free(str, TRUE); } void -_nm_config_state_set (NMConfig *self, - gboolean allow_persist, - gboolean force_persist, - ...) -{ - NMConfigPrivate *priv; - va_list ap; - NMConfigRunStatePropertyType property_type; - - g_return_if_fail (NM_IS_CONFIG (self)); - - priv = NM_CONFIG_GET_PRIVATE (self); - - va_start (ap, force_persist); - - /* We expect that the NMConfigRunStatePropertyType is an integer type <= sizeof (int). - * Smaller would be fine, since the variadic arguments get promoted to int. - * Larger would be a problem, also, because we want that "0" is a valid sentinel. */ - G_STATIC_ASSERT_EXPR (sizeof (NMConfigRunStatePropertyType) <= sizeof (int)); - - while ((property_type = va_arg (ap, int)) != NM_CONFIG_STATE_PROPERTY_NONE) { - bool *p_bool, v_bool; - - switch (property_type) { - case NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED: - p_bool = &priv->state->p.net_enabled; - break; - case NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED: - p_bool = &priv->state->p.wifi_enabled; - break; - case NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED: - p_bool = &priv->state->p.wwan_enabled; - break; - default: - va_end (ap); - g_return_if_reached (); - } - - v_bool = va_arg (ap, gboolean); - if (*p_bool == v_bool) - continue; - *p_bool = v_bool; - priv->state->p.dirty = TRUE; - } - - va_end (ap); - - if ( allow_persist - && (force_persist || priv->state->p.dirty)) - state_write (self); +_nm_config_state_set(NMConfig *self, gboolean allow_persist, gboolean force_persist, ...) +{ + NMConfigPrivate * priv; + va_list ap; + NMConfigRunStatePropertyType property_type; + + g_return_if_fail(NM_IS_CONFIG(self)); + + priv = NM_CONFIG_GET_PRIVATE(self); + + va_start(ap, force_persist); + + /* We expect that the NMConfigRunStatePropertyType is an integer type <= sizeof (int). + * Smaller would be fine, since the variadic arguments get promoted to int. + * Larger would be a problem, also, because we want that "0" is a valid sentinel. */ + G_STATIC_ASSERT_EXPR(sizeof(NMConfigRunStatePropertyType) <= sizeof(int)); + + while ((property_type = va_arg(ap, int)) != NM_CONFIG_STATE_PROPERTY_NONE) { + bool *p_bool, v_bool; + + switch (property_type) { + case NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED: + p_bool = &priv->state->p.net_enabled; + break; + case NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED: + p_bool = &priv->state->p.wifi_enabled; + break; + case NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED: + p_bool = &priv->state->p.wwan_enabled; + break; + default: + va_end(ap); + g_return_if_reached(); + } + + v_bool = va_arg(ap, gboolean); + if (*p_bool == v_bool) + continue; + *p_bool = v_bool; + priv->state->p.dirty = TRUE; + } + + va_end(ap); + + if (allow_persist && (force_persist || priv->state->p.dirty)) + state_write(self); } /*****************************************************************************/ -#define DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE "device" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED "managed" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE "perm-hw-addr-fake" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID "connection-uuid" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED "nm-owned" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED "route-metric-default-aspired" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE "route-metric-default-effective" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH "root-path" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER "next-server" - -static -NM_UTILS_LOOKUP_STR_DEFINE (_device_state_managed_type_to_str, NMConfigDeviceStateManagedType, - NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"), - NM_UTILS_LOOKUP_STR_ITEM (NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN, "unknown"), - NM_UTILS_LOOKUP_STR_ITEM (NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED, "unmanaged"), - NM_UTILS_LOOKUP_STR_ITEM (NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED, "managed"), -); +#define DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE "device" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED "managed" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE "perm-hw-addr-fake" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID "connection-uuid" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED "nm-owned" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED \ + "route-metric-default-aspired" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE \ + "route-metric-default-effective" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH "root-path" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER "next-server" + +static NM_UTILS_LOOKUP_STR_DEFINE( + _device_state_managed_type_to_str, + NMConfigDeviceStateManagedType, + NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"), + NM_UTILS_LOOKUP_STR_ITEM(NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN, "unknown"), + NM_UTILS_LOOKUP_STR_ITEM(NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED, "unmanaged"), + NM_UTILS_LOOKUP_STR_ITEM(NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED, "managed"), ); static NMConfigDeviceStateData * -_config_device_state_data_new (int ifindex, GKeyFile *kf) -{ - NMConfigDeviceStateData *device_state; - NMConfigDeviceStateManagedType managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN; - gs_free char *connection_uuid = NULL; - gs_free char *perm_hw_addr_fake = NULL; - gsize connection_uuid_len; - gsize perm_hw_addr_fake_len; - NMTernary nm_owned; - char *p; - guint32 route_metric_default_effective; - guint32 route_metric_default_aspired; - - nm_assert (kf); - nm_assert (ifindex > 0); - - switch (nm_config_keyfile_get_boolean (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED, - -1)) { - case TRUE: - managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED; - connection_uuid = nm_config_keyfile_get_value (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID, - NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); - break; - case FALSE: - managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED; - break; - case -1: - /* missing property in keyfile. */ - break; - } - - perm_hw_addr_fake = nm_config_keyfile_get_value (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE, - NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); - if (perm_hw_addr_fake) { - char *normalized; - - normalized = nm_utils_hwaddr_canonical (perm_hw_addr_fake, -1); - g_free (perm_hw_addr_fake); - perm_hw_addr_fake = normalized; - } - - nm_owned = nm_config_keyfile_get_boolean (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED, - NM_TERNARY_DEFAULT); - - /* metric zero is not a valid metric. While zero valid for IPv4, for IPv6 it is an alias - * for 1024. Since we handle here IPv4 and IPv6 the same, we cannot allow zero. */ - route_metric_default_effective = nm_config_keyfile_get_int64 (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE, - 10, 1, G_MAXUINT32, 0); - if (route_metric_default_effective) { - route_metric_default_aspired = nm_config_keyfile_get_int64 (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE, - 10, 1, route_metric_default_effective, - route_metric_default_effective); - } else - route_metric_default_aspired = 0; - - connection_uuid_len = connection_uuid ? strlen (connection_uuid) + 1 : 0; - perm_hw_addr_fake_len = perm_hw_addr_fake ? strlen (perm_hw_addr_fake) + 1 : 0; - - device_state = g_malloc (sizeof (NMConfigDeviceStateData) + - connection_uuid_len + - perm_hw_addr_fake_len); - - device_state->ifindex = ifindex; - device_state->managed = managed_type; - device_state->connection_uuid = NULL; - device_state->perm_hw_addr_fake = NULL; - device_state->nm_owned = nm_owned; - device_state->route_metric_default_aspired = route_metric_default_aspired; - device_state->route_metric_default_effective = route_metric_default_effective; - - p = (char *) (&device_state[1]); - if (connection_uuid) { - memcpy (p, connection_uuid, connection_uuid_len); - device_state->connection_uuid = p; - p += connection_uuid_len; - } - if (perm_hw_addr_fake) { - memcpy (p, perm_hw_addr_fake, perm_hw_addr_fake_len); - device_state->perm_hw_addr_fake = p; - p += perm_hw_addr_fake_len; - } - - return device_state; +_config_device_state_data_new(int ifindex, GKeyFile *kf) +{ + NMConfigDeviceStateData * device_state; + NMConfigDeviceStateManagedType managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN; + gs_free char * connection_uuid = NULL; + gs_free char * perm_hw_addr_fake = NULL; + gsize connection_uuid_len; + gsize perm_hw_addr_fake_len; + NMTernary nm_owned; + char * p; + guint32 route_metric_default_effective; + guint32 route_metric_default_aspired; + + nm_assert(kf); + nm_assert(ifindex > 0); + + switch (nm_config_keyfile_get_boolean(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED, + -1)) { + case TRUE: + managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED; + connection_uuid = + nm_config_keyfile_get_value(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID, + NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); + break; + case FALSE: + managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED; + break; + case -1: + /* missing property in keyfile. */ + break; + } + + perm_hw_addr_fake = + nm_config_keyfile_get_value(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE, + NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); + if (perm_hw_addr_fake) { + char *normalized; + + normalized = nm_utils_hwaddr_canonical(perm_hw_addr_fake, -1); + g_free(perm_hw_addr_fake); + perm_hw_addr_fake = normalized; + } + + nm_owned = nm_config_keyfile_get_boolean(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED, + NM_TERNARY_DEFAULT); + + /* metric zero is not a valid metric. While zero valid for IPv4, for IPv6 it is an alias + * for 1024. Since we handle here IPv4 and IPv6 the same, we cannot allow zero. */ + route_metric_default_effective = nm_config_keyfile_get_int64( + kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE, + 10, + 1, + G_MAXUINT32, + 0); + if (route_metric_default_effective) { + route_metric_default_aspired = nm_config_keyfile_get_int64( + kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE, + 10, + 1, + route_metric_default_effective, + route_metric_default_effective); + } else + route_metric_default_aspired = 0; + + connection_uuid_len = connection_uuid ? strlen(connection_uuid) + 1 : 0; + perm_hw_addr_fake_len = perm_hw_addr_fake ? strlen(perm_hw_addr_fake) + 1 : 0; + + device_state = + g_malloc(sizeof(NMConfigDeviceStateData) + connection_uuid_len + perm_hw_addr_fake_len); + + device_state->ifindex = ifindex; + device_state->managed = managed_type; + device_state->connection_uuid = NULL; + device_state->perm_hw_addr_fake = NULL; + device_state->nm_owned = nm_owned; + device_state->route_metric_default_aspired = route_metric_default_aspired; + device_state->route_metric_default_effective = route_metric_default_effective; + + p = (char *) (&device_state[1]); + if (connection_uuid) { + memcpy(p, connection_uuid, connection_uuid_len); + device_state->connection_uuid = p; + p += connection_uuid_len; + } + if (perm_hw_addr_fake) { + memcpy(p, perm_hw_addr_fake, perm_hw_addr_fake_len); + device_state->perm_hw_addr_fake = p; + p += perm_hw_addr_fake_len; + } + + return device_state; } #define DEVICE_STATE_FILENAME_LEN_MAX 60 @@ -2306,233 +2472,242 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf) * Must be freed with g_free(). */ NMConfigDeviceStateData * -nm_config_device_state_load (int ifindex) -{ - NMConfigDeviceStateData *device_state; - char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/") + DEVICE_STATE_FILENAME_LEN_MAX + 1]; - gs_unref_keyfile GKeyFile *kf = NULL; - const char *nm_owned_str; - - g_return_val_if_fail (ifindex > 0, NULL); - - nm_sprintf_buf (path, "%s/%d", NM_CONFIG_DEVICE_STATE_DIR, ifindex); - - kf = nm_config_create_keyfile (); - if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL)) - return NULL; - - device_state = _config_device_state_data_new (ifindex, kf); - nm_owned_str = device_state->nm_owned == NM_TERNARY_TRUE - ? ", nm-owned=1" - : ( device_state->nm_owned == NM_TERNARY_FALSE - ? ", nm-owned=0" - : ""); - - _LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"", - kf ? "read" : "miss", - ifindex, path, - _device_state_managed_type_to_str (device_state->managed), - NM_PRINT_FMT_QUOTED (device_state->connection_uuid, ", connection-uuid=", device_state->connection_uuid, "", ""), - NM_PRINT_FMT_QUOTED (device_state->perm_hw_addr_fake, ", perm-hw-addr-fake=", device_state->perm_hw_addr_fake, "", ""), - nm_owned_str, - device_state->route_metric_default_aspired, - device_state->route_metric_default_effective); - - return device_state; +nm_config_device_state_load(int ifindex) +{ + NMConfigDeviceStateData *device_state; + char path[NM_STRLEN(NM_CONFIG_DEVICE_STATE_DIR "/") + DEVICE_STATE_FILENAME_LEN_MAX + 1]; + nm_auto_unref_keyfile GKeyFile *kf = NULL; + const char * nm_owned_str; + + g_return_val_if_fail(ifindex > 0, NULL); + + nm_sprintf_buf(path, "%s/%d", NM_CONFIG_DEVICE_STATE_DIR, ifindex); + + kf = nm_config_create_keyfile(); + if (!g_key_file_load_from_file(kf, path, G_KEY_FILE_NONE, NULL)) + return NULL; + + device_state = _config_device_state_data_new(ifindex, kf); + nm_owned_str = device_state->nm_owned == NM_TERNARY_TRUE + ? ", nm-owned=1" + : (device_state->nm_owned == NM_TERNARY_FALSE ? ", nm-owned=0" : ""); + + _LOGT("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s, " + "route-metric-default=%" G_GUINT32_FORMAT "-%" G_GUINT32_FORMAT "", + kf ? "read" : "miss", + ifindex, + path, + _device_state_managed_type_to_str(device_state->managed), + NM_PRINT_FMT_QUOTED(device_state->connection_uuid, + ", connection-uuid=", + device_state->connection_uuid, + "", + ""), + NM_PRINT_FMT_QUOTED(device_state->perm_hw_addr_fake, + ", perm-hw-addr-fake=", + device_state->perm_hw_addr_fake, + "", + ""), + nm_owned_str, + device_state->route_metric_default_aspired, + device_state->route_metric_default_effective); + + return device_state; } static int -_device_state_parse_filename (const char *filename) +_device_state_parse_filename(const char *filename) { - if (!filename || !filename[0]) - return 0; - if (!NM_STRCHAR_ALL (filename, ch, g_ascii_isdigit (ch))) - return 0; - return _nm_utils_ascii_str_to_int64 (filename, 10, 1, G_MAXINT, 0); + if (!filename || !filename[0]) + return 0; + if (!NM_STRCHAR_ALL(filename, ch, g_ascii_isdigit(ch))) + return 0; + return _nm_utils_ascii_str_to_int64(filename, 10, 1, G_MAXINT, 0); } GHashTable * -nm_config_device_state_load_all (void) +nm_config_device_state_load_all(void) { - GHashTable *states; - GDir *dir; - const char *fn; - int ifindex; + GHashTable *states; + GDir * dir; + const char *fn; + int ifindex; - states = g_hash_table_new_full (nm_direct_hash, NULL, NULL, g_free); + states = g_hash_table_new_full(nm_direct_hash, NULL, NULL, g_free); - dir = g_dir_open (NM_CONFIG_DEVICE_STATE_DIR, 0, NULL); - if (!dir) - return states; + dir = g_dir_open(NM_CONFIG_DEVICE_STATE_DIR, 0, NULL); + if (!dir) + return states; - while ((fn = g_dir_read_name (dir))) { - NMConfigDeviceStateData *state; + while ((fn = g_dir_read_name(dir))) { + NMConfigDeviceStateData *state; - ifindex = _device_state_parse_filename (fn); - if (ifindex <= 0) - continue; + ifindex = _device_state_parse_filename(fn); + if (ifindex <= 0) + continue; - state = nm_config_device_state_load (ifindex); - if (!state) - continue; + state = nm_config_device_state_load(ifindex); + if (!state) + continue; - if (!g_hash_table_insert (states, GINT_TO_POINTER (ifindex), state)) - nm_assert_not_reached (); - } - g_dir_close (dir); + if (!g_hash_table_insert(states, GINT_TO_POINTER(ifindex), state)) + nm_assert_not_reached(); + } + g_dir_close(dir); - return states; + return states; } gboolean -nm_config_device_state_write (int ifindex, - NMConfigDeviceStateManagedType managed, - const char *perm_hw_addr_fake, - const char *connection_uuid, - NMTernary nm_owned, - guint32 route_metric_default_aspired, - guint32 route_metric_default_effective, - const char *next_server, - const char *root_path) -{ - char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/") + DEVICE_STATE_FILENAME_LEN_MAX + 1]; - GError *local = NULL; - gs_unref_keyfile GKeyFile *kf = NULL; - - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (!connection_uuid || *connection_uuid, FALSE); - g_return_val_if_fail (managed == NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED || !connection_uuid, FALSE); - - nm_assert (!perm_hw_addr_fake || nm_utils_hwaddr_valid (perm_hw_addr_fake, -1)); - - nm_sprintf_buf (path, "%s/%d", NM_CONFIG_DEVICE_STATE_DIR, ifindex); - - kf = nm_config_create_keyfile (); - if (NM_IN_SET (managed, - NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED, - NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED)) { - g_key_file_set_boolean (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED, - managed == NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED); - } - if (perm_hw_addr_fake) { - g_key_file_set_string (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE, - perm_hw_addr_fake); - } - if (connection_uuid) { - g_key_file_set_string (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID, - connection_uuid); - } - if (nm_owned != NM_TERNARY_DEFAULT) { - g_key_file_set_boolean (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED, - nm_owned); - } - - if (route_metric_default_effective != 0) { - g_key_file_set_int64 (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE, - route_metric_default_effective); - if (route_metric_default_aspired != route_metric_default_effective) { - g_key_file_set_int64 (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED, - route_metric_default_aspired); - } - } - if (next_server) { - g_key_file_set_string (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER, - next_server); - } - if (root_path) { - g_key_file_set_string (kf, - DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, - DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH, - root_path); - } - - if (!g_key_file_save_to_file (kf, path, &local)) { - _LOGW ("device-state: write #%d (%s) failed: %s", ifindex, path, local->message); - g_error_free (local); - return FALSE; - } - _LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"%s%s%s%s%s%s", - ifindex, path, - _device_state_managed_type_to_str (managed), - NM_PRINT_FMT_QUOTED (connection_uuid, ", connection-uuid=", connection_uuid, "", ""), - NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""), - route_metric_default_aspired, - route_metric_default_effective, - NM_PRINT_FMT_QUOTED (next_server, ", next-server=", next_server, "", ""), - NM_PRINT_FMT_QUOTED (root_path, ", root-path=", root_path, "", "")); - return TRUE; +nm_config_device_state_write(int ifindex, + NMConfigDeviceStateManagedType managed, + const char * perm_hw_addr_fake, + const char * connection_uuid, + NMTernary nm_owned, + guint32 route_metric_default_aspired, + guint32 route_metric_default_effective, + const char * next_server, + const char * root_path) +{ + char path[NM_STRLEN(NM_CONFIG_DEVICE_STATE_DIR "/") + DEVICE_STATE_FILENAME_LEN_MAX + 1]; + GError *local = NULL; + nm_auto_unref_keyfile GKeyFile *kf = NULL; + + g_return_val_if_fail(ifindex > 0, FALSE); + g_return_val_if_fail(!connection_uuid || *connection_uuid, FALSE); + g_return_val_if_fail(managed == NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED || !connection_uuid, + FALSE); + + nm_assert(!perm_hw_addr_fake || nm_utils_hwaddr_valid(perm_hw_addr_fake, -1)); + + nm_sprintf_buf(path, "%s/%d", NM_CONFIG_DEVICE_STATE_DIR, ifindex); + + kf = nm_config_create_keyfile(); + if (NM_IN_SET(managed, + NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED, + NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED)) { + g_key_file_set_boolean(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED, + managed == NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED); + } + if (perm_hw_addr_fake) { + g_key_file_set_string(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE, + perm_hw_addr_fake); + } + if (connection_uuid) { + g_key_file_set_string(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID, + connection_uuid); + } + if (nm_owned != NM_TERNARY_DEFAULT) { + g_key_file_set_boolean(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED, + nm_owned); + } + + if (route_metric_default_effective != 0) { + g_key_file_set_int64(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE, + route_metric_default_effective); + if (route_metric_default_aspired != route_metric_default_effective) { + g_key_file_set_int64(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED, + route_metric_default_aspired); + } + } + if (next_server) { + g_key_file_set_string(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER, + next_server); + } + if (root_path) { + g_key_file_set_string(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH, + root_path); + } + + if (!g_key_file_save_to_file(kf, path, &local)) { + _LOGW("device-state: write #%d (%s) failed: %s", ifindex, path, local->message); + g_error_free(local); + return FALSE; + } + _LOGT("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, " + "route-metric-default=%" G_GUINT32_FORMAT "-%" G_GUINT32_FORMAT "%s%s%s%s%s%s", + ifindex, + path, + _device_state_managed_type_to_str(managed), + NM_PRINT_FMT_QUOTED(connection_uuid, ", connection-uuid=", connection_uuid, "", ""), + NM_PRINT_FMT_QUOTED(perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""), + route_metric_default_aspired, + route_metric_default_effective, + NM_PRINT_FMT_QUOTED(next_server, ", next-server=", next_server, "", ""), + NM_PRINT_FMT_QUOTED(root_path, ", root-path=", root_path, "", "")); + return TRUE; } void -nm_config_device_state_prune_stale (GHashTable *preserve_ifindexes, - NMPlatform *preserve_in_platform) +nm_config_device_state_prune_stale(GHashTable *preserve_ifindexes, NMPlatform *preserve_in_platform) { - GDir *dir; - const char *fn; - char buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/") + DEVICE_STATE_FILENAME_LEN_MAX + 1] = NM_CONFIG_DEVICE_STATE_DIR"/"; - char *buf_p = &buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/")]; + GDir * dir; + const char *fn; + char buf[NM_STRLEN(NM_CONFIG_DEVICE_STATE_DIR "/") + DEVICE_STATE_FILENAME_LEN_MAX + 1] = + NM_CONFIG_DEVICE_STATE_DIR "/"; + char *buf_p = &buf[NM_STRLEN(NM_CONFIG_DEVICE_STATE_DIR "/")]; - dir = g_dir_open (NM_CONFIG_DEVICE_STATE_DIR, 0, NULL); - if (!dir) - return; + dir = g_dir_open(NM_CONFIG_DEVICE_STATE_DIR, 0, NULL); + if (!dir) + return; - while ((fn = g_dir_read_name (dir))) { - int ifindex; - gsize fn_len; + while ((fn = g_dir_read_name(dir))) { + int ifindex; + gsize fn_len; - ifindex = _device_state_parse_filename (fn); - if (ifindex <= 0) - continue; + ifindex = _device_state_parse_filename(fn); + if (ifindex <= 0) + continue; - if ( preserve_ifindexes - && g_hash_table_contains (preserve_ifindexes, GINT_TO_POINTER (ifindex))) - continue; + if (preserve_ifindexes + && g_hash_table_contains(preserve_ifindexes, GINT_TO_POINTER(ifindex))) + continue; - if ( preserve_in_platform - && nm_platform_link_get (preserve_in_platform, ifindex)) - continue; + if (preserve_in_platform && nm_platform_link_get(preserve_in_platform, ifindex)) + continue; - fn_len = strlen (fn); - nm_assert (fn_len > 0); - nm_assert (&buf_p[fn_len] < &buf[G_N_ELEMENTS (buf)]); - memcpy (buf_p, fn, fn_len + 1u); - nm_assert (({ - char bb[30]; + fn_len = strlen(fn); + nm_assert(fn_len > 0); + nm_assert(&buf_p[fn_len] < &buf[G_N_ELEMENTS(buf)]); + memcpy(buf_p, fn, fn_len + 1u); + nm_assert(({ + char bb[30]; - nm_streq0 (nm_sprintf_buf (bb, "%d", ifindex), - buf_p); - })); - _LOGT ("device-state: prune #%d (%s)", ifindex, buf); - (void) unlink (buf); - } + nm_streq0(nm_sprintf_buf(bb, "%d", ifindex), buf_p); + })); + _LOGT("device-state: prune #%d (%s)", ifindex, buf); + (void) unlink(buf); + } - g_dir_close (dir); + g_dir_close(dir); } /*****************************************************************************/ static GHashTable * -_device_state_get_all (NMConfig *self) +_device_state_get_all(NMConfig *self) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE(self); - if (G_UNLIKELY (!priv->device_states)) - priv->device_states = nm_config_device_state_load_all (); - return priv->device_states; + if (G_UNLIKELY(!priv->device_states)) + priv->device_states = nm_config_device_state_load_all(); + return priv->device_states; } /** @@ -2546,423 +2721,437 @@ _device_state_get_all (NMConfig *self) * Returns: the internal #GHashTable object with all device states. */ const GHashTable * -nm_config_device_state_get_all (NMConfig *self) +nm_config_device_state_get_all(NMConfig *self) { - g_return_val_if_fail (NM_IS_CONFIG (self), NULL); + g_return_val_if_fail(NM_IS_CONFIG(self), NULL); - return _device_state_get_all (self); + return _device_state_get_all(self); } const NMConfigDeviceStateData * -nm_config_device_state_get (NMConfig *self, - int ifindex) +nm_config_device_state_get(NMConfig *self, int ifindex) { - g_return_val_if_fail (NM_IS_CONFIG (self), NULL); - g_return_val_if_fail (ifindex > 0 , NULL); + g_return_val_if_fail(NM_IS_CONFIG(self), NULL); + g_return_val_if_fail(ifindex > 0, NULL); - return g_hash_table_lookup (_device_state_get_all (self), GINT_TO_POINTER (ifindex)); + return g_hash_table_lookup(_device_state_get_all(self), GINT_TO_POINTER(ifindex)); } /*****************************************************************************/ void -nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags, gboolean emit_warnings) -{ - NMConfigPrivate *priv; - GError *error = NULL; - GKeyFile *keyfile, *keyfile_intern; - NMConfigData *new_data = NULL; - char *config_main_file = NULL; - char *config_description = NULL; - gs_strfreev char **no_auto_default = NULL; - gboolean intern_config_needs_rewrite; - gs_unref_ptrarray GPtrArray *warnings = NULL; - guint i; - - g_return_if_fail (NM_IS_CONFIG (self)); - g_return_if_fail ( reload_flags - && !NM_FLAGS_ANY (reload_flags, ~NM_CONFIG_CHANGE_CAUSES) - && !NM_FLAGS_ANY (reload_flags, NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT - | NM_CONFIG_CHANGE_CAUSE_SET_VALUES)); - - priv = NM_CONFIG_GET_PRIVATE (self); - - if (!NM_FLAGS_ANY (reload_flags, NM_CONFIG_CHANGE_CAUSE_SIGHUP | NM_CONFIG_CHANGE_CAUSE_CONF)) { - /* unless SIGHUP is specified, we don't reload the configuration from disc. */ - _set_config_data (self, NULL, reload_flags); - return; - } - - warnings = g_ptr_array_new_with_free_func (g_free); - - /* 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, - priv->system_config_dir, - &config_main_file, - &config_description, - warnings, - &error); - if (!keyfile) { - _LOGE ("Failed to reload the configuration: %s", error->message); - g_clear_error (&error); - _set_config_data (self, NULL, reload_flags); - return; - } - - no_auto_default = no_auto_default_from_file (priv->no_auto_default_file); - - keyfile_intern = intern_config_read (priv->intern_config_file, - keyfile, - (const char *const*) priv->atomic_section_prefixes, - &intern_config_needs_rewrite); - if (intern_config_needs_rewrite) { - intern_config_write (priv->intern_config_file, keyfile_intern, keyfile, - (const char *const*) priv->atomic_section_prefixes, NULL); - } - - new_data = nm_config_data_new (config_main_file, - config_description, - (const char *const*) no_auto_default, - keyfile, - keyfile_intern); - - if (emit_warnings) { - nm_config_data_get_warnings (priv->config_data_orig, warnings); - for (i = 0; i < warnings->len; i++) - _LOGW ("%s", (const char *) warnings->pdata[i]); - } - - g_free (config_main_file); - g_free (config_description); - g_key_file_unref (keyfile); - if (keyfile_intern) - g_key_file_unref (keyfile_intern); - - _set_config_data (self, new_data, reload_flags); -} - -NM_UTILS_FLAGS2STR_DEFINE (nm_config_change_flags_to_string, NMConfigChangeFlags, - - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CAUSE_CONF, "CONF"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CAUSE_DNS_RC, "DNS_RC"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CAUSE_DNS_FULL, "DNS_FULL"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CAUSE_SIGHUP, "SIGHUP"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CAUSE_SIGUSR1, "SIGUSR1"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CAUSE_SIGUSR2, "SIGUSR2"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT, "NO_AUTO_DEFAULT"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CAUSE_SET_VALUES, "SET_VALUES"), - - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CONFIG_FILES, "config-files"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_VALUES, "values"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_VALUES_USER, "values-user"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_VALUES_INTERN, "values-intern"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_CONNECTIVITY, "connectivity"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_NO_AUTO_DEFAULT, "no-auto-default"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_DNS_MODE, "dns-mode"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_RC_MANAGER, "rc-manager"), - NM_UTILS_FLAGS2STR (NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG, "global-dns-config"), -); +nm_config_reload(NMConfig *self, NMConfigChangeFlags reload_flags, gboolean emit_warnings) +{ + NMConfigPrivate * priv; + GError * error = NULL; + GKeyFile * keyfile, *keyfile_intern; + NMConfigData * new_data = NULL; + char * config_main_file = NULL; + char * config_description = NULL; + gs_strfreev char **no_auto_default = NULL; + gboolean intern_config_needs_rewrite; + gs_unref_ptrarray GPtrArray *warnings = NULL; + guint i; + + g_return_if_fail(NM_IS_CONFIG(self)); + g_return_if_fail(reload_flags && !NM_FLAGS_ANY(reload_flags, ~NM_CONFIG_CHANGE_CAUSES) + && !NM_FLAGS_ANY(reload_flags, + NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT + | NM_CONFIG_CHANGE_CAUSE_SET_VALUES)); + + priv = NM_CONFIG_GET_PRIVATE(self); + + if (!NM_FLAGS_ANY(reload_flags, NM_CONFIG_CHANGE_CAUSE_SIGHUP | NM_CONFIG_CHANGE_CAUSE_CONF)) { + /* unless SIGHUP is specified, we don't reload the configuration from disc. */ + _set_config_data(self, NULL, reload_flags); + return; + } + + warnings = g_ptr_array_new_with_free_func(g_free); + + /* 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, + priv->system_config_dir, + &config_main_file, + &config_description, + warnings, + &error); + if (!keyfile) { + _LOGE("Failed to reload the configuration: %s", error->message); + g_clear_error(&error); + _set_config_data(self, NULL, reload_flags); + return; + } + + no_auto_default = no_auto_default_from_file(priv->no_auto_default_file); + + keyfile_intern = intern_config_read(priv->intern_config_file, + keyfile, + (const char *const *) priv->atomic_section_prefixes, + &intern_config_needs_rewrite); + if (intern_config_needs_rewrite) { + intern_config_write(priv->intern_config_file, + keyfile_intern, + keyfile, + (const char *const *) priv->atomic_section_prefixes, + NULL); + } + + new_data = nm_config_data_new(config_main_file, + config_description, + (const char *const *) no_auto_default, + keyfile, + keyfile_intern); + + if (emit_warnings) { + nm_config_data_get_warnings(priv->config_data_orig, warnings); + for (i = 0; i < warnings->len; i++) + _LOGW("%s", (const char *) warnings->pdata[i]); + } + + g_free(config_main_file); + g_free(config_description); + g_key_file_unref(keyfile); + if (keyfile_intern) + g_key_file_unref(keyfile_intern); + + _set_config_data(self, new_data, reload_flags); +} + +NM_UTILS_FLAGS2STR_DEFINE(nm_config_change_flags_to_string, + NMConfigChangeFlags, + + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CAUSE_CONF, "CONF"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CAUSE_DNS_RC, "DNS_RC"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CAUSE_DNS_FULL, "DNS_FULL"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CAUSE_SIGHUP, "SIGHUP"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CAUSE_SIGUSR1, "SIGUSR1"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CAUSE_SIGUSR2, "SIGUSR2"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT, + "NO_AUTO_DEFAULT"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CAUSE_SET_VALUES, "SET_VALUES"), + + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CONFIG_FILES, "config-files"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_VALUES, "values"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_VALUES_USER, "values-user"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_VALUES_INTERN, "values-intern"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_CONNECTIVITY, "connectivity"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_NO_AUTO_DEFAULT, "no-auto-default"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_DNS_MODE, "dns-mode"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_RC_MANAGER, "rc-manager"), + NM_UTILS_FLAGS2STR(NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG, + "global-dns-config"), ); static void -_set_config_data (NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags reload_flags) -{ - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); - NMConfigData *old_data = priv->config_data; - NMConfigChangeFlags changes, changes_diff; - gboolean had_new_data = !!new_data; - - nm_assert (reload_flags); - nm_assert (!NM_FLAGS_ANY (reload_flags, ~NM_CONFIG_CHANGE_CAUSES)); - nm_assert ( NM_IN_SET (reload_flags, NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT, NM_CONFIG_CHANGE_CAUSE_SET_VALUES) - || !NM_FLAGS_ANY (reload_flags, NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT | NM_CONFIG_CHANGE_CAUSE_SET_VALUES)); - - changes = reload_flags; - - 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 ( NM_IN_SET (reload_flags, - NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT, - NM_CONFIG_CHANGE_CAUSE_SET_VALUES, - NM_CONFIG_CHANGE_CAUSE_CONF) - && !new_data) { - /* no relevant changes that should be propagated. Return silently. */ - return; - } - - if (new_data) { - _LOGI ("signal: %s (%s)", - nm_config_change_flags_to_string (changes, NULL, 0), - nm_config_data_get_config_description (new_data)); - nm_config_data_log (new_data, "CONFIG: ", " ", priv->no_auto_default_file, NULL); - priv->config_data = new_data; - } else if (had_new_data) - _LOGI ("signal: %s (no changes from disk)", nm_config_change_flags_to_string (changes, NULL, 0)); - else - _LOGI ("signal: %s", nm_config_change_flags_to_string (changes, NULL, 0)); - g_signal_emit (self, signals[SIGNAL_CONFIG_CHANGED], 0, - new_data ?: old_data, - changes, old_data); - if (new_data) - g_object_unref (old_data); -} - -NM_DEFINE_SINGLETON_REGISTER (NMConfig); +_set_config_data(NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags reload_flags) +{ + NMConfigPrivate * priv = NM_CONFIG_GET_PRIVATE(self); + NMConfigData * old_data = priv->config_data; + NMConfigChangeFlags changes, changes_diff; + gboolean had_new_data = !!new_data; + + nm_assert(reload_flags); + nm_assert(!NM_FLAGS_ANY(reload_flags, ~NM_CONFIG_CHANGE_CAUSES)); + nm_assert(NM_IN_SET(reload_flags, + NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT, + NM_CONFIG_CHANGE_CAUSE_SET_VALUES) + || !NM_FLAGS_ANY(reload_flags, + NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT + | NM_CONFIG_CHANGE_CAUSE_SET_VALUES)); + + changes = reload_flags; + + 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 (NM_IN_SET(reload_flags, + NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT, + NM_CONFIG_CHANGE_CAUSE_SET_VALUES, + NM_CONFIG_CHANGE_CAUSE_CONF) + && !new_data) { + /* no relevant changes that should be propagated. Return silently. */ + return; + } + + if (new_data) { + _LOGI("signal: %s (%s)", + nm_config_change_flags_to_string(changes, NULL, 0), + nm_config_data_get_config_description(new_data)); + nm_config_data_log(new_data, "CONFIG: ", " ", priv->no_auto_default_file, NULL); + priv->config_data = new_data; + } else if (had_new_data) + _LOGI("signal: %s (no changes from disk)", + nm_config_change_flags_to_string(changes, NULL, 0)); + else + _LOGI("signal: %s", nm_config_change_flags_to_string(changes, NULL, 0)); + g_signal_emit(self, signals[SIGNAL_CONFIG_CHANGED], 0, new_data ?: old_data, changes, old_data); + if (new_data) + g_object_unref(old_data); +} + +NM_DEFINE_SINGLETON_REGISTER(NMConfig); NMConfig * -nm_config_get (void) +nm_config_get(void) { - g_assert (singleton_instance); - return singleton_instance; + g_assert(singleton_instance); + return singleton_instance; } NMConfig * -nm_config_setup (const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error) +nm_config_setup(const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error) { - g_assert (!singleton_instance); + g_assert(!singleton_instance); - singleton_instance = nm_config_new (cli, atomic_section_prefixes, error); - if (singleton_instance) { - nm_singleton_instance_register (); + singleton_instance = nm_config_new(cli, atomic_section_prefixes, error); + if (singleton_instance) { + nm_singleton_instance_register(); - /* usually, you would not see this logging line because when creating the - * NMConfig instance, the logging is not yet set up to print debug message. */ - nm_log_dbg (LOGD_CORE, "setup %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")", - "NMConfig", NM_HASH_OBFUSCATE_PTR (singleton_instance)); - } - return singleton_instance; + /* usually, you would not see this logging line because when creating the + * NMConfig instance, the logging is not yet set up to print debug message. */ + nm_log_dbg(LOGD_CORE, + "setup %s singleton (" NM_HASH_OBFUSCATE_PTR_FMT ")", + "NMConfig", + NM_HASH_OBFUSCATE_PTR(singleton_instance)); + } + return singleton_instance; } /*****************************************************************************/ 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; - char **strv; - - 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; - case PROP_ATOMIC_SECTION_PREFIXES: - /* construct-only */ - strv = g_value_get_boxed (value); - if (strv && !strv[0]) - strv = NULL; - priv->atomic_section_prefixes = g_strdupv (strv); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } +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; + char ** strv; + + 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; + case PROP_ATOMIC_SECTION_PREFIXES: + /* construct-only */ + strv = g_value_get_boxed(value); + if (strv && !strv[0]) + strv = NULL; + priv->atomic_section_prefixes = g_strdupv(strv); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } } /*****************************************************************************/ static gboolean -init_sync (GInitable *initable, GCancellable *cancellable, GError **error) -{ - NMConfig *self = NM_CONFIG (initable); - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); - gs_unref_keyfile GKeyFile *keyfile = NULL; - gs_unref_keyfile GKeyFile *keyfile_intern = NULL; - gs_free char *config_main_file = NULL; - gs_free char *config_description = NULL; - gs_strfreev char **no_auto_default = NULL; - gs_unref_ptrarray GPtrArray *warnings = NULL; - gs_free char *configure_and_quit = NULL; - gboolean intern_config_needs_rewrite; - const char *s; - - 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"); - g_return_val_if_reached (FALSE); - } - - s = priv->cli.config_dir ?: ""DEFAULT_CONFIG_DIR; - priv->config_dir = g_strdup (s[0] == '/' ? s : ""); - - s = priv->cli.system_config_dir ?: ""DEFAULT_SYSTEM_CONFIG_DIR; - if ( s[0] != '/' - || nm_streq (s, priv->config_dir)) - s = ""; - priv->system_config_dir = g_strdup (s); - - if (priv->cli.intern_config_file) - priv->intern_config_file = g_strdup (priv->cli.intern_config_file); - else - priv->intern_config_file = g_strdup (DEFAULT_INTERN_CONFIG_FILE); - - warnings = g_ptr_array_new_with_free_func (g_free); - - keyfile = read_entire_config (&priv->cli, - priv->config_dir, - priv->system_config_dir, - &config_main_file, - &config_description, - warnings, - error); - if (!keyfile) - return FALSE; - - /* 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 (DEFAULT_NO_AUTO_DEFAULT_FILE); - - priv->log_level = nm_strstrip (g_key_file_get_string (keyfile, - NM_CONFIG_KEYFILE_GROUP_LOGGING, - NM_CONFIG_KEYFILE_KEY_LOGGING_LEVEL, - NULL)); - priv->log_domains = nm_strstrip (g_key_file_get_string (keyfile, - NM_CONFIG_KEYFILE_GROUP_LOGGING, - NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS, - NULL)); - configure_and_quit = nm_strstrip (g_key_file_get_string (keyfile, - NM_CONFIG_KEYFILE_GROUP_MAIN, - NM_CONFIG_KEYFILE_KEY_MAIN_CONFIGURE_AND_QUIT, - NULL)); - priv->configure_and_quit = string_to_configure_and_quit (configure_and_quit, error); - if (priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) - return FALSE; - - no_auto_default = no_auto_default_from_file (priv->no_auto_default_file); - - keyfile_intern = intern_config_read (priv->intern_config_file, - keyfile, - (const char *const*) priv->atomic_section_prefixes, - &intern_config_needs_rewrite); - if ( intern_config_needs_rewrite - && priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED) { - intern_config_write (priv->intern_config_file, keyfile_intern, keyfile, - (const char *const*) priv->atomic_section_prefixes, NULL); - } - - priv->config_data_orig = nm_config_data_new (config_main_file, - config_description, - (const char *const*) no_auto_default, - keyfile, - keyfile_intern); - - nm_config_data_get_warnings (priv->config_data_orig, warnings); - - priv->config_data = g_object_ref (priv->config_data_orig); - if (warnings->len > 0) { - g_ptr_array_add (warnings, NULL); - priv->warnings = (char **) g_ptr_array_free (g_steal_pointer (&warnings), FALSE); - } - return TRUE; +init_sync(GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMConfig * self = NM_CONFIG(initable); + NMConfigPrivate * priv = NM_CONFIG_GET_PRIVATE(self); + nm_auto_unref_keyfile GKeyFile *keyfile = NULL; + nm_auto_unref_keyfile GKeyFile *keyfile_intern = NULL; + gs_free char * config_main_file = NULL; + gs_free char * config_description = NULL; + gs_strfreev char ** no_auto_default = NULL; + gs_unref_ptrarray GPtrArray *warnings = NULL; + gs_free char * configure_and_quit = NULL; + gboolean intern_config_needs_rewrite; + const char * s; + + 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"); + g_return_val_if_reached(FALSE); + } + + s = priv->cli.config_dir ?: "" DEFAULT_CONFIG_DIR; + priv->config_dir = g_strdup(s[0] == '/' ? s : ""); + + s = priv->cli.system_config_dir ?: "" DEFAULT_SYSTEM_CONFIG_DIR; + if (s[0] != '/' || nm_streq(s, priv->config_dir)) + s = ""; + priv->system_config_dir = g_strdup(s); + + if (priv->cli.intern_config_file) + priv->intern_config_file = g_strdup(priv->cli.intern_config_file); + else + priv->intern_config_file = g_strdup(DEFAULT_INTERN_CONFIG_FILE); + + warnings = g_ptr_array_new_with_free_func(g_free); + + keyfile = read_entire_config(&priv->cli, + priv->config_dir, + priv->system_config_dir, + &config_main_file, + &config_description, + warnings, + error); + if (!keyfile) + return FALSE; + + /* 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(DEFAULT_NO_AUTO_DEFAULT_FILE); + + priv->log_level = nm_strstrip(g_key_file_get_string(keyfile, + NM_CONFIG_KEYFILE_GROUP_LOGGING, + NM_CONFIG_KEYFILE_KEY_LOGGING_LEVEL, + NULL)); + priv->log_domains = nm_strstrip(g_key_file_get_string(keyfile, + NM_CONFIG_KEYFILE_GROUP_LOGGING, + NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS, + NULL)); + configure_and_quit = + nm_strstrip(g_key_file_get_string(keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_CONFIGURE_AND_QUIT, + NULL)); + priv->configure_and_quit = string_to_configure_and_quit(configure_and_quit, error); + if (priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) + return FALSE; + + no_auto_default = no_auto_default_from_file(priv->no_auto_default_file); + + keyfile_intern = intern_config_read(priv->intern_config_file, + keyfile, + (const char *const *) priv->atomic_section_prefixes, + &intern_config_needs_rewrite); + if (intern_config_needs_rewrite + && priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED) { + intern_config_write(priv->intern_config_file, + keyfile_intern, + keyfile, + (const char *const *) priv->atomic_section_prefixes, + NULL); + } + + priv->config_data_orig = nm_config_data_new(config_main_file, + config_description, + (const char *const *) no_auto_default, + keyfile, + keyfile_intern); + + nm_config_data_get_warnings(priv->config_data_orig, warnings); + + priv->config_data = g_object_ref(priv->config_data_orig); + if (warnings->len > 0) { + g_ptr_array_add(warnings, NULL); + priv->warnings = (char **) g_ptr_array_free(g_steal_pointer(&warnings), FALSE); + } + return TRUE; } /*****************************************************************************/ static void -nm_config_init (NMConfig *config) -{ -} +nm_config_init(NMConfig *config) +{} NMConfig * -nm_config_new (const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error) +nm_config_new(const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error) { - return NM_CONFIG (g_initable_new (NM_TYPE_CONFIG, - NULL, - error, - NM_CONFIG_CMD_LINE_OPTIONS, cli, - NM_CONFIG_ATOMIC_SECTION_PREFIXES, atomic_section_prefixes, - NULL)); + return NM_CONFIG(g_initable_new(NM_TYPE_CONFIG, + NULL, + error, + NM_CONFIG_CMD_LINE_OPTIONS, + cli, + NM_CONFIG_ATOMIC_SECTION_PREFIXES, + atomic_section_prefixes, + NULL)); } static void -finalize (GObject *gobject) +finalize(GObject *gobject) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject); + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE(gobject); - state_free (priv->state); + state_free(priv->state); - g_free (priv->config_dir); - g_free (priv->system_config_dir); - g_free (priv->no_auto_default_file); - g_free (priv->intern_config_file); - g_free (priv->log_level); - g_free (priv->log_domains); - g_strfreev (priv->atomic_section_prefixes); - g_strfreev (priv->warnings); + g_free(priv->config_dir); + g_free(priv->system_config_dir); + g_free(priv->no_auto_default_file); + g_free(priv->intern_config_file); + g_free(priv->log_level); + g_free(priv->log_domains); + g_strfreev(priv->atomic_section_prefixes); + g_strfreev(priv->warnings); - _nm_config_cmd_line_options_clear (&priv->cli); + _nm_config_cmd_line_options_clear(&priv->cli); - g_clear_object (&priv->config_data); - g_clear_object (&priv->config_data_orig); + g_clear_object(&priv->config_data); + g_clear_object(&priv->config_data_orig); - G_OBJECT_CLASS (nm_config_parent_class)->finalize (gobject); + G_OBJECT_CLASS(nm_config_parent_class)->finalize(gobject); } static void -nm_config_class_init (NMConfigClass *config_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (config_class); - - object_class->finalize = finalize; - object_class->set_property = set_property; - - obj_properties[PROP_CMD_LINE_OPTIONS] = - g_param_spec_pointer (NM_CONFIG_CMD_LINE_OPTIONS, "", "", - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ATOMIC_SECTION_PREFIXES] = - g_param_spec_boxed (NM_CONFIG_ATOMIC_SECTION_PREFIXES, "", "", - G_TYPE_STRV, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - signals[SIGNAL_CONFIG_CHANGED] = - g_signal_new (NM_CONFIG_SIGNAL_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, - 3, - NM_TYPE_CONFIG_DATA, - /* Use plain guint type for changes argument. This avoids - * glib/ffi bug https://bugzilla.redhat.com/show_bug.cgi?id=1260577 */ - /* NM_TYPE_CONFIG_CHANGE_FLAGS, */ - G_TYPE_UINT, - NM_TYPE_CONFIG_DATA); - - G_STATIC_ASSERT_EXPR (sizeof (guint) == sizeof (NMConfigChangeFlags)); - G_STATIC_ASSERT_EXPR (((gint64) ((NMConfigChangeFlags) -1)) > ((gint64) 0)); +nm_config_class_init(NMConfigClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(config_class); + + object_class->finalize = finalize; + object_class->set_property = set_property; + + obj_properties[PROP_CMD_LINE_OPTIONS] = + g_param_spec_pointer(NM_CONFIG_CMD_LINE_OPTIONS, + "", + "", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_ATOMIC_SECTION_PREFIXES] = + g_param_spec_boxed(NM_CONFIG_ATOMIC_SECTION_PREFIXES, + "", + "", + G_TYPE_STRV, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + signals[SIGNAL_CONFIG_CHANGED] = + g_signal_new(NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 3, + NM_TYPE_CONFIG_DATA, + /* Use plain guint type for changes argument. This avoids + * glib/ffi bug https://bugzilla.redhat.com/show_bug.cgi?id=1260577 */ + /* NM_TYPE_CONFIG_CHANGE_FLAGS, */ + G_TYPE_UINT, + NM_TYPE_CONFIG_DATA); + + G_STATIC_ASSERT_EXPR(sizeof(guint) == sizeof(NMConfigChangeFlags)); + G_STATIC_ASSERT_EXPR(((gint64)((NMConfigChangeFlags) -1)) > ((gint64) 0)); } static void -nm_config_initable_iface_init (GInitableIface *iface) +nm_config_initable_iface_init(GInitableIface *iface) { - iface->init = init_sync; + iface->init = init_sync; } |