Commit 0186330a authored by Dan Winship's avatar Dan Winship
Browse files

settings: use NMConfig directly rather than reparsing NetworkManager.conf

Add some new API to NMConfig so that NMSettings and its plugins can
use NMConfig to look up values rather than reparsing the config file
themselves.

Also, move the no-auto-default cache from NetworkManager.conf to
$NMSTATEDIR/no-auto-default.state, so NM isn't rewriting its own
config file at runtime.
parent 57b0ed41
......@@ -14,7 +14,8 @@ libnm_config_la_CPPFLAGS = \
-I$(top_srcdir)/libnm-util \
-I$(top_builddir)/libnm-util \
-I$(top_srcdir)/src/logging \
-DNMCONFDIR=\"$(nmconfdir)\"
-DNMCONFDIR=\"$(nmconfdir)\" \
-DNMSTATEDIR=\"$(nmstatedir)\"
libnm_config_la_LIBADD = \
$(top_builddir)/libnm-util/libnm-util.la \
......
......@@ -25,14 +25,19 @@
#include "nm-config.h"
#include "nm-logging.h"
#include "nm-utils.h"
#include <glib/gi18n.h>
#define NM_DEFAULT_SYSTEM_CONF_FILE NMCONFDIR "/NetworkManager.conf"
#define NM_OLD_SYSTEM_CONF_FILE NMCONFDIR "/nm-system-settings.conf"
#define NM_DEFAULT_SYSTEM_CONF_FILE NMCONFDIR "/NetworkManager.conf"
#define NM_OLD_SYSTEM_CONF_FILE NMCONFDIR "/nm-system-settings.conf"
#define NM_NO_AUTO_DEFAULT_STATE_FILE NMSTATEDIR "/no-auto-default.state"
typedef struct {
char *path;
char *no_auto_default_file;
GKeyFile *keyfile;
char **plugins;
char *dhcp_client;
char **dns_plugins;
......@@ -41,6 +46,7 @@ typedef struct {
char *connectivity_uri;
gint connectivity_interval;
char *connectivity_response;
char **no_auto_default;
} NMConfigPrivate;
static NMConfig *singleton = NULL;
......@@ -126,9 +132,101 @@ nm_config_get_connectivity_response (NMConfig *config)
return NM_CONFIG_GET_PRIVATE (config)->connectivity_response;
}
char *
nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error)
{
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
return g_key_file_get_string (priv->keyfile, group, key, error);
}
/************************************************************************/
static void
merge_no_auto_default_state (NMConfig *config)
{
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
GPtrArray *updated;
char **list;
int i, j;
char *data;
/* If the config already matches everything, we don't need to do anything else. */
if (priv->no_auto_default && !g_strcmp0 (priv->no_auto_default[0], "*"))
return;
updated = g_ptr_array_new ();
if (priv->no_auto_default) {
for (i = 0; priv->no_auto_default[i]; i++)
g_ptr_array_add (updated, priv->no_auto_default[i]);
g_free (priv->no_auto_default);
}
if (g_file_get_contents (priv->no_auto_default_file, &data, NULL, NULL)) {
list = g_strsplit (data, "\n", -1);
for (i = 0; list[i]; i++) {
if (!*list[i])
continue;
for (j = 0; j < updated->len; j++) {
if (!strcmp (list[i], updated->pdata[j]))
break;
}
if (j == updated->len)
g_ptr_array_add (updated, list[i]);
}
g_free (list);
g_free (data);
}
g_ptr_array_add (updated, NULL);
priv->no_auto_default = (char **) g_ptr_array_free (updated, FALSE);
}
gboolean
nm_config_get_ethernet_can_auto_default (NMConfig *config, NMConfigDevice *device)
{
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
return !nm_config_device_spec_match_list (device, (const char **) priv->no_auto_default);
}
void
nm_config_set_ethernet_no_auto_default (NMConfig *config, NMConfigDevice *device)
{
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
char *current;
GString *updated;
GError *error = NULL;
if (!nm_config_get_ethernet_can_auto_default (config, device))
return;
updated = g_string_new (NULL);
if (g_file_get_contents (priv->no_auto_default_file, &current, NULL, NULL)) {
g_string_append (updated, current);
g_free (current);
if (updated->str[updated->len - 1] != '\n')
g_string_append_c (updated, '\n');
}
g_string_append (updated, nm_config_device_get_hwaddr (device));
g_string_append_c (updated, '\n');
if (!g_file_set_contents (priv->no_auto_default_file, updated->str, updated->len, &error)) {
nm_log_warn (LOGD_SETTINGS, "Could not update no-auto-default.state file: %s",
error->message);
g_error_free (error);
}
g_string_free (updated, TRUE);
merge_no_auto_default_state (config);
}
/************************************************************************/
static char *cli_config_path;
static char *cli_no_auto_default_file;
static char *cli_plugins;
static char *cli_log_level;
static char *cli_log_domains;
......@@ -138,6 +236,7 @@ static char *cli_connectivity_response;
static GOptionEntry config_options[] = {
{ "config", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_path, N_("Config file location"), N_("/path/to/config.file") },
{ "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli_no_auto_default_file, "no-auto-default.state location", NULL },
{ "plugins", 0, 0, G_OPTION_ARG_STRING, &cli_plugins, N_("List of plugins separated by ','"), N_("plugin1,plugin2") },
{ "log-level", 0, 0, G_OPTION_ARG_STRING, &cli_log_level, N_("Log level: one of [%s]"), "INFO" },
{ "log-domains", 0, 0, G_OPTION_ARG_STRING, &cli_log_domains,
......@@ -216,10 +315,14 @@ read_config (NMConfig *config, const char *path, GError **error)
if (!priv->connectivity_response)
priv->connectivity_response = g_key_file_get_value (kf, "connectivity", "response", NULL);
if (!priv->no_auto_default)
priv->no_auto_default = g_key_file_get_string_list (kf, "main", "no-auto-default", NULL, NULL);
priv->keyfile = kf;
success = TRUE;
}
} else
g_key_file_free (kf);
g_key_file_free (kf);
return success;
}
......@@ -268,8 +371,9 @@ nm_config_new (GError **error)
if (!read_config (singleton, cli_config_path, error)) {
g_object_unref (singleton);
singleton = NULL;
return NULL;
}
return singleton;
goto got_config;
}
/* Even though we prefer NetworkManager.conf, we need to check the
......@@ -281,7 +385,7 @@ nm_config_new (GError **error)
/* Try deprecated nm-system-settings.conf first */
if (read_config (singleton, NM_OLD_SYSTEM_CONF_FILE, &local))
return singleton;
goto got_config;
if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND) == FALSE) {
fprintf (stderr, "Default config file %s invalid: (%d) %s\n",
......@@ -293,7 +397,7 @@ nm_config_new (GError **error)
/* Try the standard config file location next */
if (read_config (singleton, NM_DEFAULT_SYSTEM_CONF_FILE, &local))
return singleton;
goto got_config;
if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND) == FALSE) {
fprintf (stderr, "Default config file %s invalid: (%d) %s\n",
......@@ -316,6 +420,15 @@ nm_config_new (GError **error)
/* ignore error if config file not found */
g_clear_error (&local);
got_config:
/* Handle no-auto-default state file */
if (cli_no_auto_default_file)
priv->no_auto_default_file = g_strdup (cli_no_auto_default_file);
else
priv->no_auto_default_file = g_strdup (NM_NO_AUTO_DEFAULT_STATE_FILE);
merge_no_auto_default_state (singleton);
return singleton;
}
......@@ -331,6 +444,7 @@ finalize (GObject *gobject)
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject);
g_free (priv->path);
g_clear_pointer (&priv->keyfile, g_key_file_unref);
g_strfreev (priv->plugins);
g_free (priv->dhcp_client);
g_strfreev (priv->dns_plugins);
......
......@@ -25,6 +25,8 @@
#include <glib.h>
#include <glib-object.h>
#include "nm-config-device.h"
G_BEGIN_DECLS
#define NM_TYPE_CONFIG (nm_config_get_type ())
......@@ -56,6 +58,11 @@ const char *nm_config_get_connectivity_uri (NMConfig *config);
const guint nm_config_get_connectivity_interval (NMConfig *config);
const char *nm_config_get_connectivity_response (NMConfig *config);
gboolean nm_config_get_ethernet_can_auto_default (NMConfig *config, NMConfigDevice *device);
void nm_config_set_ethernet_no_auto_default (NMConfig *config, NMConfigDevice *device);
char *nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error);
/* for main.c only */
GOptionEntry *nm_config_get_options (void);
NMConfig *nm_config_new (GError **error);
......
if ENABLE_TESTS
INCLUDES = \
-I$(top_srcdir)/include \
-I$(top_srcdir)/libnm-util \
-I$(top_builddir)/libnm-util \
-I$(top_srcdir)/src/config
noinst_PROGRAMS = \
test-config
test_config_SOURCES = \
nm-test-device.c \
nm-test-device.h \
test-config.c
test_config_CPPFLAGS = \
-DSRCDIR=\""$(srcdir)"\" \
$(GLIB_CFLAGS)
......
[main]
dhcp=dhclient
plugins=foo,bar,baz
no-auto-default=11:11:11:11:11:11
[logging]
level=INFO
......
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright 2013 Red Hat, Inc.
*/
#include "config.h"
#include <string.h>
#include <netinet/ether.h>
#include "nm-test-device.h"
#include "nm-config-device.h"
#include "nm-utils.h"
static void nm_test_device_config_device_interface_init (NMConfigDeviceInterface *iface);
G_DEFINE_TYPE_WITH_CODE (NMTestDevice, nm_test_device, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (NM_TYPE_CONFIG_DEVICE, nm_test_device_config_device_interface_init))
static void
nm_test_device_init (NMTestDevice *self)
{
}
static void
finalize (GObject *object)
{
NMTestDevice *self = NM_TEST_DEVICE (object);
g_free (self->hwaddr);
g_free (self->hwaddr_bytes);
G_OBJECT_CLASS (nm_test_device_parent_class)->finalize (object);
}
static void
nm_test_device_class_init (NMTestDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = finalize;
}
static gboolean
spec_match_list (NMConfigDevice *device, const GSList *specs)
{
NMTestDevice *self = NM_TEST_DEVICE (device);
const GSList *iter;
const char *spec;
for (iter = specs; iter; iter = iter->next) {
spec = iter->data;
if (g_str_has_prefix (spec, "mac:") && !strcmp (spec + 4, self->hwaddr))
return TRUE;
}
return FALSE;
}
static const guint8 *
get_hw_address (NMConfigDevice *device, guint *out_len)
{
NMTestDevice *self = NM_TEST_DEVICE (device);
if (out_len)
*out_len = ETH_ALEN;
return self->hwaddr_bytes;
}
static void
nm_test_device_config_device_interface_init (NMConfigDeviceInterface *iface)
{
iface->spec_match_list = spec_match_list;
iface->get_hw_address = get_hw_address;
}
NMTestDevice *
nm_test_device_new (const char *hwaddr)
{
NMTestDevice *self = g_object_new (NM_TYPE_TEST_DEVICE, NULL);
self->hwaddr = g_strdup (hwaddr);
self->hwaddr_bytes = g_malloc (ETH_ALEN);
nm_utils_hwaddr_aton (hwaddr, ARPHRD_ETHER, self->hwaddr_bytes);
return self;
}
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright 2013 Red Hat, Inc.
*/
#ifndef NM_TEST_DEVICE_H
#define NM_TEST_DEVICE_H
#include <glib-object.h>
G_BEGIN_DECLS
#define NM_TYPE_TEST_DEVICE (nm_test_device_get_type ())
#define NM_TEST_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TEST_DEVICE, NMTestDevice))
#define NM_TEST_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_TEST_DEVICE, NMTestDeviceClass))
#define NM_IS_TEST_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_TEST_DEVICE))
#define NM_IS_TEST_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_TEST_DEVICE))
#define NM_TEST_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_TEST_DEVICE, NMTestDeviceClass))
typedef struct {
GObject parent;
char *hwaddr;
guint8 *hwaddr_bytes;
} NMTestDevice;
typedef struct {
GObjectClass parent;
} NMTestDeviceClass;
GType nm_test_device_get_type (void);
NMTestDevice *nm_test_device_new (const char *hwaddr);
G_END_DECLS
#endif /* NM_DEVICE_H */
......@@ -18,9 +18,14 @@
*
*/
#include "config.h"
#include <unistd.h>
#include <glib.h>
#include <nm-config.h>
#include "nm-test-device.h"
static void
setup_config (const char *config_file, ...)
......@@ -58,6 +63,7 @@ test_config_simple (void)
NMConfig *config;
GError *error = NULL;
const char **plugins;
char *value;
setup_config (SRCDIR "/NetworkManager.conf", NULL);
config = nm_config_new (&error);
......@@ -74,6 +80,18 @@ test_config_simple (void)
g_assert_cmpstr (plugins[1], ==, "bar");
g_assert_cmpstr (plugins[2], ==, "baz");
value = nm_config_get_value (config, "extra-section", "extra-key", NULL);
g_assert_cmpstr (value, ==, "some value");
g_free (value);
value = nm_config_get_value (config, "extra-section", "no-key", &error);
g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND);
g_clear_error (&error);
value = nm_config_get_value (config, "no-section", "no-key", &error);
g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND);
g_clear_error (&error);
g_object_unref (config);
}
......@@ -106,7 +124,7 @@ test_config_override (void)
GError *error = NULL;
const char **plugins;
setup_config (SRCDIR "/NetworkManager.conf",
setup_config (SRCDIR "/NetworkManager.conf", "/no/such/dir",
"--plugins", "alpha,beta,gamma,delta",
"--connectivity-interval", "12",
NULL);
......@@ -128,6 +146,67 @@ test_config_override (void)
g_object_unref (config);
}
static void
test_config_no_auto_default (void)
{
NMConfig *config;
GError *error = NULL;
int fd, nwrote;
char *state_file;
NMTestDevice *dev1, *dev2, *dev3, *dev4;
fd = g_file_open_tmp (NULL, &state_file, &error);
g_assert_no_error (error);
nwrote = write (fd, "22:22:22:22:22:22\n", 18);
g_assert_cmpint (nwrote, ==, 18);
nwrote = write (fd, "44:44:44:44:44:44\n", 18);
g_assert_cmpint (nwrote, ==, 18);
close (fd);
setup_config (SRCDIR "/NetworkManager.conf",
"--no-auto-default", state_file,
NULL);
config = nm_config_new (&error);
g_assert_no_error (error);
dev1 = nm_test_device_new ("11:11:11:11:11:11");
dev2 = nm_test_device_new ("22:22:22:22:22:22");
dev3 = nm_test_device_new ("33:33:33:33:33:33");
dev4 = nm_test_device_new ("44:44:44:44:44:44");
g_assert (!nm_config_get_ethernet_can_auto_default (config, NM_CONFIG_DEVICE (dev1)));
g_assert (!nm_config_get_ethernet_can_auto_default (config, NM_CONFIG_DEVICE (dev2)));
g_assert (nm_config_get_ethernet_can_auto_default (config, NM_CONFIG_DEVICE (dev3)));
g_assert (!nm_config_get_ethernet_can_auto_default (config, NM_CONFIG_DEVICE (dev4)));
nm_config_set_ethernet_no_auto_default (config, NM_CONFIG_DEVICE (dev3));
g_assert (!nm_config_get_ethernet_can_auto_default (config, NM_CONFIG_DEVICE (dev3)));
g_object_unref (config);
setup_config (SRCDIR "/NetworkManager.conf",
"--no-auto-default", state_file,
NULL);
config = nm_config_new (&error);
g_assert_no_error (error);
g_assert (!nm_config_get_ethernet_can_auto_default (config, NM_CONFIG_DEVICE (dev1)));
g_assert (!nm_config_get_ethernet_can_auto_default (config, NM_CONFIG_DEVICE (dev2)));
g_assert (!nm_config_get_ethernet_can_auto_default (config, NM_CONFIG_DEVICE (dev3)));
g_assert (!nm_config_get_ethernet_can_auto_default (config, NM_CONFIG_DEVICE (dev4)));
g_object_unref (config);
g_object_unref (dev1);
g_object_unref (dev2);
g_object_unref (dev3);
g_object_unref (dev4);
unlink (state_file);
g_free (state_file);
}
int
main (int argc, char **argv)
{
......@@ -137,6 +216,7 @@ main (int argc, char **argv)
g_test_add_func ("/config/simple", test_config_simple);
g_test_add_func ("/config/non-existent", test_config_non_existent);
g_test_add_func ("/config/parse-error", test_config_parse_error);
g_test_add_func ("/config/no-auto-default", test_config_no_auto_default);
/* 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
......
......@@ -28,7 +28,6 @@
#include <unistd.h>
#include <string.h>
#include <gmodule.h>
#include <net/if_arp.h>
#include <pwd.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
......@@ -71,8 +70,6 @@
#include "nm-connection-provider.h"
#include "nm-config.h"
#define CONFIG_KEY_NO_AUTO_DEFAULT "no-auto-default"
/* LINKER CRACKROCK */
#define EXPORT(sym) void * __export_##sym = &sym;
......@@ -1305,7 +1302,7 @@ impl_settings_save_hostname (NMSettings *self,
}
static gboolean
have_connection_for_device (NMSettings *self, GByteArray *mac, NMDevice *device)
have_connection_for_device (NMSettings *self, NMDevice *device)
{
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
GHashTableIter iter;
......@@ -1313,10 +1310,13 @@ have_connection_for_device (NMSettings *self, GByteArray *mac, NMDevice *device)
NMSettingConnection *s_con;
NMSettingWired *s_wired;
const GByteArray *setting_mac;
const guint8 *hwaddr;
guint hwaddr_len = 0;
gboolean ret = FALSE;
g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
g_return_val_if_fail (mac != NULL, FALSE);
hwaddr = nm_device_get_hw_address (device, &hwaddr_len);
/* Find a wired connection locked to the given MAC address, if any */
g_hash_table_iter_init (&iter, priv->connections);
......@@ -1353,8 +1353,8 @@ have_connection_for_device (NMSettings *self, GByteArray *mac, NMDevice *device)
setting_mac = nm_setting_wired_get_mac_address (s_wired);
if (setting_mac) {
/* A connection mac-locked to this device */
if (mac->len == setting_mac->len &&
!memcmp (setting_mac->data, mac->data, mac->len)) {
if (hwaddr_len == setting_mac->len &&
!memcmp (setting_mac->data, hwaddr, hwaddr_len)) {
ret = TRUE;
break;
}
......@@ -1368,54 +1368,6 @@ have_connection_for_device (NMSettings *self, GByteArray *mac, NMDevice *device)
return ret;
}
/* Search through the list of blacklisted MAC addresses in the config file. */
static gboolean
is_mac_auto_wired_blacklisted (NMSettings *self, const GByteArray *mac)
{
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
const char *config_file;
GKeyFile *config;
char **list, **iter;
gboolean found = FALSE;
int hwaddr_type;
g_return_val_if_fail (mac != NULL, FALSE);
config_file = nm_config_get_path (priv->config);
if (!config_file)
return FALSE;
config = g_key_file_new ();
g_key_file_set_list_separator (config, ',');