summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2015-07-02 16:02:36 +0200
committerThomas Haller <thaller@redhat.com>2015-07-02 16:04:31 +0200
commit05db3ee08a30edc503e2b9033993ee3d2cbf019f (patch)
treeed9e54026c180f097ac8e61c495d2705d4c5847c
parent65753dbc133f77ccd5531288aab9a9e80da3b786 (diff)
parent25b23f931ec0d26b3f68720e8d9ab299d4c43cdb (diff)
config: merge branch 'th/nm-config-intern-bgo750558'
Add write support to NMConfig. Also, add a new configuration directory /usr/lib/NetworkManager/conf.d/ which allows us to install configuration snippets under /usr, instead of /etc. https://bugzilla.gnome.org/show_bug.cgi?id=738853 https://bugzilla.gnome.org/show_bug.cgi?id=750558
-rw-r--r--configure.ac7
-rw-r--r--contrib/fedora/rpm/NetworkManager.conf20
-rw-r--r--contrib/fedora/rpm/NetworkManager.spec25
-rwxr-xr-xcontrib/fedora/rpm/build.sh3
-rw-r--r--man/NetworkManager.conf.xml.in37
-rw-r--r--src/Makefile.am1
-rw-r--r--src/devices/nm-device.c2
-rw-r--r--src/main.c2
-rw-r--r--src/nm-config-data.c232
-rw-r--r--src/nm-config-data.h25
-rw-r--r--src/nm-config.c799
-rw-r--r--src/nm-config.h17
-rw-r--r--src/settings/nm-settings.c141
-rw-r--r--src/tests/config/test-config.c376
14 files changed, 1501 insertions, 186 deletions
diff --git a/configure.ac b/configure.ac
index e1a1917db6..e9b39d01f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -78,6 +78,7 @@ AC_SUBST(runstatedir)
# NetworkManager paths
AC_SUBST(nmbinary, "$sbindir/$PACKAGE", [NetworkManager binary executable])
AC_SUBST(nmconfdir, "$sysconfdir/$PACKAGE", [NetworkManager configuration directory])
+AC_SUBST(nmlibdir, "$libdir/$PACKAGE", [NetworkManager library directory])
AC_SUBST(nmdatadir, "$datadir/$PACKAGE", [NetworkManager shared data directory])
AC_SUBST(nmstatedir, "$localstatedir/lib/$PACKAGE", [NetworkManager persistent state directory])
AC_SUBST(nmrundir, "$runstatedir/$PACKAGE", [NetworkManager runtime state directory])
@@ -125,6 +126,11 @@ test "$enable_ifnet" = "yes" && distro_plugins="$distro_plugins,ifn
distro_plugins="${distro_plugins#,}"
AC_DEFINE_UNQUOTED(CONFIG_PLUGINS_DEFAULT, "$config_plugins_default", [Default configuration option for main.plugins setting])
+if test "${enable_config_plugin_ibft}" = yes; then
+ AC_DEFINE(WITH_SETTINGS_PLUGIN_IBFT, 1, [Whether compilation of ibft setting plugin is enabled])
+else
+ AC_DEFINE(WITH_SETTINGS_PLUGIN_IBFT, 0, [Whether compilation of ibft setting plugin is enabled])
+fi
if test "$enable_ifcfg_rh" = "yes"; then
DISTRO_NETWORK_SERVICE=network.service
@@ -1065,6 +1071,7 @@ echo " exec_prefix: $exec_prefix"
echo " systemdunitdir: $with_systemdsystemunitdir"
echo " nmbinary: $nmbinary"
echo " nmconfdir: $nmconfdir"
+echo " nmlibdir: $nmlibdir"
echo " nmdatadir: $nmdatadir"
echo " nmstatedir: $nmstatedir"
echo " nmrundir: $nmrundir"
diff --git a/contrib/fedora/rpm/NetworkManager.conf b/contrib/fedora/rpm/NetworkManager.conf
index 900ca9b23b..0352aa1087 100644
--- a/contrib/fedora/rpm/NetworkManager.conf
+++ b/contrib/fedora/rpm/NetworkManager.conf
@@ -2,14 +2,24 @@
#
# See "man 5 NetworkManager.conf" for details.
#
+# The directory /usr/lib/NetworkManager/conf.d/ can contain additional configuration
+# snippets installed by packages. These files are read before NetworkManager.conf
+# and have thus lowest priority.
# The directory /etc/NetworkManager/conf.d/ can contain additional configuration
-# snippets that are installed by some packages. Those snippets override the
-# settings from this main file.
-# To override a configuration from a conf.d/ snippet, add another configuration
-# with a name sorted lastly (such as 99-my.conf).
+# snippets. Those snippets override the settings from this main file.
+#
+# The files within one conf.d/ directory are read in asciibetical order.
+#
+# If /etc/NetworkManager/conf.d/ contains a file with the same name as
+# /usr/lib/NetworkManager/conf.d/, the latter file is shadowed and thus ignored.
+# Hence, to disable loading a file from /usr/lib/NetworkManager/conf.d/ you can
+# put an empty file with the same name.
+#
+# If two files define the same key, the one that is read afterwards will overwrite
+# the previous one.
[main]
-plugins=ifcfg-rh,ibft
+#plugins=ifcfg-rh,ibft
[logging]
#level=DEBUG
diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec
index 0bdabe8dec..0786b305a8 100644
--- a/contrib/fedora/rpm/NetworkManager.spec
+++ b/contrib/fedora/rpm/NetworkManager.spec
@@ -34,6 +34,7 @@
%define systemd_dir %{_prefix}/lib/systemd/system
%define udev_dir %{_prefix}/lib/udev
+%define nmlibdir %{_prefix}/lib/%{name}
%global with_adsl 1
%global with_bluetooth 1
@@ -83,8 +84,7 @@ URL: http://www.gnome.org/projects/NetworkManager/
Source: __SOURCE1__
Source1: NetworkManager.conf
Source2: 00-server.conf
-Source3: 10-ibft-plugin.conf
-Source4: 20-connectivity-fedora.conf
+Source3: 20-connectivity-fedora.conf
#Patch1: 0001-some.patch
@@ -443,9 +443,9 @@ make install DESTDIR=$RPM_BUILD_ROOT
%{__cp} %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/conf.d
-%{__cp} %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/conf.d
-%{__cp} %{SOURCE3} $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/conf.d
-%{__cp} %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/conf.d
+mkdir -p $RPM_BUILD_ROOT%{nmlibdir}/conf.d
+%{__cp} %{SOURCE2} $RPM_BUILD_ROOT%{nmlibdir}/conf.d/
+%{__cp} %{SOURCE3} $RPM_BUILD_ROOT%{nmlibdir}/conf.d/
# create a VPN directory
%{__mkdir_p} $RPM_BUILD_ROOT%{_sysconfdir}/NetworkManager/VPN
@@ -543,7 +543,8 @@ fi
%endif
%dir %{_sysconfdir}/%{name}
%dir %{_sysconfdir}/%{name}/conf.d
-%config %{_sysconfdir}/%{name}/conf.d/10-ibft-plugin.conf
+%dir %{nmlibdir}
+%dir %{nmlibdir}/conf.d
%{_mandir}/man1/*
%{_mandir}/man5/*
%{_mandir}/man8/*
@@ -660,15 +661,15 @@ fi
%files config-connectivity-fedora
%defattr(-,root,root,0755)
-%dir %{_sysconfdir}/%{name}
-%dir %{_sysconfdir}/%{name}/conf.d
-%config(noreplace) %{_sysconfdir}/%{name}/conf.d/20-connectivity-fedora.conf
+%dir %{nmlibdir}
+%dir %{nmlibdir}/conf.d
+%{nmlibdir}/conf.d/20-connectivity-fedora.conf
%files config-server
%defattr(-,root,root,0755)
-%dir %{_sysconfdir}/%{name}
-%dir %{_sysconfdir}/%{name}/conf.d
-%config(noreplace) %{_sysconfdir}/%{name}/conf.d/00-server.conf
+%dir %{nmlibdir}
+%dir %{nmlibdir}/conf.d
+%{nmlibdir}/conf.d/00-server.conf
%if 0%{?with_nmtui}
%files tui
diff --git a/contrib/fedora/rpm/build.sh b/contrib/fedora/rpm/build.sh
index c1ededb554..8e6539dcb1 100755
--- a/contrib/fedora/rpm/build.sh
+++ b/contrib/fedora/rpm/build.sh
@@ -73,7 +73,6 @@ SOURCE="$(abs_path "$SOURCE" "$(ls -1 "$GITDIR/NetworkManager-$VERSION"*.tar* 2>
[[ -f "$SOURCE" ]] || die "could not find source ${_SOURCE:-$GITDIR/NetworkManager-$VERSION*.tar*} . Did you execute \`make dist\`? Otherwise set \$SOURCE variable"
SOURCE_NETWORKMANAGER_CONF="$(abs_path "$SOURCE_NETWORKMANAGER_CONF" "$SCRIPTDIR/NetworkManager.conf")"
SOURCE_CONFIG_SERVER="$(abs_path "$SOURCE_CONFIG_SERVER" "$SCRIPTDIR/00-server.conf")"
-SOURCE_CONFIG_IBFT_PLUGIN="$(abs_path "$SOURCE_CONFIG_IBFT_PLUGIN" "$SCRIPTDIR/10-ibft-plugin.conf")"
SOURCE_CONFIG_CONNECTIVITY_FEDORA="$(abs_path "$SOURCE_CONFIG_CONNECTIVITY_FEDORA" "$SCRIPTDIR/20-connectivity-fedora.conf")"
TEMP="$(mktemp -d "$SCRIPTDIR/NetworkManager.$DATE.XXXXXX")"
@@ -88,7 +87,6 @@ LOG "SPECFILE=$SPECFILE"
LOG "SOURCE=$SOURCE"
LOG "SOURCE_NETWORKMANAGER_CONF=$SOURCE_NETWORKMANAGER_CONF"
LOG "SOURCE_CONFIG_SERVER=$SOURCE_CONFIG_SERVER"
-LOG "SOURCE_CONFIG_IBFT_PLUGIN=$SOURCE_CONFIG_IBFT_PLUGIN"
LOG "SOURCE_CONFIG_CONNECTIVITY_FEDORA=$SOURCE_CONFIG_CONNECTIVITY_FEDORA"
LOG "BASEDIR=$TEMP"
@@ -102,7 +100,6 @@ mkdir -p "$TEMP/SOURCES/" "$TEMP/SPECS/" || die "error creating SPECS directoy"
cp "$SOURCE" "$TEMP/SOURCES/" || die "Could not copy source $SOURCE to $TEMP/SOURCES"
cp "$SOURCE_NETWORKMANAGER_CONF" "$TEMP/SOURCES/NetworkManager.conf" || die "Could not copy source $SOURCE_NETWORKMANAGER_CONF to $TEMP/SOURCES"
cp "$SOURCE_CONFIG_SERVER" "$TEMP/SOURCES/00-server.conf" || die "Could not copy source $SOURCE_CONFIG_SERVER to $TEMP/SOURCES"
-cp "$SOURCE_CONFIG_IBFT_PLUGIN" "$TEMP/SOURCES/10-ibft-plugin.conf" || die "Could not copy source $SOURCE_CONFIG_IBFT_PLUGIN to $TEMP/SOURCES"
cp "$SOURCE_CONFIG_CONNECTIVITY_FEDORA" "$TEMP/SOURCES/20-connectivity-fedora.conf" || die "Could not copy source $SOURCE_CONFIG_CONNECTIVITY_FEDORA to $TEMP/SOURCES"
write_changelog
diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in
index e2501c8464..bf15c9e8b5 100644
--- a/man/NetworkManager.conf.xml.in
+++ b/man/NetworkManager.conf.xml.in
@@ -27,23 +27,39 @@ Copyright 2010 - 2014 Red Hat, Inc.
<refsynopsisdiv>
<para><filename>/etc/NetworkManager/NetworkManager.conf</filename>,
- <filename>/etc/NetworkManager/conf.d/<replaceable>name</replaceable>.conf</filename>
+ <filename>/etc/NetworkManager/conf.d/<replaceable>name</replaceable>.conf</filename>,
+ <filename>/usr/lib/NetworkManager/conf.d/<replaceable>name</replaceable>.conf</filename>,
+ <filename>/var/lib/NetworkManager/NetworkManager-intern.conf</filename>
</para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para>This is a configuration file for NetworkManager. It is used
+ <para><literal>NetworkManager.conf</literal> is the configuration file for NetworkManager. It is used
to set up various aspects of NetworkManager's behavior. The
- location of the file may be changed through use of the
- <option>--config</option> argument for NetworkManager.
+ location of the main file and configuration directories may be changed
+ through use of the <option>--config</option>, <option>--config-dir</option>,
+ <option>--system-config-dir</option>, and <option>--intern-config</option>
+ argument for NetworkManager, respectively.
</para>
<para>If a default <literal>NetworkManager.conf</literal> is
provided by your distribution's packages, you should not modify
it, since your changes may get overwritten by package
updates. Instead, you can add additional <literal>.conf</literal>
- files to the <literal>conf.d</literal> directory. These will be read in order,
- with later files overriding earlier ones.
+ files to the <literal>/etc/NetworkManager/conf.d</literal> directory.
+ These will be read in order, with later files overriding earlier ones.
+ Packages might install further configuration snippets to <literal>/usr/lib/NetworkManager/conf.d</literal>.
+ This directory is parsed first, even before <literal>NetworkManager.conf</literal>.
+ The loading of a file <literal>/usr/lib/NetworkManager/conf.d/<replaceable>name</replaceable>.conf</literal>
+ can be prevented by adding a file <literal>/etc/NetworkManager/conf.d/<replaceable>name</replaceable>.conf</literal>.
+ In this case, the file from the etc configuration shadows the file from the
+ system configuration directory.
+ </para>
+ <para>
+ NetworkManager can overwrite certain user configuration options via D-Bus or other internal
+ operations. In this case it writes those changes to <literal>/var/lib/NetworkManager/NetworkManager-intern.conf</literal>.
+ This file is not intended to be modified by the user, but it is read last and can shadow
+ user configuration from <literal>NetworkManager.conf</literal>.
</para>
</refsect1>
@@ -625,6 +641,9 @@ ipv6.ip6-privacy=1
<filename>/etc/sysconfig/network-scripts/ifcfg-*</filename>
files. It currently supports reading Ethernet, Wi-Fi,
InfiniBand, VLAN, Bond, Bridge, and Team connections.
+ Enabling <literal>ifcfg-rh</literal> implicitly enables
+ <literal>ibft</literal> plugin, if it is available.
+ This can be disabled by adding <literal>no-ibft</literal>.
</para>
</listitem>
</varlistentry>
@@ -658,12 +677,16 @@ ipv6.ip6-privacy=1
</varlistentry>
<varlistentry>
- <term><varname>ibft</varname></term>
+ <term><varname>ibft</varname>, <varname>no-ibft</varname></term>
<listitem>
<para>
This plugin allows to read iBFT configuration (iSCSI Boot Firmware Table).
The configuration is read using /sbin/iscsiadm. Users are expected to
configure iBFT connections via the firmware interfaces.
+ If ibft support is available, it is automatically enabled after
+ <literal>ifcfg-rh</literal>. This can be disabled by <literal>no-ibft</literal>.
+ You can also explicitly specify <literal>ibft</literal> to load the
+ plugin without <literal>ifcfg-rh</literal> or to change the plugin order.
</para>
</listitem>
</varlistentry>
diff --git a/src/Makefile.am b/src/Makefile.am
index 3aa8b5f4f9..e994b19cad 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -428,6 +428,7 @@ AM_CPPFLAGS += \
-DNMPLUGINDIR=\"$(pkglibdir)\" \
-DNMRUNDIR=\"$(nmrundir)\" \
-DNMSTATEDIR=\"$(nmstatedir)\" \
+ -DNMLIBDIR=\"$(nmlibdir)\" \
\
-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
-DDHCPCD_PATH=\"$(DHCPCD_PATH)\" \
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index dcdd29e440..16d4c48c69 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -3323,7 +3323,7 @@ ip4_config_merge_and_apply (NMDevice *self,
if ( !priv->default_route.v4_configure_first_time
&& !nm_device_uses_assumed_connection (self)
&& connection_is_never_default) {
- /* If the connection is explicitly configured as never-default, we enforce the (absense of the)
+ /* If the connection is explicitly configured as never-default, we enforce the (absence of the)
* default-route only once. That allows the user to configure a connection as never-default,
* but he can add default routes externally (via a dispatcher script) and NM will not interfere. */
goto END_ADD_DEFAULT_ROUTE;
diff --git a/src/main.c b/src/main.c
index dc6973446f..4f906f5574 100644
--- a/src/main.c
+++ b/src/main.c
@@ -343,7 +343,7 @@ main (int argc, char *argv[])
}
/* Read the config file and CLI overrides */
- config = nm_config_setup (config_cli, &error);
+ config = nm_config_setup (config_cli, NULL, &error);
nm_config_cmd_line_options_free (config_cli);
config_cli = NULL;
if (config == NULL) {
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index 82c1deab11..03cae638c7 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -48,6 +48,8 @@ typedef struct {
char *config_description;
GKeyFile *keyfile;
+ GKeyFile *keyfile_user;
+ GKeyFile *keyfile_intern;
/* A zero-terminated list of pre-processed information from the
* [connection] sections. This is to speed up lookup. */
@@ -77,7 +79,8 @@ enum {
PROP_0,
PROP_CONFIG_MAIN_FILE,
PROP_CONFIG_DESCRIPTION,
- PROP_KEYFILE,
+ PROP_KEYFILE_USER,
+ PROP_KEYFILE_INTERN,
PROP_CONNECTIVITY_URI,
PROP_CONNECTIVITY_INTERVAL,
PROP_CONNECTIVITY_RESPONSE,
@@ -92,6 +95,14 @@ G_DEFINE_TYPE (NMConfigData, nm_config_data, G_TYPE_OBJECT)
/************************************************************************/
+#define _HAS_PREFIX(str, prefix) \
+ ({ \
+ const char *_str = (str); \
+ g_str_has_prefix ( _str, ""prefix"") && _str[STRLEN(prefix)] != '\0'; \
+ })
+
+/************************************************************************/
+
const char *
nm_config_data_get_config_main_file (const NMConfigData *self)
{
@@ -238,6 +249,40 @@ nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *devic
return nm_device_spec_match_list (device, NM_CONFIG_DATA_GET_PRIVATE (self)->assume_ipv6ll_only);
}
+GKeyFile *
+nm_config_data_clone_keyfile_intern (const NMConfigData *self)
+{
+ NMConfigDataPrivate *priv;
+ GKeyFile *keyfile;
+
+ g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE);
+
+ priv = NM_CONFIG_DATA_GET_PRIVATE (self);
+
+ keyfile = nm_config_create_keyfile ();
+ if (priv->keyfile_intern)
+ _nm_keyfile_copy (keyfile, priv->keyfile_intern);
+ return keyfile;
+}
+
+GKeyFile *
+_nm_config_data_get_keyfile (const NMConfigData *self)
+{
+ return NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile;
+}
+
+GKeyFile *
+_nm_config_data_get_keyfile_intern (const NMConfigData *self)
+{
+ return NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile_intern;
+}
+
+GKeyFile *
+_nm_config_data_get_keyfile_user (const NMConfigData *self)
+{
+ return NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile_user;
+}
+
/************************************************************************/
/**
@@ -265,15 +310,128 @@ nm_config_data_get_keys (const NMConfigData *self, const char *group)
return g_key_file_get_keys (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, NULL, NULL);
}
+/**
+ * nm_config_data_is_intern_atomic_group:
+ * @self:
+ * @group: name of the group to check.
+ *
+ * whether a configuration group @group exists and is entirely overwritten
+ * by internal configuration, i.e. whether it is an atomic group that is
+ * overwritten.
+ *
+ * It doesn't say, that there actually is a user setting that was overwritten. That
+ * means there could be no corresponding section defined in user configuration
+ * that required overwriting.
+ *
+ * Returns: %TRUE if @group exists and is an atomic group set via internal configuration.
+ */
+gboolean
+nm_config_data_is_intern_atomic_group (const NMConfigData *self, const char *group)
+{
+ NMConfigDataPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE);
+ g_return_val_if_fail (group && *group, FALSE);
+
+ priv = NM_CONFIG_DATA_GET_PRIVATE (self);
+
+ if ( !priv->keyfile_intern
+ || !g_key_file_has_key (priv->keyfile_intern, group, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, NULL))
+ return FALSE;
+
+ /* we have a .was entry for the section. That means that the section would be overwritten
+ * from user configuration. But it doesn't mean that the merged configuration contains this
+ * groups, because the internal setting could hide the user section.
+ * Only return TRUE, if we actually have such a group in the merged configuration.*/
+ return g_key_file_has_group (priv->keyfile, group);
+}
+
+/************************************************************************/
+
+static GKeyFile *
+_merge_keyfiles (GKeyFile *keyfile_user, GKeyFile *keyfile_intern)
+{
+ gs_strfreev char **groups = NULL;
+ guint g, k;
+ GKeyFile *keyfile;
+ gsize ngroups;
+
+ keyfile = nm_config_create_keyfile ();
+ if (keyfile_user)
+ _nm_keyfile_copy (keyfile, keyfile_user);
+ if (!keyfile_intern)
+ return keyfile;
+
+ groups = g_key_file_get_groups (keyfile_intern, &ngroups);
+ if (!groups)
+ return keyfile;
+
+ /* we must reverse the order of the connection settings so that we
+ * have lowest priority last. */
+ _nm_config_sort_groups (groups, ngroups);
+ for (g = 0; groups[g]; g++) {
+ const char *group = groups[g];
+ gs_strfreev char **keys = NULL;
+ gboolean is_intern, is_atomic = FALSE;
+
+ keys = g_key_file_get_keys (keyfile_intern, group, NULL, NULL);
+ if (!keys)
+ continue;
+
+ is_intern = g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN);
+ if ( !is_intern
+ && g_key_file_has_key (keyfile_intern, group, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, NULL)) {
+ /* the entire section is atomically overwritten by @keyfile_intern. */
+ g_key_file_remove_group (keyfile, group, NULL);
+ is_atomic = TRUE;
+ }
+
+ for (k = 0; keys[k]; k++) {
+ const char *key = keys[k];
+ gs_free char *value = NULL;
+
+ if (is_atomic && strcmp (key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS) == 0)
+ continue;
+
+ if ( !is_intern && !is_atomic
+ && _HAS_PREFIX (key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) {
+ const char *key_base = &key[STRLEN (NM_CONFIG_KEYFILE_KEYPREFIX_WAS)];
+
+ if (!g_key_file_has_key (keyfile_intern, group, key_base, NULL))
+ g_key_file_remove_key (keyfile, group, key_base, NULL);
+ continue;
+ }
+ if (!is_intern && !is_atomic && _HAS_PREFIX (key, NM_CONFIG_KEYFILE_KEYPREFIX_SET))
+ continue;
+
+ value = g_key_file_get_value (keyfile_intern, group, key, NULL);
+ g_key_file_set_value (keyfile, group, key, value);
+ }
+ }
+ return keyfile;
+}
+
/************************************************************************/
static int
_nm_config_data_log_sort (const char **pa, const char **pb, gpointer dummy)
{
gboolean a_is_connection, b_is_connection;
+ gboolean a_is_intern, b_is_intern;
const char *a = *pa;
const char *b = *pb;
+ /* we sort intern groups to the end. */
+ a_is_intern = g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN);
+ b_is_intern = g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN);
+
+ if (a_is_intern && b_is_intern)
+ return 0;
+ if (a_is_intern)
+ return 1;
+ if (b_is_intern)
+ return -1;
+
/* we sort connection groups before intern groups (to the end). */
a_is_connection = a && g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION);
b_is_connection = b && g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION);
@@ -336,9 +494,12 @@ nm_config_data_log (const NMConfigData *self, const char *prefix)
for (g = 0; g < ngroups; g++) {
const char *group = groups[g];
gs_strfreev char **keys = NULL;
+ gboolean is_atomic;
+
+ is_atomic = nm_config_data_is_intern_atomic_group (self, group);
_LOG ("");
- _LOG ("[%s]", group);
+ _LOG ("[%s]%s", group, is_atomic ? "*" : "");
keys = g_key_file_get_keys (priv->keyfile, group, NULL, NULL);
for (k = 0; keys && keys[k]; k++) {
@@ -479,8 +640,11 @@ nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data)
priv_old = NM_CONFIG_DATA_GET_PRIVATE (old_data);
priv_new = NM_CONFIG_DATA_GET_PRIVATE (new_data);
- if (!_nm_keyfile_equals (priv_old->keyfile, priv_new->keyfile, TRUE))
- changes |= NM_CONFIG_CHANGE_VALUES;
+ if (!_nm_keyfile_equals (priv_old->keyfile_user, priv_new->keyfile_user, TRUE))
+ changes |= NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER;
+
+ if (!_nm_keyfile_equals (priv_old->keyfile_intern, priv_new->keyfile_intern, TRUE))
+ changes |= NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN;
if ( g_strcmp0 (nm_config_data_get_config_main_file (old_data), nm_config_data_get_config_main_file (new_data)) != 0
|| g_strcmp0 (nm_config_data_get_config_description (old_data), nm_config_data_get_config_description (new_data)) != 0)
@@ -553,10 +717,21 @@ set_property (GObject *object,
case PROP_CONFIG_DESCRIPTION:
priv->config_description = g_value_dup_string (value);
break;
- case PROP_KEYFILE:
- priv->keyfile = g_value_dup_boxed (value);
- if (!priv->keyfile)
- priv->keyfile = nm_config_create_keyfile ();
+ case PROP_KEYFILE_USER:
+ priv->keyfile_user = g_value_dup_boxed (value);
+ if ( priv->keyfile_user
+ && !_nm_keyfile_has_values (priv->keyfile_user)) {
+ g_key_file_unref (priv->keyfile_user);
+ priv->keyfile_user = NULL;
+ }
+ break;
+ case PROP_KEYFILE_INTERN:
+ priv->keyfile_intern = g_value_dup_boxed (value);
+ if ( priv->keyfile_intern
+ && !_nm_keyfile_has_values (priv->keyfile_intern)) {
+ g_key_file_unref (priv->keyfile_intern);
+ priv->keyfile_intern = NULL;
+ }
break;
case PROP_NO_AUTO_DEFAULT:
{
@@ -620,6 +795,10 @@ finalize (GObject *gobject)
}
g_key_file_unref (priv->keyfile);
+ if (priv->keyfile_user)
+ g_key_file_unref (priv->keyfile_user);
+ if (priv->keyfile_intern)
+ g_key_file_unref (priv->keyfile_intern);
G_OBJECT_CLASS (nm_config_data_parent_class)->finalize (gobject);
}
@@ -636,6 +815,8 @@ constructed (GObject *object)
NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (self);
char *interval;
+ priv->keyfile = _merge_keyfiles (priv->keyfile_user, priv->keyfile_intern);
+
priv->connection_infos = _get_connection_infos (priv->keyfile);
priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", NULL));
@@ -664,17 +845,33 @@ NMConfigData *
nm_config_data_new (const char *config_main_file,
const char *config_description,
const char *const*no_auto_default,
- GKeyFile *keyfile)
+ GKeyFile *keyfile_user,
+ GKeyFile *keyfile_intern)
{
return g_object_new (NM_TYPE_CONFIG_DATA,
NM_CONFIG_DATA_CONFIG_MAIN_FILE, config_main_file,
NM_CONFIG_DATA_CONFIG_DESCRIPTION, config_description,
- NM_CONFIG_DATA_KEYFILE, keyfile,
+ NM_CONFIG_DATA_KEYFILE_USER, keyfile_user,
+ NM_CONFIG_DATA_KEYFILE_INTERN, keyfile_intern,
NM_CONFIG_DATA_NO_AUTO_DEFAULT, no_auto_default,
NULL);
}
NMConfigData *
+nm_config_data_new_update_keyfile_intern (const NMConfigData *base, GKeyFile *keyfile_intern)
+{
+ NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (base);
+
+ return g_object_new (NM_TYPE_CONFIG_DATA,
+ NM_CONFIG_DATA_CONFIG_MAIN_FILE, priv->config_main_file,
+ NM_CONFIG_DATA_CONFIG_DESCRIPTION, priv->config_description,
+ NM_CONFIG_DATA_KEYFILE_USER, priv->keyfile_user, /* the keyfile is unchanged. It's safe to share it. */
+ NM_CONFIG_DATA_KEYFILE_INTERN, keyfile_intern,
+ NM_CONFIG_DATA_NO_AUTO_DEFAULT, priv->no_auto_default.arr,
+ NULL);
+}
+
+NMConfigData *
nm_config_data_new_update_no_auto_default (const NMConfigData *base,
const char *const*no_auto_default)
{
@@ -683,7 +880,8 @@ nm_config_data_new_update_no_auto_default (const NMConfigData *base,
return g_object_new (NM_TYPE_CONFIG_DATA,
NM_CONFIG_DATA_CONFIG_MAIN_FILE, priv->config_main_file,
NM_CONFIG_DATA_CONFIG_DESCRIPTION, priv->config_description,
- NM_CONFIG_DATA_KEYFILE, priv->keyfile, /* the keyfile is unchanged. It's safe to share it. */
+ NM_CONFIG_DATA_KEYFILE_USER, priv->keyfile_user, /* the keyfile is unchanged. It's safe to share it. */
+ NM_CONFIG_DATA_KEYFILE_INTERN, priv->keyfile_intern,
NM_CONFIG_DATA_NO_AUTO_DEFAULT, no_auto_default,
NULL);
}
@@ -718,8 +916,16 @@ nm_config_data_class_init (NMConfigDataClass *config_class)
G_PARAM_STATIC_STRINGS));
g_object_class_install_property
- (object_class, PROP_KEYFILE,
- g_param_spec_boxed (NM_CONFIG_DATA_KEYFILE, "", "",
+ (object_class, PROP_KEYFILE_USER,
+ g_param_spec_boxed (NM_CONFIG_DATA_KEYFILE_USER, "", "",
+ G_TYPE_KEY_FILE,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property
+ (object_class, PROP_KEYFILE_INTERN,
+ g_param_spec_boxed (NM_CONFIG_DATA_KEYFILE_INTERN, "", "",
G_TYPE_KEY_FILE,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
diff --git a/src/nm-config-data.h b/src/nm-config-data.h
index bd26b9dfbb..0dfdf531ec 100644
--- a/src/nm-config-data.h
+++ b/src/nm-config-data.h
@@ -38,7 +38,8 @@ G_BEGIN_DECLS
#define NM_CONFIG_DATA_CONFIG_MAIN_FILE "config-main-file"
#define NM_CONFIG_DATA_CONFIG_DESCRIPTION "config-description"
-#define NM_CONFIG_DATA_KEYFILE "keyfile"
+#define NM_CONFIG_DATA_KEYFILE_USER "keyfile-user"
+#define NM_CONFIG_DATA_KEYFILE_INTERN "keyfile-intern"
#define NM_CONFIG_DATA_CONNECTIVITY_URI "connectivity-uri"
#define NM_CONFIG_DATA_CONNECTIVITY_INTERVAL "connectivity-interval"
#define NM_CONFIG_DATA_CONNECTIVITY_RESPONSE "connectivity-response"
@@ -71,10 +72,12 @@ typedef enum { /*< flags >*/
NM_CONFIG_CHANGE_CONFIG_FILES = (1L << 3),
NM_CONFIG_CHANGE_VALUES = (1L << 4),
- NM_CONFIG_CHANGE_CONNECTIVITY = (1L << 5),
- NM_CONFIG_CHANGE_NO_AUTO_DEFAULT = (1L << 6),
- NM_CONFIG_CHANGE_DNS_MODE = (1L << 7),
- NM_CONFIG_CHANGE_RC_MANAGER = (1L << 8),
+ NM_CONFIG_CHANGE_VALUES_USER = (1L << 5),
+ NM_CONFIG_CHANGE_VALUES_INTERN = (1L << 6),
+ NM_CONFIG_CHANGE_CONNECTIVITY = (1L << 7),
+ NM_CONFIG_CHANGE_NO_AUTO_DEFAULT = (1L << 8),
+ NM_CONFIG_CHANGE_DNS_MODE = (1L << 9),
+ NM_CONFIG_CHANGE_RC_MANAGER = (1L << 10),
_NM_CONFIG_CHANGE_LAST,
NM_CONFIG_CHANGE_ALL = ((_NM_CONFIG_CHANGE_LAST - 1) << 1) - 1,
@@ -93,7 +96,9 @@ GType nm_config_data_get_type (void);
NMConfigData *nm_config_data_new (const char *config_main_file,
const char *config_description,
const char *const*no_auto_default,
- GKeyFile *keyfile);
+ GKeyFile *keyfile_user,
+ GKeyFile *keyfile_intern);
+NMConfigData *nm_config_data_new_update_keyfile_intern (const NMConfigData *base, GKeyFile *keyfile_intern);
NMConfigData *nm_config_data_new_update_no_auto_default (const NMConfigData *base, const char *const*no_auto_default);
NMConfigChangeFlags nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data);
@@ -127,6 +132,14 @@ char *nm_config_data_get_connection_default (const NMConfigData *self,
char **nm_config_data_get_groups (const NMConfigData *self);
char **nm_config_data_get_keys (const NMConfigData *self, const char *group);
+gboolean nm_config_data_is_intern_atomic_group (const NMConfigData *self, const char *group);
+
+GKeyFile *nm_config_data_clone_keyfile_intern (const NMConfigData *self);
+
+/* private accessors */
+GKeyFile *_nm_config_data_get_keyfile (const NMConfigData *self);
+GKeyFile *_nm_config_data_get_keyfile_user (const NMConfigData *self);
+GKeyFile *_nm_config_data_get_keyfile_intern (const NMConfigData *self);
G_END_DECLS
diff --git a/src/nm-config.c b/src/nm-config.c
index add9b4d696..215595c208 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -33,6 +33,7 @@
#include "gsystem-local-alloc.h"
#include "nm-enum-types.h"
#include "nm-core-internal.h"
+#include "nm-keyfile-internal.h"
#include <gio/gio.h>
#include <glib/gi18n.h>
@@ -40,11 +41,15 @@
#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 DEFAULT_NO_AUTO_DEFAULT_FILE NMSTATEDIR "/no-auto-default.state"
+#define DEFAULT_INTERN_CONFIG_FILE NMSTATEDIR "/NetworkManager-intern.conf"
struct NMConfigCmdLineOptions {
char *config_main_file;
+ char *intern_config_file;
char *config_dir;
+ char *system_config_dir;
char *no_auto_default_file;
char *plugins;
gboolean configure_and_quit;
@@ -64,7 +69,9 @@ typedef struct {
NMConfigData *config_data_orig;
char *config_dir;
+ char *system_config_dir;
char *no_auto_default_file;
+ char *intern_config_file;
char **plugins;
gboolean monitor_connection_files;
@@ -77,11 +84,14 @@ typedef struct {
char *debug;
gboolean configure_and_quit;
+
+ char **atomic_section_prefixes;
} NMConfigPrivate;
enum {
PROP_0,
PROP_CMD_LINE_OPTIONS,
+ PROP_ATOMIC_SECTION_PREFIXES,
LAST_PROP,
};
@@ -108,6 +118,14 @@ static void _set_config_data (NMConfig *self, NMConfigData *new_data, int signal
/************************************************************************/
+#define _HAS_PREFIX(str, prefix) \
+ ({ \
+ const char *_str = (str); \
+ g_str_has_prefix ( _str, ""prefix"") && _str[STRLEN(prefix)] != '\0'; \
+ })
+
+/************************************************************************/
+
gint
nm_config_parse_boolean (const char *str,
gint default_value)
@@ -359,7 +377,7 @@ nm_config_get_no_auto_default_for_device (NMConfig *self, NMDevice *device)
void
nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device)
{
- NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self);
+ NMConfigPrivate *priv;
GError *error = NULL;
NMConfigData *new_data = NULL;
const char *hw_address;
@@ -370,6 +388,8 @@ nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device)
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_hw_address (device);
no_auto_default_current = nm_config_data_get_no_auto_default (priv->config_data);
@@ -407,7 +427,9 @@ _nm_config_cmd_line_options_clear (NMConfigCmdLineOptions *cli)
{
g_clear_pointer (&cli->config_main_file, g_free);
g_clear_pointer (&cli->config_dir, g_free);
+ g_clear_pointer (&cli->system_config_dir, g_free);
g_clear_pointer (&cli->no_auto_default_file, g_free);
+ g_clear_pointer (&cli->intern_config_file, g_free);
g_clear_pointer (&cli->plugins, g_free);
cli->configure_and_quit = FALSE;
g_clear_pointer (&cli->connectivity_uri, g_free);
@@ -424,8 +446,10 @@ _nm_config_cmd_line_options_copy (const NMConfigCmdLineOptions *cli, NMConfigCmd
_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->plugins = g_strdup (cli->plugins);
dst->configure_and_quit = cli->configure_and_quit;
dst->connectivity_uri = g_strdup (cli->connectivity_uri);
@@ -462,6 +486,8 @@ nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli,
GOptionEntry config_options[] = {
{ "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), N_(DEFAULT_CONFIG_MAIN_FILE) },
{ "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), N_(DEFAULT_CONFIG_DIR) },
+ { "system-config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->system_config_dir, N_("System config directory location"), N_(DEFAULT_SYSTEM_CONFIG_DIR) },
+ { "intern-config", 0, 0, G_OPTION_ARG_FILENAME, &cli->intern_config_file, N_("Internal config file location"), N_(DEFAULT_INTERN_CONFIG_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"), N_(DEFAULT_NO_AUTO_DEFAULT_FILE) },
{ "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), N_(CONFIG_PLUGINS_DEFAULT) },
{ "configure-and-quit", 0, 0, G_OPTION_ARG_NONE, &cli->configure_and_quit, N_("Quit after initial configuration"), NULL },
@@ -528,6 +554,18 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy)
return pa > pb ? -1 : 1;
}
+void
+_nm_config_sort_groups (char **groups, gsize ngroups)
+{
+ 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)
{
@@ -550,17 +588,23 @@ _setting_is_string_list (const char *group, const char *key)
}
static gboolean
-read_config (GKeyFile *keyfile, const char *path, GError **error)
+read_config (GKeyFile *keyfile, const char *dirname, const char *path, GError **error)
{
GKeyFile *kf;
char **groups, **keys;
gsize ngroups, nkeys;
int g, k;
+ gs_free char *path_free = NULL;
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;
@@ -587,17 +631,15 @@ read_config (GKeyFile *keyfile, const char *path, GError **error)
* At the very end, we will revert the order of all sections again and
* get thus the right behavior. This final reversing is done in
* NMConfigData:_get_connection_infos(). */
- if (ngroups > 1) {
- g_qsort_with_data (groups,
- ngroups,
- sizeof (char *),
- (GCompareDataFunc) _sort_groups_cmp,
- NULL);
- }
+ _nm_config_sort_groups (groups, ngroups);
for (g = 0; groups[g]; g++) {
const char *group = groups[g];
+ if (g_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;
@@ -609,6 +651,18 @@ read_config (GKeyFile *keyfile, const char *path, GError **error)
key = keys[k];
g_assert (key && *key);
+
+ if ( _HAS_PREFIX (key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)
+ || _HAS_PREFIX (key, NM_CONFIG_KEYFILE_KEYPREFIX_SET)) {
+ /* these keys are protected. We ignore them if the user sets them. */
+ continue;
+ }
+
+ if (!strcmp (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
@@ -710,7 +764,7 @@ read_base_config (GKeyFile *keyfile,
/* 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, cli_config_main_file, error)) {
+ if (read_config (keyfile, NULL, cli_config_main_file, error)) {
*out_config_main_file = g_strdup (cli_config_main_file);
return TRUE;
} else
@@ -725,7 +779,7 @@ read_base_config (GKeyFile *keyfile,
*/
/* Try deprecated nm-system-settings.conf first */
- if (read_config (keyfile, DEFAULT_CONFIG_MAIN_FILE_OLD, &my_error)) {
+ if (read_config (keyfile, NULL, DEFAULT_CONFIG_MAIN_FILE_OLD, &my_error)) {
*out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE_OLD);
return TRUE;
}
@@ -738,7 +792,7 @@ read_base_config (GKeyFile *keyfile,
g_clear_error (&my_error);
/* Try the standard config file location next */
- if (read_config (keyfile, DEFAULT_CONFIG_MAIN_FILE, &my_error)) {
+ if (read_config (keyfile, NULL, DEFAULT_CONFIG_MAIN_FILE, &my_error)) {
*out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE);
return TRUE;
}
@@ -771,24 +825,20 @@ sort_asciibetically (gconstpointer a, gconstpointer b)
}
static GPtrArray *
-_get_config_dir_files (const char *config_main_file,
- const char *config_dir,
- char **out_config_description)
+_get_config_dir_files (const char *config_dir)
{
GFile *dir;
GFileEnumerator *direnum;
GFileInfo *info;
GPtrArray *confs;
- GString *config_description;
const char *name;
- guint i;
- g_return_val_if_fail (config_main_file, NULL);
g_return_val_if_fail (config_dir, NULL);
- g_return_val_if_fail (out_config_description && !*out_config_description, NULL);
confs = g_ptr_array_new_with_free_func (g_free);
- config_description = g_string_new (config_main_file);
+ 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) {
@@ -802,43 +852,59 @@ _get_config_dir_files (const char *config_main_file,
}
g_object_unref (dir);
- if (confs->len > 0) {
- g_ptr_array_sort (confs, sort_asciibetically);
- g_string_append (config_description, " and conf.d: ");
- for (i = 0; i < confs->len; i++) {
- char *n = confs->pdata[i];
-
- if (i > 0)
- g_string_append (config_description, ", ");
- g_string_append (config_description, n);
- confs->pdata[i] = g_build_filename (config_dir, n, NULL);
- g_free (n);
- }
- }
-
- *out_config_description = g_string_free (config_description, FALSE);
+ g_ptr_array_sort (confs, sort_asciibetically);
return confs;
}
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,
GError **error)
{
- GKeyFile *keyfile = nm_config_create_keyfile ();
- GPtrArray *confs;
+ GKeyFile *keyfile;
+ gs_unref_ptrarray GPtrArray *system_confs = NULL;
+ gs_unref_ptrarray GPtrArray *confs = NULL;
guint i;
- char *o_config_main_file = NULL;
- char *o_config_description = NULL;
- char **plugins_tmp;
+ gs_free char *o_config_main_file = NULL;
+ GString *str;
+ char **plugins_default;
g_return_val_if_fail (config_dir, NULL);
- g_return_val_if_fail (out_config_main_file && !*out_config_main_file, FALSE);
- g_return_val_if_fail (out_config_description && !*out_config_description, NULL);
+ g_return_val_if_fail (system_config_dir, NULL);
+ g_return_val_if_fail (!out_config_main_file || !*out_config_main_file, FALSE);
+ g_return_val_if_fail (!out_config_description || !*out_config_description, NULL);
g_return_val_if_fail (!error || !*error, FALSE);
+ /* create a default configuration file. */
+ keyfile = nm_config_create_keyfile ();
+
+ plugins_default = g_strsplit (CONFIG_PLUGINS_DEFAULT, ",", -1);
+ if (plugins_default && plugins_default[0])
+ nm_config_keyfile_set_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", (const char *const*) plugins_default, -1);
+ g_strfreev (plugins_default);
+
+ system_confs = _get_config_dir_files (system_config_dir);
+ confs = _get_config_dir_files (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, skip it. */
+ if (_nm_utils_strv_find_first ((char **) confs->pdata, confs->len, filename) >= 0) {
+ g_ptr_array_remove_index (system_confs, i);
+ continue;
+ }
+
+ if (!read_config (keyfile, system_config_dir, filename, error)) {
+ g_key_file_free (keyfile);
+ return NULL;
+ }
+ i++;
+ }
+
/* First read the base config file */
if (!read_base_config (keyfile, cli ? cli->config_main_file : NULL, &o_config_main_file, error)) {
g_key_file_free (keyfile);
@@ -847,33 +913,22 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
g_assert (o_config_main_file);
- confs = _get_config_dir_files (o_config_main_file, config_dir, &o_config_description);
for (i = 0; i < confs->len; i++) {
- if (!read_config (keyfile, confs->pdata[i], error)) {
+ if (!read_config (keyfile, config_dir, confs->pdata[i], error)) {
g_key_file_free (keyfile);
- g_free (o_config_main_file);
- g_free (o_config_description);
- g_ptr_array_unref (confs);
return NULL;
}
}
- g_ptr_array_unref (confs);
/* Merge settings from command line. They overwrite everything read from
* config files. */
-
- if (cli && cli->plugins && cli->plugins[0])
+ if (cli && 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);
- plugins_tmp = g_key_file_get_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NULL, NULL);
- if (!plugins_tmp) {
- if (STRLEN (CONFIG_PLUGINS_DEFAULT) > 0)
- g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", CONFIG_PLUGINS_DEFAULT);
- } else
- g_strfreev (plugins_tmp);
-
+ }
if (cli && cli->configure_and_quit)
g_key_file_set_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", TRUE);
-
if (cli && cli->connectivity_uri && cli->connectivity_uri[0])
g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", cli->connectivity_uri);
if (cli && cli->connectivity_interval >= 0)
@@ -881,11 +936,462 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
if (cli && cli->connectivity_response && cli->connectivity_response[0])
g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", cli->connectivity_response);
- *out_config_main_file = o_config_main_file;
- *out_config_description = o_config_description;
+ str = g_string_new (o_config_main_file);
+ if (system_confs->len > 0) {
+ for (i = 0; i < system_confs->len; i++) {
+ if (i == 0)
+ g_string_append (str, " (lib: ");
+ else
+ g_string_append (str, ", ");
+ g_string_append (str, system_confs->pdata[i]);
+ }
+ g_string_append (str, ")");
+ }
+ if (confs->len > 0) {
+ for (i = 0; i < confs->len; i++) {
+ if (i == 0)
+ g_string_append (str, " (etc: ");
+ else
+ g_string_append (str, ", ");
+ g_string_append (str, confs->pdata[i]);
+ }
+ g_string_append (str, ")");
+ }
+
+ if (out_config_main_file)
+ *out_config_main_file = o_config_main_file;
+ else
+ g_free (o_config_main_file);
+ if (out_config_description)
+ *out_config_description = g_string_free (str, FALSE);
+ else
+ g_string_free (str, TRUE);
+
+ o_config_main_file = NULL;
return keyfile;
}
+static gboolean
+_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;
+}
+
+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, '+');
+ default:
+ g_string_append_c (str, *value);
+ }
+ value++;
+ }
+}
+
+static char *
+_keyfile_serialize_section (GKeyFile *keyfile, const char *group)
+{
+ 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#");
+
+ /* prepend a version. */
+ str = g_string_new ("1#");
+
+ 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, ':');
+
+ 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);
+}
+
+/**
+ * intern_config_read:
+ * @filename: the filename where to store the internal config
+ * @keyfile_conf: the merged configuration from user (/etc/NM/NetworkManager.conf).
+ * @out_needs_rewrite: (allow-none): whether the read keyfile contains inconsistent
+ * data (compared to @keyfile_conf). If %TRUE, you might want to rewrite
+ * the file.
+ *
+ * Does the opposite of intern_config_write(). It reads the internal configuration.
+ * Note that the actual format of how the configuration is saved in @filename
+ * is different then what we return here. NMConfig manages what is written internally
+ * by having it inside a keyfile_intern. But we don't write that to disk as is.
+ * Especially, we also store parts of @keyfile_conf as ".was" and on read we compare
+ * what we have, with what ".was".
+ *
+ * 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;
+
+ keys = g_key_file_get_keys (keyfile, group, NULL, NULL);
+ if (!keys)
+ continue;
+
+ is_intern = g_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 (g_strcmp0 (conf_section_was, conf_section_is) != 0) {
+ /* 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 (strcmp (key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS) == 0)
+ continue;
+ g_key_file_set_value (keyfile_intern, group, key, value_set);
+ } else if (_HAS_PREFIX (key, NM_CONFIG_KEYFILE_KEYPREFIX_SET)) {
+ const char *key_base = &key[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 (g_strcmp0 (value_conf, value_was) != 0) {
+ /* 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 (_HAS_PREFIX (key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) {
+ const char *key_base = &key[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 (g_strcmp0 (value_conf, value_was) != 0) {
+ /* 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:
+ g_key_file_unref (keyfile);
+
+ if (out_needs_rewrite)
+ *out_needs_rewrite = needs_rewrite;
+
+ nm_log_dbg (LOGD_CORE, "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 ? *a : NULL);
+ const char *g_b = (b ? *b : NULL);
+ gboolean a_is, b_is;
+
+ a_is = g_str_has_prefix (g_a, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN);
+ b_is = g_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 has_intern = FALSE;
+ 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 = g_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) {
+ has_intern = TRUE;
+ g_key_file_set_value (keyfile, group, key, value_set);
+ } else if (is_atomic)
+ g_key_file_set_value (keyfile, group, key, value_set);
+ else {
+ gs_free char *value_was = NULL;
+
+ if (_HAS_PREFIX (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 (_HAS_PREFIX (key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) {
+ const char *key_base = &key[STRLEN (NM_CONFIG_KEYFILE_KEYPREFIX_WAS)];
+
+ if ( _HAS_PREFIX (key_base, NM_CONFIG_KEYFILE_KEYPREFIX_SET)
+ || _HAS_PREFIX (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 (g_strcmp0 (value_set, value_was) == 0) {
+ /* 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);
+
+ nm_log_dbg (LOGD_CORE, "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_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key, gboolean *out_has_key)
{
@@ -902,16 +1408,112 @@ nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, con
/************************************************************************/
+/**
+ * nm_config_set_values:
+ * @self: the NMConfig instance
+ * @keyfile_intern_new: (allow-none): the new internal settings to set.
+ * If %NULL, it is equal to an empty keyfile.
+ * @allow_write: only if %TRUE, allow writing the changes to file. Otherwise,
+ * do the changes in-memory only.
+ * @force_rewrite: if @allow_write is %FALSE, this has no effect. If %FALSE,
+ * only write the configuration to file, if there are any actual changes.
+ * If %TRUE, always write the configuration to file, even if tere are seemingly
+ * no changes.
+ *
+ * This is the most flexible function to set values. It all depends on the
+ * keys and values you set in @keyfile_intern_new. You basically reset all
+ * internal configuration values to what is in @keyfile_intern_new.
+ *
+ * There are 3 types of settings:
+ * - all groups/sections with a prefix [.intern.*] are taken as is. As these
+ * groups are separate from user configuration, there is no conflict. You set
+ * them, that's it.
+ * - there are atomic sections, i.e. sections whose name start with one of
+ * NM_CONFIG_ATOMIC_SECTION_PREFIXES. If you put values in these sections,
+ * it means you completely replace the section from user configuration.
+ * You can also hide a user provided section by only putting the special
+ * key NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS into that section.
+ * - otherwise you can overwrite individual values from user-configuration.
+ * Just set the value. Keys with a prefix NM_CONFIG_KEYFILE_KEYPREFIX_*
+ * are protected -- as they are not value user keys.
+ * You can also hide a certain user setting by putting only a key
+ * 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;
+ gint 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);
+
+ nm_log_dbg (LOGD_CORE, "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)) {
+ nm_log_warn (LOGD_CORE, "error saving internal configuration \"%s\": %s", priv->intern_config_file, local->message);
+ g_clear_error (&local);
+ }
+ } else
+ nm_log_dbg (LOGD_CORE, "don't persistate internal configuration (no file set, use --intern-config?)");
+ }
+ if (new_data)
+ _set_config_data (self, new_data, 0);
+
+ g_key_file_unref (keyfile_new);
+}
+
+/************************************************************************/
+
void
nm_config_reload (NMConfig *self, int signal)
{
NMConfigPrivate *priv;
GError *error = NULL;
- GKeyFile *keyfile;
+ 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;
g_return_if_fail (NM_IS_CONFIG (self));
@@ -928,6 +1530,7 @@ nm_config_reload (NMConfig *self, int signal)
*/
keyfile = read_entire_config (&priv->cli,
priv->config_dir,
+ priv->system_config_dir,
&config_main_file,
&config_description,
&error);
@@ -937,12 +1540,24 @@ nm_config_reload (NMConfig *self, int signal)
_set_config_data (self, NULL, signal);
return;
}
+
no_auto_default = no_auto_default_from_file (priv->no_auto_default_file);
- new_data = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile);
+ 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);
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, signal);
}
@@ -961,6 +1576,10 @@ _change_flags_one_to_string (NMConfigChangeFlags flag)
return "config-files";
case NM_CONFIG_CHANGE_VALUES:
return "values";
+ case NM_CONFIG_CHANGE_VALUES_USER:
+ return "values-user";
+ case NM_CONFIG_CHANGE_VALUES_INTERN:
+ return "values-intern";
case NM_CONFIG_CHANGE_CONNECTIVITY:
return "connectivity";
case NM_CONFIG_CHANGE_NO_AUTO_DEFAULT:
@@ -1054,11 +1673,11 @@ nm_config_get (void)
}
NMConfig *
-nm_config_setup (const NMConfigCmdLineOptions *cli, GError **error)
+nm_config_setup (const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error)
{
g_assert (!singleton_instance);
- singleton_instance = nm_config_new (cli, error);
+ singleton_instance = nm_config_new (cli, atomic_section_prefixes, error);
if (singleton_instance)
nm_singleton_instance_weak_ref_register ();
return singleton_instance;
@@ -1069,10 +1688,11 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
{
NMConfig *self = NM_CONFIG (initable);
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self);
- GKeyFile *keyfile;
+ GKeyFile *keyfile, *keyfile_intern;
char *config_main_file = NULL;
char *config_description = NULL;
gs_strfreev char **no_auto_default = NULL;
+ gboolean intern_config_needs_rewrite;
if (priv->config_dir) {
/* Object is already initialized. */
@@ -1087,8 +1707,26 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
else
priv->config_dir = g_strdup (DEFAULT_CONFIG_DIR);
+ if (priv->cli.system_config_dir)
+ priv->system_config_dir = g_strdup (priv->cli.system_config_dir);
+ else
+ priv->system_config_dir = g_strdup (DEFAULT_SYSTEM_CONFIG_DIR);
+
+ if (strcmp (priv->config_dir, priv->system_config_dir) == 0) {
+ /* having the same directory twice makes no sense. In that case, clear
+ * @system_config_dir. */
+ g_free (priv->system_config_dir);
+ priv->system_config_dir = g_strdup ("");
+ }
+
+ 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);
+
keyfile = read_entire_config (&priv->cli,
priv->config_dir,
+ priv->system_config_dir,
&config_main_file,
&config_description,
error);
@@ -1122,23 +1760,35 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
no_auto_default = no_auto_default_from_file (priv->no_auto_default_file);
- priv->config_data_orig = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile);
+ 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);
+ }
+
+ priv->config_data_orig = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile, keyfile_intern);
priv->config_data = g_object_ref (priv->config_data_orig);
g_free (config_main_file);
g_free (config_description);
g_key_file_unref (keyfile);
+ if (keyfile_intern)
+ g_key_file_unref (keyfile_intern);
return TRUE;
}
NMConfig *
-nm_config_new (const NMConfigCmdLineOptions *cli, 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));
}
@@ -1156,12 +1806,15 @@ finalize (GObject *gobject)
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject);
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_strfreev (priv->plugins);
g_free (priv->dhcp_client);
g_free (priv->log_level);
g_free (priv->log_domains);
g_free (priv->debug);
+ g_strfreev (priv->atomic_section_prefixes);
_nm_config_cmd_line_options_clear (&priv->cli);
@@ -1188,6 +1841,10 @@ set_property (GObject *object, guint prop_id,
else
_nm_config_cmd_line_options_copy (cli, &priv->cli);
break;
+ case PROP_ATOMIC_SECTION_PREFIXES:
+ /* construct only */
+ priv->atomic_section_prefixes = g_strdupv (g_value_get_boxed (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1210,6 +1867,14 @@ nm_config_class_init (NMConfigClass *config_class)
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, 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));
+
signals[SIGNAL_CONFIG_CHANGED] =
g_signal_new (NM_CONFIG_SIGNAL_CONFIG_CHANGED,
G_OBJECT_CLASS_TYPE (object_class),
diff --git a/src/nm-config.h b/src/nm-config.h
index 5b096f165d..3caf0c5c31 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -39,6 +39,7 @@ G_BEGIN_DECLS
/* Properties */
#define NM_CONFIG_CMD_LINE_OPTIONS "cmd-line-options"
+#define NM_CONFIG_ATOMIC_SECTION_PREFIXES "atomic-section-prefixes"
/* Signals */
#define NM_CONFIG_SIGNAL_CONFIG_CHANGED "config-changed"
@@ -48,6 +49,7 @@ G_BEGIN_DECLS
#define NM_CONFIG_KEYFILE_LIST_SEPARATOR ','
+#define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN ".intern."
#define NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION "connection"
#define NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".test-append-stringlist"
@@ -59,10 +61,14 @@ G_BEGIN_DECLS
#define NM_CONFIG_KEYFILE_GROUP_IFUPDOWN "ifupdown"
#define NM_CONFIG_KEYFILE_GROUP_IFNET "ifnet"
+#define NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS ".was"
#define NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH "auto_refresh"
#define NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED "managed"
#define NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED "managed"
+#define NM_CONFIG_KEYFILE_KEYPREFIX_WAS ".was."
+#define NM_CONFIG_KEYFILE_KEYPREFIX_SET ".set."
+
typedef struct NMConfigCmdLineOptions NMConfigCmdLineOptions;
struct _NMConfig {
@@ -97,6 +103,11 @@ const char *nm_config_get_log_domains (NMConfig *config);
const char *nm_config_get_debug (NMConfig *config);
gboolean nm_config_get_configure_and_quit (NMConfig *config);
+void nm_config_set_values (NMConfig *self,
+ GKeyFile *keyfile_intern_new,
+ gboolean allow_write,
+ gboolean force_rewrite);
+
/* for main.c only */
NMConfigCmdLineOptions *nm_config_cmd_line_options_new (void);
void nm_config_cmd_line_options_free (NMConfigCmdLineOptions *cli);
@@ -106,8 +117,8 @@ void nm_config_cmd_line_options_add_to_entries (NMConfigCmdLi
gboolean nm_config_get_no_auto_default_for_device (NMConfig *config, NMDevice *device);
void nm_config_set_no_auto_default_for_device (NMConfig *config, NMDevice *device);
-NMConfig *nm_config_new (const NMConfigCmdLineOptions *cli, GError **error);
-NMConfig *nm_config_setup (const NMConfigCmdLineOptions *cli, GError **error);
+NMConfig *nm_config_new (const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error);
+NMConfig *nm_config_setup (const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error);
void nm_config_reload (NMConfig *config, int signal);
gint nm_config_parse_boolean (const char *str, gint default_value);
@@ -128,6 +139,8 @@ void nm_config_keyfile_set_string_list (GKeyFile *keyfile,
gssize len);
GSList *nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key, gboolean *out_has_key);
+void _nm_config_sort_groups (char **groups, gsize ngroups);
+
G_END_DECLS
#endif /* __NETWORKMANAGER_CONFIG_H__ */
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 9f7b47d9bc..befebc1591 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -764,18 +764,20 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
const char **iter;
gboolean keyfile_added = FALSE;
gboolean success = TRUE;
+ gboolean add_ibft = FALSE;
+ gboolean has_no_ibft;
+ gssize idx_no_ibft, idx_ibft;
+
+ idx_ibft = _nm_utils_strv_find_first ((char **) plugins, -1, "ibft");
+ idx_no_ibft = _nm_utils_strv_find_first ((char **) plugins, -1, "no-ibft");
+ has_no_ibft = idx_no_ibft >= 0 && idx_no_ibft > idx_ibft;
+#if WITH_SETTINGS_PLUGIN_IBFT
+ add_ibft = idx_no_ibft < 0 && idx_ibft < 0;
+#endif
for (iter = plugins; iter && *iter; iter++) {
- GModule *plugin;
- gs_free char *full_name = NULL;
- gs_free char *path = NULL;
- const char *pname;
+ const char *pname = *iter;
GObject *obj;
- GObject * (*factory_func) (void);
- struct stat st;
- int errsv;
-
- pname = *iter;
if (!*pname || strchr (pname, '/')) {
LOG (LOGL_WARN, "ignore invalid plugin \"%s\"", pname);
@@ -787,6 +789,11 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
continue;
}
+ if (!strcmp (pname, "no-ibft"))
+ continue;
+ if (has_no_ibft && !strcmp (pname, "ibft"))
+ continue;
+
obj = find_plugin (list, pname);
if (obj)
continue;
@@ -800,61 +807,79 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
continue;
}
- full_name = g_strdup_printf ("nm-settings-plugin-%s", pname);
- path = g_module_build_path (NMPLUGINDIR, full_name);
+load_plugin:
+ {
+ GModule *plugin;
+ gs_free char *full_name = NULL;
+ gs_free char *path = NULL;
+ GObject * (*factory_func) (void);
+ struct stat st;
+ int errsv;
+
+ full_name = g_strdup_printf ("nm-settings-plugin-%s", pname);
+ path = g_module_build_path (NMPLUGINDIR, full_name);
+
+ if (stat (path, &st) != 0) {
+ errsv = errno;
+ LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': %s", pname, path, strerror (errsv));
+ goto next;
+ }
+ if (!S_ISREG (st.st_mode)) {
+ LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': not a file", pname, path);
+ goto next;
+ }
+ if (st.st_uid != 0) {
+ LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': file must be owned by root", pname, path);
+ goto next;
+ }
+ if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) {
+ LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': invalid file permissions", pname, path);
+ goto next;
+ }
- if (stat (path, &st) != 0) {
- errsv = errno;
- LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': %s", pname, path, strerror (errsv));
- continue;
- }
- if (!S_ISREG (st.st_mode)) {
- LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': not a file", pname, path);
- continue;
- }
- if (st.st_uid != 0) {
- LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': file must be owned by root", pname, path);
- continue;
- }
- if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) {
- LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': invalid file permissions", pname, path);
- continue;
- }
+ plugin = g_module_open (path, G_MODULE_BIND_LOCAL);
+ if (!plugin) {
+ LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': %s",
+ pname, path, g_module_error ());
+ goto next;
+ }
- plugin = g_module_open (path, G_MODULE_BIND_LOCAL);
- if (!plugin) {
- LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': %s",
- pname, path, g_module_error ());
- continue;
- }
+ /* errors after this point are fatal, because we loaded the shared library already. */
+
+ if (!g_module_symbol (plugin, "nm_system_config_factory", (gpointer) (&factory_func))) {
+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
+ "Could not find plugin '%s' factory function.",
+ pname);
+ success = FALSE;
+ g_module_close (plugin);
+ break;
+ }
- /* errors after this point are fatal, because we loaded the shared library already. */
+ obj = (*factory_func) ();
+ if (!obj || !NM_IS_SYSTEM_CONFIG_INTERFACE (obj)) {
+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
+ "Plugin '%s' returned invalid system config object.",
+ pname);
+ success = FALSE;
+ g_module_close (plugin);
+ break;
+ }
- if (!g_module_symbol (plugin, "nm_system_config_factory", (gpointer) (&factory_func))) {
- g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Could not find plugin '%s' factory function.",
- pname);
- success = FALSE;
- g_module_close (plugin);
- break;
+ g_module_make_resident (plugin);
+ g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin);
+ g_object_set_data_full (obj, PLUGIN_MODULE_PATH, path, g_free);
+ path = NULL;
+ add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (obj));
+ list = g_slist_append (list, obj);
}
-
- obj = (*factory_func) ();
- if (!obj || !NM_IS_SYSTEM_CONFIG_INTERFACE (obj)) {
- g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
- "Plugin '%s' returned invalid system config object.",
- pname);
- success = FALSE;
- g_module_close (plugin);
- break;
+next:
+ if (add_ibft && !strcmp (pname, "ifcfg-rh")) {
+ /* The plugin ibft is not explicitly mentioned but we just enabled "ifcfg-rh".
+ * Enable "ibft" by default after "ifcfg-rh". */
+ pname = "ibft";
+ add_ibft = FALSE;
+ goto load_plugin;
}
-
- g_module_make_resident (plugin);
- g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin);
- g_object_set_data_full (obj, PLUGIN_MODULE_PATH, path, g_free);
- path = NULL;
- add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (obj));
- list = g_slist_append (list, obj);
}
/* If keyfile plugin was not among configured plugins, add it as the last one */
diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c
index ca115fd1ab..33b66d614f 100644
--- a/src/tests/config/test-config.c
+++ b/src/tests/config/test-config.c
@@ -32,8 +32,27 @@
#include "nm-test-utils.h"
+/********************************************************************************/
+
+static void
+_assert_config_value (const NMConfigData *config_data, const char *group, const char *key, const char *expected_value, const char *file, int line)
+{
+ gs_free char *value = NULL;
+
+ value = nm_config_data_get_value (config_data, group, key, NM_CONFIG_GET_VALUE_NONE);
+ if (g_strcmp0 (value, expected_value)) {
+ g_error ("(%s:%d) invalid value in config-data %s.%s = %s%s%s (instead of %s%s%s)",
+ file, line, group, key,
+ NM_PRINT_FMT_QUOTED (value, "\"", value, "\"", "(null)"),
+ NM_PRINT_FMT_QUOTED (expected_value, "\"", expected_value, "\"", "(null)"));
+ }
+}
+#define assert_config_value(config_data, group, key, expected_value) _assert_config_value (config_data, group, key, expected_value, __FILE__, __LINE__)
+
+/********************************************************************************/
+
static NMConfig *
-setup_config (GError **error, const char *config_file, const char *config_dir, ...)
+setup_config (GError **error, const char *config_file, const char *intern_config, const char *const* atomic_section_prefixes, const char *config_dir, const char *system_config_dir, ...)
{
va_list ap;
GPtrArray *args;
@@ -51,10 +70,18 @@ setup_config (GError **error, const char *config_file, const char *config_dir, .
g_ptr_array_add (args, "test-config");
g_ptr_array_add (args, "--config");
g_ptr_array_add (args, (char *)config_file);
+ if (intern_config) {
+ g_ptr_array_add (args, "--intern-config");
+ g_ptr_array_add (args, (char *)intern_config);
+ }
g_ptr_array_add (args, "--config-dir");
g_ptr_array_add (args, (char *)config_dir);
+ if (system_config_dir) {
+ g_ptr_array_add (args, "--system-config-dir");
+ g_ptr_array_add (args, (char *) system_config_dir);
+ }
- va_start (ap, config_dir);
+ va_start (ap, system_config_dir);
while ((arg = va_arg (ap, char *)))
g_ptr_array_add (args, arg);
va_end (ap);
@@ -74,7 +101,7 @@ setup_config (GError **error, const char *config_file, const char *config_dir, .
g_ptr_array_free (args, TRUE);
- config = nm_config_setup (cli, &local_error);
+ config = nm_config_setup (cli, (char **) atomic_section_prefixes, &local_error);
if (error) {
g_assert (!config);
g_assert (local_error);
@@ -97,7 +124,7 @@ test_config_simple (void)
gs_unref_object NMDevice *dev51 = nm_test_device_new ("00:00:00:00:00:51");
gs_unref_object NMDevice *dev52 = nm_test_device_new ("00:00:00:00:00:52");
- config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "/no/such/dir", NULL);
+ config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", NULL);
g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, SRCDIR "/NetworkManager.conf");
g_assert_cmpstr (nm_config_get_dhcp_client (config), ==, "dhclient");
@@ -176,7 +203,7 @@ test_config_non_existent (void)
{
GError *error = NULL;
- setup_config (&error, SRCDIR "/no-such-file", "/no/such/dir", NULL);
+ setup_config (&error, SRCDIR "/no-such-file", "", NULL, "/no/such/dir", "", NULL);
g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND);
g_clear_error (&error);
}
@@ -186,7 +213,7 @@ test_config_parse_error (void)
{
GError *error = NULL;
- setup_config (&error, SRCDIR "/bad.conf", "/no/such/dir", NULL);
+ setup_config (&error, SRCDIR "/bad.conf", "", NULL, "/no/such/dir", "", NULL);
g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE);
g_clear_error (&error);
}
@@ -197,7 +224,7 @@ test_config_override (void)
NMConfig *config;
const char **plugins;
- config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "/no/such/dir",
+ config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "",
"--plugins", "alpha,beta,gamma,delta",
"--connectivity-interval", "12",
NULL);
@@ -235,7 +262,7 @@ test_config_no_auto_default (void)
g_assert_cmpint (nwrote, ==, 18);
close (fd);
- config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "/no/such/dir",
+ config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "",
"--no-auto-default", state_file,
NULL);
@@ -257,7 +284,7 @@ test_config_no_auto_default (void)
g_object_unref (config);
- config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "/no/such/dir",
+ config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "",
"--no-auto-default", state_file,
NULL);
@@ -285,7 +312,7 @@ test_config_confdir (void)
char *value;
GSList *specs;
- config = setup_config (NULL, SRCDIR "/NetworkManager.conf", SRCDIR "/conf.d", NULL);
+ config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR "/conf.d", "", NULL);
g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, SRCDIR "/NetworkManager.conf");
g_assert_cmpstr (nm_config_get_dhcp_client (config), ==, "dhcpcd");
@@ -391,11 +418,336 @@ test_config_confdir_parse_error (void)
GError *error = NULL;
/* Using SRCDIR as the conf dir will pick up bad.conf */
- setup_config (&error, SRCDIR "/NetworkManager.conf", SRCDIR, NULL);
+ setup_config (&error, SRCDIR "/NetworkManager.conf", "", NULL, SRCDIR, "", NULL);
g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE);
g_clear_error (&error);
}
+/*****************************************************************************/
+
+typedef void (*TestSetValuesUserSetFcn) (NMConfig *config, gboolean is_user, GKeyFile *keyfile_user, NMConfigChangeFlags *out_expected_changes);
+typedef void (*TestSetValuesCheckStateFcn) (NMConfig *config, NMConfigData *config_data, gboolean is_change_event, NMConfigChangeFlags changes, NMConfigData *old_data);
+
+typedef struct {
+ NMConfigChangeFlags changes;
+ TestSetValuesCheckStateFcn check_state_fcn;
+} TestSetValuesConfigChangedData;
+
+static void
+_set_values_config_changed_cb (NMConfig *config,
+ NMConfigData *config_data,
+ NMConfigChangeFlags changes,
+ NMConfigData *old_data,
+ TestSetValuesConfigChangedData *config_changed_data)
+{
+ g_assert (changes != NM_CONFIG_CHANGE_NONE);
+ g_assert (config_changed_data);
+ g_assert (config_changed_data->changes == NM_CONFIG_CHANGE_NONE);
+
+ if (changes == NM_CONFIG_CHANGE_SIGHUP)
+ return;
+ changes &= ~NM_CONFIG_CHANGE_SIGHUP;
+
+ config_changed_data->changes = changes;
+
+ if (config_changed_data->check_state_fcn)
+ config_changed_data->check_state_fcn (config, config_data, TRUE, changes, old_data);
+}
+
+static void
+_set_values_user (NMConfig *config,
+ const char *CONFIG_USER,
+ TestSetValuesUserSetFcn set_fcn,
+ TestSetValuesCheckStateFcn check_state_fcn)
+{
+ GKeyFile *keyfile_user;
+ gboolean success;
+ gs_free_error GError *error = NULL;
+ TestSetValuesConfigChangedData config_changed_data = {
+ .changes = NM_CONFIG_CHANGE_NONE,
+ .check_state_fcn = check_state_fcn,
+ };
+ NMConfigChangeFlags expected_changes = NM_CONFIG_CHANGE_NONE;
+ gs_unref_object NMConfigData *config_data_before = NULL;
+
+ keyfile_user = nm_config_create_keyfile ();
+
+ success = g_key_file_load_from_file (keyfile_user, CONFIG_USER, G_KEY_FILE_NONE, &error);
+ nmtst_assert_success (success, error);
+
+ if (set_fcn)
+ set_fcn (config, TRUE, keyfile_user, &expected_changes);
+
+ success = g_key_file_save_to_file (keyfile_user, CONFIG_USER, &error);
+ nmtst_assert_success (success, error);
+
+ g_signal_connect (G_OBJECT (config),
+ NM_CONFIG_SIGNAL_CONFIG_CHANGED,
+ G_CALLBACK (_set_values_config_changed_cb),
+ &config_changed_data);
+
+ config_data_before = g_object_ref (nm_config_get_data (config));
+
+ if (expected_changes != NM_CONFIG_CHANGE_NONE)
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: update *");
+ else
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: signal SIGHUP (no changes from disk)*");
+
+ nm_config_reload (config, SIGHUP);
+
+ g_test_assert_expected_messages ();
+
+ g_assert (expected_changes == config_changed_data.changes);
+
+ if (check_state_fcn)
+ check_state_fcn (config, nm_config_get_data (config), FALSE, NM_CONFIG_CHANGE_NONE, config_data_before);
+
+ g_signal_handlers_disconnect_by_func (config, _set_values_config_changed_cb, &config_changed_data);
+
+ g_key_file_unref (keyfile_user);
+}
+
+static void
+_set_values_intern (NMConfig *config,
+ TestSetValuesUserSetFcn set_fcn,
+ TestSetValuesCheckStateFcn check_state_fcn)
+{
+ GKeyFile *keyfile_intern;
+ TestSetValuesConfigChangedData config_changed_data = {
+ .changes = NM_CONFIG_CHANGE_NONE,
+ .check_state_fcn = check_state_fcn,
+ };
+ NMConfigChangeFlags expected_changes = NM_CONFIG_CHANGE_NONE;
+ gs_unref_object NMConfigData *config_data_before = NULL;
+
+ config_data_before = g_object_ref (nm_config_get_data (config));
+
+ keyfile_intern = nm_config_data_clone_keyfile_intern (config_data_before);
+
+ if (set_fcn)
+ set_fcn (config, FALSE, keyfile_intern, &expected_changes);
+
+ g_signal_connect (G_OBJECT (config),
+ NM_CONFIG_SIGNAL_CONFIG_CHANGED,
+ G_CALLBACK (_set_values_config_changed_cb),
+ &config_changed_data);
+
+ if (expected_changes != NM_CONFIG_CHANGE_NONE)
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: update *");
+
+ nm_config_set_values (config, keyfile_intern, TRUE, FALSE);
+
+ g_test_assert_expected_messages ();
+
+ g_assert (expected_changes == config_changed_data.changes);
+
+ if (check_state_fcn)
+ check_state_fcn (config, nm_config_get_data (config), FALSE, NM_CONFIG_CHANGE_NONE, config_data_before);
+
+ g_signal_handlers_disconnect_by_func (config, _set_values_config_changed_cb, &config_changed_data);
+
+ g_key_file_unref (keyfile_intern);
+}
+
+static void
+_set_values_user_intern_section_set (NMConfig *config, gboolean set_user, GKeyFile *keyfile, NMConfigChangeFlags *out_expected_changes)
+{
+ g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN"section1", "key", "this-should-be-ignored");
+}
+
+static void
+_set_values_user_intern_section_check (NMConfig *config, NMConfigData *config_data, gboolean is_change_event, NMConfigChangeFlags changes, NMConfigData *old_data)
+{
+ g_assert (changes == NM_CONFIG_CHANGE_NONE);
+ g_assert (!nm_config_data_has_group (config_data, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN"section1"));
+}
+
+static void
+_set_values_user_initial_values_set (NMConfig *config, gboolean set_user, GKeyFile *keyfile, NMConfigChangeFlags *out_expected_changes)
+{
+ g_key_file_remove_group (keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN"section1", NULL);
+ g_key_file_set_string (keyfile, "section1", "key1", "value1");
+ *out_expected_changes = NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER;
+}
+
+static void
+_set_values_user_initial_values_check (NMConfig *config, NMConfigData *config_data, gboolean is_change_event, NMConfigChangeFlags changes, NMConfigData *old_data)
+{
+ if (is_change_event)
+ g_assert (changes == (NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER));
+ assert_config_value (config_data, "section1", "key1", "value1");
+}
+
+static void
+_set_values_intern_internal_set (NMConfig *config, gboolean set_user, GKeyFile *keyfile, NMConfigChangeFlags *out_expected_changes)
+{
+ g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN"section1", "key", "internal-section");
+ *out_expected_changes = NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN;
+}
+
+static void
+_set_values_intern_internal_check (NMConfig *config, NMConfigData *config_data, gboolean is_change_event, NMConfigChangeFlags changes, NMConfigData *old_data)
+{
+ if (is_change_event)
+ g_assert (changes == (NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN));
+ assert_config_value (config_data, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN"section1", "key", "internal-section");
+}
+
+static void
+_set_values_user_atomic_section_1_set (NMConfig *config, gboolean set_user, GKeyFile *keyfile, NMConfigChangeFlags *out_expected_changes)
+{
+ g_key_file_set_string (keyfile, "atomic-prefix-1.section-a", "key1", "user-value1");
+ g_key_file_set_string (keyfile, "atomic-prefix-1.section-a", "key2", "user-value2");
+ g_key_file_set_string (keyfile, "atomic-prefix-1.section-b", "key1", "user-value1");
+ g_key_file_set_string (keyfile, "non-atomic-prefix-1.section-a", "nap1-key1", "user-value1");
+ g_key_file_set_string (keyfile, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2");
+ *out_expected_changes = NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER;
+}
+
+static void
+_set_values_user_atomic_section_1_check (NMConfig *config, NMConfigData *config_data, gboolean is_change_event, NMConfigChangeFlags changes, NMConfigData *old_data)
+{
+ if (is_change_event)
+ g_assert (changes == (NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER));
+ assert_config_value (config_data, "atomic-prefix-1.section-a", "key1", "user-value1");
+ assert_config_value (config_data, "atomic-prefix-1.section-a", "key2", "user-value2");
+ assert_config_value (config_data, "atomic-prefix-1.section-b", "key1", "user-value1");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key1", "user-value1");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2");
+}
+
+static void
+_set_values_intern_atomic_section_1_set (NMConfig *config, gboolean set_user, GKeyFile *keyfile, NMConfigChangeFlags *out_expected_changes)
+{
+ g_key_file_set_string (keyfile, "atomic-prefix-1.section-a", "key1", "intern-value1");
+ g_key_file_set_string (keyfile, "atomic-prefix-1.section-a", "key3", "intern-value3");
+ g_key_file_set_string (keyfile, "non-atomic-prefix-1.section-a", "nap1-key1", "intern-value1");
+ g_key_file_set_string (keyfile, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3");
+ *out_expected_changes = NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN;
+}
+
+static void
+_set_values_intern_atomic_section_1_check (NMConfig *config, NMConfigData *config_data, gboolean is_change_event, NMConfigChangeFlags changes, NMConfigData *old_data)
+{
+ if (is_change_event)
+ g_assert (changes == (NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN));
+ assert_config_value (config_data, "atomic-prefix-1.section-a", "key1", "intern-value1");
+ assert_config_value (config_data, "atomic-prefix-1.section-a", "key2", NULL);
+ assert_config_value (config_data, "atomic-prefix-1.section-a", "key3", "intern-value3");
+ assert_config_value (config_data, "atomic-prefix-1.section-b", "key1", "user-value1");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key1", "intern-value1");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3");
+ g_assert ( nm_config_data_is_intern_atomic_group (config_data, "atomic-prefix-1.section-a"));
+ g_assert (!nm_config_data_is_intern_atomic_group (config_data, "atomic-prefix-1.section-b"));
+ g_assert (!nm_config_data_is_intern_atomic_group (config_data, "non-atomic-prefix-1.section-a"));
+}
+
+static void
+_set_values_user_atomic_section_2_set (NMConfig *config, gboolean set_user, GKeyFile *keyfile, NMConfigChangeFlags *out_expected_changes)
+{
+ g_key_file_set_string (keyfile, "atomic-prefix-1.section-a", "key1", "user-value1-x");
+ g_key_file_set_string (keyfile, "atomic-prefix-1.section-a", "key2", "user-value2");
+ g_key_file_set_string (keyfile, "non-atomic-prefix-1.section-a", "nap1-key1", "user-value1-x");
+ g_key_file_set_string (keyfile, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2-x");
+ *out_expected_changes = NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER | NM_CONFIG_CHANGE_VALUES_INTERN;
+}
+
+static void
+_set_values_user_atomic_section_2_check (NMConfig *config, NMConfigData *config_data, gboolean is_change_event, NMConfigChangeFlags changes, NMConfigData *old_data)
+{
+ if (is_change_event)
+ g_assert (changes == (NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER | NM_CONFIG_CHANGE_VALUES_INTERN));
+ assert_config_value (config_data, "atomic-prefix-1.section-a", "key1", "user-value1-x");
+ assert_config_value (config_data, "atomic-prefix-1.section-a", "key2", "user-value2");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key1", "user-value1-x");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2-x");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3");
+ g_assert (!nm_config_data_is_intern_atomic_group (config_data, "atomic-prefix-1.section-a"));
+ g_assert (!nm_config_data_is_intern_atomic_group (config_data, "atomic-prefix-1.section-b"));
+ g_assert (!nm_config_data_is_intern_atomic_group (config_data, "non-atomic-prefix-1.section-a"));
+}
+
+static void
+_set_values_intern_atomic_section_2_set (NMConfig *config, gboolean set_user, GKeyFile *keyfile, NMConfigChangeFlags *out_expected_changes)
+{
+ /* let's hide an atomic section and one key. */
+ g_key_file_set_string (keyfile, "atomic-prefix-1.section-a", NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, "any-value");
+ g_key_file_set_string (keyfile, "non-atomic-prefix-1.section-a", NM_CONFIG_KEYFILE_KEYPREFIX_WAS"nap1-key1", "any-value");
+ g_key_file_set_string (keyfile, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3");
+ g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN"with-whitespace", "key1", " b c\\, d ");
+ g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN"with-whitespace", "key2", " b c\\, d ");
+ *out_expected_changes = NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN;
+}
+
+static void
+_set_values_intern_atomic_section_2_check (NMConfig *config, NMConfigData *config_data, gboolean is_change_event, NMConfigChangeFlags changes, NMConfigData *old_data)
+{
+ if (is_change_event)
+ g_assert (changes == (NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN));
+ g_assert (!nm_config_data_has_group (config_data, "atomic-prefix-1.section-a"));
+ assert_config_value (config_data, "atomic-prefix-1.section-b", "key1", "user-value1");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key1", NULL);
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2-x");
+ assert_config_value (config_data, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3");
+ g_assert (!nm_config_data_is_intern_atomic_group (config_data, "atomic-prefix-1.section-a"));
+ g_assert (!nm_config_data_is_intern_atomic_group (config_data, "atomic-prefix-1.section-b"));
+ g_assert (!nm_config_data_is_intern_atomic_group (config_data, "non-atomic-prefix-1.section-a"));
+ assert_config_value (config_data, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN"with-whitespace", "key1", " b c\\, d ");
+ assert_config_value (config_data, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN"with-whitespace", "key2", " b c\\, d ");
+}
+
+static void
+test_config_set_values (void)
+{
+ gs_unref_object NMConfig *config = NULL;
+ const char *CONFIG_USER = SRCDIR"/test-set-values-user.conf";
+ const char *CONFIG_INTERN = SRCDIR"/test-set-values-intern.conf";
+ const char *atomic_section_prefixes[] = {
+ "atomic-prefix-1.",
+ "atomic-prefix-2.",
+ NULL,
+ };
+
+ g_assert (g_file_set_contents (CONFIG_USER, "", 0, NULL));
+ g_assert (g_file_set_contents (CONFIG_INTERN, "", 0, NULL));
+
+ config = setup_config (NULL, CONFIG_USER, CONFIG_INTERN, atomic_section_prefixes, "", "", NULL);
+
+ _set_values_user (config, CONFIG_USER,
+ _set_values_user_intern_section_set,
+ _set_values_user_intern_section_check);
+
+ _set_values_user (config, CONFIG_USER,
+ _set_values_user_initial_values_set,
+ _set_values_user_initial_values_check);
+
+ _set_values_intern (config,
+ _set_values_intern_internal_set,
+ _set_values_intern_internal_check);
+
+ _set_values_user (config, CONFIG_USER,
+ _set_values_user_atomic_section_1_set,
+ _set_values_user_atomic_section_1_check);
+
+ _set_values_intern (config,
+ _set_values_intern_atomic_section_1_set,
+ _set_values_intern_atomic_section_1_check);
+
+ _set_values_user (config, CONFIG_USER,
+ _set_values_user_atomic_section_2_set,
+ _set_values_user_atomic_section_2_check);
+
+ _set_values_intern (config,
+ _set_values_intern_atomic_section_2_set,
+ _set_values_intern_atomic_section_2_check);
+
+ g_assert (remove (CONFIG_USER) == 0);
+ g_assert (remove (CONFIG_INTERN) == 0);
+}
+
+/*****************************************************************************/
+
NMTST_DEFINE ();
int
@@ -419,6 +771,8 @@ main (int argc, char **argv)
g_test_add_func ("/config/confdir", test_config_confdir);
g_test_add_func ("/config/confdir-parse-error", test_config_confdir_parse_error);
+ g_test_add_func ("/config/set-values", test_config_set_values);
+
/* This one has to come last, because it leaves its values in
* nm-config.c's global variables, and there's no way to reset
* those to NULL.