From 2e5fa45ddfbb5cffa1e78221f1cea706e2f298af Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Wed, 31 Jul 2019 10:51:42 +0200 Subject: New upstream version 1.19.90 --- src/settings/plugins/ibft/meson.build | 41 - src/settings/plugins/ibft/nms-ibft-connection.c | 77 - src/settings/plugins/ibft/nms-ibft-connection.h | 41 - src/settings/plugins/ibft/nms-ibft-plugin.c | 155 -- src/settings/plugins/ibft/nms-ibft-plugin.h | 36 - src/settings/plugins/ibft/nms-ibft-reader.c | 535 ---- src/settings/plugins/ibft/nms-ibft-reader.h | 42 - .../plugins/ibft/tests/iscsiadm-test-bad-dns1 | 21 - .../plugins/ibft/tests/iscsiadm-test-bad-dns2 | 21 - .../plugins/ibft/tests/iscsiadm-test-bad-entry | 20 - .../plugins/ibft/tests/iscsiadm-test-bad-gateway | 21 - .../plugins/ibft/tests/iscsiadm-test-bad-ipaddr | 21 - .../plugins/ibft/tests/iscsiadm-test-bad-record | 18 - src/settings/plugins/ibft/tests/iscsiadm-test-dhcp | 33 - .../plugins/ibft/tests/iscsiadm-test-static | 35 - src/settings/plugins/ibft/tests/iscsiadm-test-vlan | 19 - src/settings/plugins/ibft/tests/meson.build | 16 - src/settings/plugins/ibft/tests/test-ibft.c | 292 --- src/settings/plugins/ifcfg-rh/meson.build | 3 +- src/settings/plugins/ifcfg-rh/nm-inotify-helper.c | 213 -- src/settings/plugins/ifcfg-rh/nm-inotify-helper.h | 59 - .../plugins/ifcfg-rh/nms-ifcfg-rh-common.h | 1 - .../plugins/ifcfg-rh/nms-ifcfg-rh-connection.c | 511 ---- .../plugins/ifcfg-rh/nms-ifcfg-rh-connection.h | 54 - .../plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c | 1374 +++++----- .../plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h | 25 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 428 ++-- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.h | 9 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-storage.c | 198 ++ .../plugins/ifcfg-rh/nms-ifcfg-rh-storage.h | 93 + src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c | 52 +- src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h | 5 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 262 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.h | 7 +- src/settings/plugins/ifcfg-rh/shvar.c | 1 - src/settings/plugins/ifcfg-rh/shvar.h | 1 - .../ifcfg-Test_Write_Bond_Main.cexpected | 1 + .../ifcfg-Test_Write_VLAN_reorder_hdr.cexpected | 1 + .../ifcfg-Vlan_test-vlan-interface.cexpected | 1 + .../ifcfg-team-slave-enp31s0f1-142.cexpected | 1 + .../ifcfg-test-ip6-disabled.cexpected | 11 + .../ifcfg-test-wired-8021x-tls-p12-no-client-cert | 13 + .../ifcfg-rh/tests/network-scripts/test_client.p12 | Bin 0 -> 2848 bytes .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 122 +- src/settings/plugins/ifupdown/meson.build | 1 - .../plugins/ifupdown/nms-ifupdown-connection.c | 106 - .../plugins/ifupdown/nms-ifupdown-connection.h | 45 - .../ifupdown/nms-ifupdown-interface-parser.c | 1 - .../ifupdown/nms-ifupdown-interface-parser.h | 1 - .../plugins/ifupdown/nms-ifupdown-parser.c | 43 +- .../plugins/ifupdown/nms-ifupdown-parser.h | 16 +- .../plugins/ifupdown/nms-ifupdown-plugin.c | 358 ++- .../plugins/ifupdown/nms-ifupdown-plugin.h | 26 +- .../plugins/ifupdown/tests/test-ifupdown.c | 77 +- .../plugins/keyfile/nms-keyfile-connection.c | 186 -- .../plugins/keyfile/nms-keyfile-connection.h | 44 - src/settings/plugins/keyfile/nms-keyfile-plugin.c | 1502 ++++++++--- src/settings/plugins/keyfile/nms-keyfile-plugin.h | 38 +- src/settings/plugins/keyfile/nms-keyfile-reader.c | 31 +- src/settings/plugins/keyfile/nms-keyfile-reader.h | 8 +- src/settings/plugins/keyfile/nms-keyfile-storage.c | 277 ++ src/settings/plugins/keyfile/nms-keyfile-storage.h | 264 ++ src/settings/plugins/keyfile/nms-keyfile-utils.c | 282 +- src/settings/plugins/keyfile/nms-keyfile-utils.h | 75 +- src/settings/plugins/keyfile/nms-keyfile-writer.c | 173 +- src/settings/plugins/keyfile/nms-keyfile-writer.h | 14 +- src/settings/plugins/keyfile/tests/meson.build | 6 +- .../plugins/keyfile/tests/test-keyfile-settings.c | 2706 ++++++++++++++++++++ src/settings/plugins/keyfile/tests/test-keyfile.c | 2703 ------------------- src/settings/plugins/meson.build | 4 - 70 files changed, 6668 insertions(+), 7209 deletions(-) delete mode 100644 src/settings/plugins/ibft/meson.build delete mode 100644 src/settings/plugins/ibft/nms-ibft-connection.c delete mode 100644 src/settings/plugins/ibft/nms-ibft-connection.h delete mode 100644 src/settings/plugins/ibft/nms-ibft-plugin.c delete mode 100644 src/settings/plugins/ibft/nms-ibft-plugin.h delete mode 100644 src/settings/plugins/ibft/nms-ibft-reader.c delete mode 100644 src/settings/plugins/ibft/nms-ibft-reader.h delete mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 delete mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 delete mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry delete mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway delete mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr delete mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-bad-record delete mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-dhcp delete mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-static delete mode 100755 src/settings/plugins/ibft/tests/iscsiadm-test-vlan delete mode 100644 src/settings/plugins/ibft/tests/meson.build delete mode 100644 src/settings/plugins/ibft/tests/test-ibft.c delete mode 100644 src/settings/plugins/ifcfg-rh/nm-inotify-helper.c delete mode 100644 src/settings/plugins/ifcfg-rh/nm-inotify-helper.h delete mode 100644 src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c delete mode 100644 src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h create mode 100644 src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c create mode 100644 src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 delete mode 100644 src/settings/plugins/ifupdown/nms-ifupdown-connection.c delete mode 100644 src/settings/plugins/ifupdown/nms-ifupdown-connection.h delete mode 100644 src/settings/plugins/keyfile/nms-keyfile-connection.c delete mode 100644 src/settings/plugins/keyfile/nms-keyfile-connection.h create mode 100644 src/settings/plugins/keyfile/nms-keyfile-storage.c create mode 100644 src/settings/plugins/keyfile/nms-keyfile-storage.h create mode 100644 src/settings/plugins/keyfile/tests/test-keyfile-settings.c delete mode 100644 src/settings/plugins/keyfile/tests/test-keyfile.c (limited to 'src/settings/plugins') diff --git a/src/settings/plugins/ibft/meson.build b/src/settings/plugins/ibft/meson.build deleted file mode 100644 index c33b24cc..00000000 --- a/src/settings/plugins/ibft/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -name = 'nm-settings-plugin-ibft' - -libnms_ibft_core = static_library( - 'nms-ibft-core', - 'nms-ibft-reader.c', - dependencies: nm_dep, -) - -sources = files( - 'nms-ibft-connection.c', - 'nms-ibft-plugin.c', -) - -libnm_settings_plugin_ibft = shared_module( - name, - sources: sources, - dependencies: nm_dep, - link_with: libnms_ibft_core, - link_args: ldflags_linker_script_settings, - link_depends: linker_script_settings, - install: true, - install_dir: nm_plugindir, -) - -core_plugins += libnm_settings_plugin_ibft - -# FIXME: check_so_symbols replacement -''' -run_target( - 'check-local-symbols-settings-ibft', - command: [check_so_symbols, libnm_settings_plugin_ibft.full_path()], - depends: libnm_settings_plugin_ibft, -) - -check-local-symbols-settings-ibft: src/settings/plugins/ibft/libnm-settings-plugin-ibft.la - $(call check_so_symbols,$(builddir)/src/settings/plugins/ibft/.libs/libnm-settings-plugin-ibft.so) -''' - -if enable_tests - subdir('tests') -endif diff --git a/src/settings/plugins/ibft/nms-ibft-connection.c b/src/settings/plugins/ibft/nms-ibft-connection.c deleted file mode 100644 index a36d8a31..00000000 --- a/src/settings/plugins/ibft/nms-ibft-connection.c +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ibft-connection.h" - -#include -#include -#include - -#include "nms-ibft-reader.h" - -/*****************************************************************************/ - -struct _NMSIbftConnection { - NMSettingsConnection parent; -}; - -struct _NMSIbftConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMSIbftConnection, nms_ibft_connection, NM_TYPE_SETTINGS_CONNECTION) - -/*****************************************************************************/ - -static void -nms_ibft_connection_init (NMSIbftConnection *connection) -{ -} - -NMSIbftConnection * -nms_ibft_connection_new (const GPtrArray *block, GError **error) -{ - NMConnection *source; - GObject *object; - - source = nms_ibft_reader_get_connection_from_block (block, error); - if (!source) - return NULL; - - object = g_object_new (NMS_TYPE_IBFT_CONNECTION, NULL); - /* Update settings with what was read from iscsiadm */ - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object), - source, - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - NULL, - error)) - g_clear_object (&object); - - return (NMSIbftConnection *) object; -} - -static void -nms_ibft_connection_class_init (NMSIbftConnectionClass *ibft_connection_class) -{ -} - diff --git a/src/settings/plugins/ibft/nms-ibft-connection.h b/src/settings/plugins/ibft/nms-ibft-connection.h deleted file mode 100644 index de050662..00000000 --- a/src/settings/plugins/ibft/nms-ibft-connection.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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 2014 Red Hat, Inc. - */ - -#ifndef __NMS_IBFT_CONNECTION_H__ -#define __NMS_IBFT_CONNECTION_H__ - -#include "settings/nm-settings-connection.h" - -#define NMS_TYPE_IBFT_CONNECTION (nms_ibft_connection_get_type ()) -#define NMS_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IBFT_CONNECTION, NMSIbftConnection)) -#define NMS_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IBFT_CONNECTION, NMSIbftConnectionClass)) -#define NMS_IS_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IBFT_CONNECTION)) -#define NMS_IS_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IBFT_CONNECTION)) -#define NMS_IBFT_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IBFT_CONNECTION, NMSIbftConnectionClass)) - -typedef struct _NMSIbftConnection NMSIbftConnection; -typedef struct _NMSIbftConnectionClass NMSIbftConnectionClass; - -GType nms_ibft_connection_get_type (void); - -NMSIbftConnection *nms_ibft_connection_new (const GPtrArray *block, - GError **error); - -#endif /* __NMS_IBFT_CONNECTION_H__ */ diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c deleted file mode 100644 index 47051995..00000000 --- a/src/settings/plugins/ibft/nms-ibft-plugin.c +++ /dev/null @@ -1,155 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ibft-plugin.h" - -#include -#include - -#include "nm-setting-connection.h" -#include "settings/nm-settings-plugin.h" -#include "NetworkManagerUtils.h" - -#include "nms-ibft-reader.h" -#include "nms-ibft-connection.h" - -/*****************************************************************************/ - -typedef struct { - GHashTable *connections; /* uuid::connection */ - gboolean initialized; -} NMSIbftPluginPrivate; - -struct _NMSIbftPlugin { - NMSettingsPlugin parent; - NMSIbftPluginPrivate _priv; -}; - -struct _NMSIbftPluginClass { - NMSettingsPluginClass parent; -}; - -G_DEFINE_TYPE (NMSIbftPlugin, nms_ibft_plugin, NM_TYPE_SETTINGS_PLUGIN); - -#define NMS_IBFT_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIbftPlugin, NMS_IS_IBFT_PLUGIN) - -/*****************************************************************************/ - -static NMSIbftPlugin *nms_ibft_plugin_get (void); - -NM_DEFINE_SINGLETON_GETTER (NMSIbftPlugin, nms_ibft_plugin_get, NMS_TYPE_IBFT_PLUGIN); - -/*****************************************************************************/ - -static void -read_connections (NMSIbftPlugin *self) -{ - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - nm_auto_free_ibft_blocks GSList *blocks = NULL; - GSList *iter; - gs_free_error GError *error = NULL; - NMSIbftConnection *connection; - - if (!nms_ibft_reader_load_blocks ("/sbin/iscsiadm", &blocks, &error)) { - nm_log_dbg (LOGD_SETTINGS, "ibft: failed to read iscsiadm records: %s", error->message); - return; - } - - for (iter = blocks; iter; iter = iter->next) { - connection = nms_ibft_connection_new (iter->data, &error); - if (!connection) { - nm_log_warn (LOGD_SETTINGS, "ibft: failed to read iscsiadm record: %s", error->message); - g_clear_error (&error); - continue; - } - - nm_log_info (LOGD_SETTINGS, "ibft: read connection '%s'", - nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (connection))); - g_hash_table_insert (priv->connections, - g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))), - connection); - } -} - -static GSList * -get_connections (NMSettingsPlugin *config) -{ - NMSIbftPlugin *self = NMS_IBFT_PLUGIN (config); - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - GSList *list = NULL; - GHashTableIter iter; - NMSIbftConnection *connection; - - if (!priv->initialized) { - read_connections (self); - priv->initialized = TRUE; - } - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) - list = g_slist_prepend (list, connection); - - return list; -} - -/*****************************************************************************/ - -static void -nms_ibft_plugin_init (NMSIbftPlugin *self) -{ - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); -} - -static void -dispose (GObject *object) -{ - NMSIbftPlugin *self = NMS_IBFT_PLUGIN (object); - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - - if (priv->connections) { - g_hash_table_destroy (priv->connections); - priv->connections = NULL; - } - - G_OBJECT_CLASS (nms_ibft_plugin_parent_class)->dispose (object); -} - -static void -nms_ibft_plugin_class_init (NMSIbftPluginClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass); - - object_class->dispose = dispose; - - plugin_class->get_connections = get_connections; -} - -/*****************************************************************************/ - -G_MODULE_EXPORT NMSettingsPlugin * -nm_settings_plugin_factory (void) -{ - return NM_SETTINGS_PLUGIN (g_object_ref (nms_ibft_plugin_get ())); -} diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.h b/src/settings/plugins/ibft/nms-ibft-plugin.h deleted file mode 100644 index 33a7620b..00000000 --- a/src/settings/plugins/ibft/nms-ibft-plugin.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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 2014 Red Hat, Inc. - */ - -#ifndef __NMS_IBFT_PLUGIN_H__ -#define __NMS_IBFT_PLUGIN_H__ - -#define NMS_TYPE_IBFT_PLUGIN (nms_ibft_plugin_get_type ()) -#define NMS_IBFT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IBFT_PLUGIN, NMSIbftPlugin)) -#define NMS_IBFT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IBFT_PLUGIN, NMSIbftPluginClass)) -#define NMS_IS_IBFT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IBFT_PLUGIN)) -#define NMS_IS_IBFT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IBFT_PLUGIN)) -#define NMS_IBFT_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IBFT_PLUGIN, NMSIbftPluginClass)) - -typedef struct _NMSIbftPlugin NMSIbftPlugin; -typedef struct _NMSIbftPluginClass NMSIbftPluginClass; - -GType nms_ibft_plugin_get_type (void); - -#endif /* __NMS_IBFT_PLUGIN_H__ */ diff --git a/src/settings/plugins/ibft/nms-ibft-reader.c b/src/settings/plugins/ibft/nms-ibft-reader.c deleted file mode 100644 index fde30383..00000000 --- a/src/settings/plugins/ibft/nms-ibft-reader.c +++ /dev/null @@ -1,535 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ibft-reader.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nm-core-internal.h" -#include "platform/nm-platform.h" -#include "NetworkManagerUtils.h" - -/*****************************************************************************/ - -#define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, " " msg) - -/* Removes trailing whitespace and whitespace before and immediately after the '=' */ -static char * -remove_most_whitespace (const char *src) -{ - char *s_new, *s2; - const char *svalue; - - src = nm_str_skip_leading_spaces (src); - - svalue = strchr (src, '='); - if (!svalue || svalue == src) - return NULL; - - s_new = g_new (char, strlen (src) + 1); - - memcpy (s_new, src, svalue - src); - s_new[svalue - src] = '\0'; - g_strchomp (s_new); - - svalue++; - while (*svalue && g_ascii_isspace (*svalue)) - svalue++; - - s2 = strchr (s_new, '\0'); - s2[0] = '='; - strcpy (++s2, svalue); - g_strchomp (s2); - - return s_new; -} - -#define TAG_BEGIN "# BEGIN RECORD" -#define TAG_END "# END RECORD" - -/** - * nms_ibft_reader_load_blocks: - * @iscsiadm_path: path to iscsiadm program - * @out_blocks: on return if successful, a #GSList of #GPtrArray, or %NULL on - * failure - * @error: location for an error on failure - * - * Parses iscsiadm output and returns a #GSList of #GPtrArray in the @out_blocks - * argument on success, otherwise @out_blocks is set to %NULL. Each #GPtrArray - * in @out_blocks contains the lines from an iscsiadm interface block. - * - * Returns: %TRUE on success, %FALSE on errors - */ -gboolean -nms_ibft_reader_load_blocks (const char *iscsiadm_path, - GSList **out_blocks, - GError **error) -{ - const char *argv[4] = { iscsiadm_path, "-m", "fw", NULL }; - const char *envp[1] = { NULL }; - nm_auto_free_ibft_blocks GSList *blocks = NULL; - gs_free char *out = NULL; - gs_free char *err = NULL; - gs_free const char **lines = NULL; - GPtrArray *block_lines = NULL; - gsize i; - int status = 0; - - g_return_val_if_fail (iscsiadm_path != NULL, FALSE); - g_return_val_if_fail (out_blocks != NULL && *out_blocks == NULL, FALSE); - - if (!g_spawn_sync ("/", (char **) argv, (char **) envp, 0, - NULL, NULL, &out, &err, &status, error)) - return FALSE; - - if (!WIFEXITED (status)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "iBFT: %s exited abnormally.", iscsiadm_path); - return FALSE; - } - - if (WEXITSTATUS (status) != 0) { - if (err) { - char *nl; - - /* the error message contains newlines. concatenate the lines with whitespace */ - for (nl = err; *nl; nl++) { - if (*nl == '\n') - *nl = ' '; - } - } - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "iBFT: %s exited with error %d. Message: '%s'", - iscsiadm_path, WEXITSTATUS (status), err ?: "(none)"); - return FALSE; - } - - nm_log_dbg (LOGD_SETTINGS, "iBFT records:\n%s", out); - - lines = nm_utils_strsplit_set (out, "\n\r"); - for (i = 0; lines && lines[i]; i++) { - const char *ss = lines[i]; - - if (!g_ascii_strncasecmp (ss, TAG_BEGIN, NM_STRLEN (TAG_BEGIN))) { - if (block_lines) { - PARSE_WARNING ("malformed iscsiadm record: missing END RECORD."); - nm_clear_pointer (&block_lines, g_ptr_array_unref); - } - /* Start new record */ - block_lines = g_ptr_array_new_full (15, g_free); - } else if (!g_ascii_strncasecmp (ss, TAG_END, NM_STRLEN (TAG_END))) { - if (block_lines) { - if (block_lines->len) - blocks = g_slist_prepend (blocks, g_steal_pointer (&block_lines)); - else - g_ptr_array_unref (g_steal_pointer (&block_lines)); - } - } else if (block_lines) { - char *s = remove_most_whitespace (ss); - - if (!s) { - PARSE_WARNING ("malformed iscsiadm record: no = in '%s'.", ss); - nm_clear_pointer (&block_lines, g_ptr_array_unref); - } else - g_ptr_array_add (block_lines, s); - } - } - - if (block_lines) { - PARSE_WARNING ("malformed iscsiadm record: missing # END RECORD."); - nm_clear_pointer (&block_lines, g_ptr_array_unref); - } - - *out_blocks = g_steal_pointer (&blocks); - return TRUE; -} - -#define ISCSI_HWADDR_TAG "iface.hwaddress" -#define ISCSI_BOOTPROTO_TAG "iface.bootproto" -#define ISCSI_IPADDR_TAG "iface.ipaddress" -#define ISCSI_SUBNET_TAG "iface.subnet_mask" -#define ISCSI_GATEWAY_TAG "iface.gateway" -#define ISCSI_DNS1_TAG "iface.primary_dns" -#define ISCSI_DNS2_TAG "iface.secondary_dns" -#define ISCSI_VLAN_ID_TAG "iface.vlan_id" -#define ISCSI_IFACE_TAG "iface.net_ifacename" - -static const char * -match_iscsiadm_tag (const char *line, const char *tag) -{ - gsize taglen = strlen (tag); - - if (g_ascii_strncasecmp (line, tag, taglen) != 0) - return NULL; - if (line[taglen] != '=') - return NULL; - return line + taglen + 1; -} - -/** - * nms_ibft_reader_parse_block: - * @block: an array of iscsiadm interface block lines - * @error: return location for errors - * @...: pairs of key (const char *) : location (const char **) indicating the - * key to look for and the location to store the retrieved value in - * - * Parses an iscsiadm interface block into variables requested by the caller. - * Callers should verify the returned data is complete and valid. Returned - * strings are owned by @block and should not be used after @block is freed. - * - * Returns: %TRUE if at least , %FALSE on failure - */ -gboolean -nms_ibft_reader_parse_block (const GPtrArray *block, GError **error, ...) -{ - gboolean success = FALSE; - const char **out_value, *p; - va_list ap; - const char *key; - guint i; - - g_return_val_if_fail (block != NULL, FALSE); - g_return_val_if_fail (block->len > 0, FALSE); - - /* Find requested keys and populate return values */ - va_start (ap, error); - while ((key = va_arg (ap, const char *))) { - out_value = va_arg (ap, const char **); - *out_value = NULL; - for (i = 0; i < block->len; i++) { - p = match_iscsiadm_tag (g_ptr_array_index (block, i), key); - if (p) { - *out_value = p; - success = TRUE; - break; - } - } - } - va_end (ap); - - if (!success) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: failed to match at least one iscsiadm block field"); - } - return success; -} - -static gboolean -ip4_setting_add_from_block (const GPtrArray *block, - NMConnection *connection, - GError **error) -{ - NMSettingIPConfig *s_ip4 = NULL; - NMIPAddress *addr; - const char *s_method = NULL; - const char *s_ipaddr = NULL; - const char *s_gateway = NULL; - const char *s_dns1 = NULL; - const char *s_dns2 = NULL; - const char *s_netmask = NULL; - guint32 netmask = 0; - guint32 prefix; - - g_assert (block); - - if (!nms_ibft_reader_parse_block (block, error, - ISCSI_BOOTPROTO_TAG, &s_method, - ISCSI_IPADDR_TAG, &s_ipaddr, - ISCSI_SUBNET_TAG, &s_netmask, - ISCSI_GATEWAY_TAG, &s_gateway, - ISCSI_DNS1_TAG, &s_dns1, - ISCSI_DNS2_TAG, &s_dns2, - NULL)) - goto error; - - if (!s_method) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_BOOTPROTO_TAG); - goto error; - } - - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - - if (!g_ascii_strcasecmp (s_method, "dhcp")) { - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - goto success; - } else if (g_ascii_strcasecmp (s_method, "static") != 0) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: unknown " ISCSI_BOOTPROTO_TAG " '%s'.", - s_method); - goto error; - } - - /* Static configuration stuff */ - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL); - - /* IP address */ - if (!s_ipaddr || !nm_utils_ipaddr_valid (AF_INET, s_ipaddr)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid IP address '%s'.", - s_ipaddr); - goto error; - } - - /* Subnet/prefix */ - if (!s_netmask || inet_pton (AF_INET, s_netmask, &netmask) != 1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid subnet mask '%s'.", - s_netmask); - goto error; - } - prefix = nm_utils_ip4_netmask_to_prefix (netmask); - - if (s_gateway && !nm_utils_ipaddr_valid (AF_INET, s_gateway)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid IP gateway '%s'.", - s_gateway); - goto error; - } - - if (s_dns1 && !nm_utils_ipaddr_valid (AF_INET, s_dns1)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid DNS1 address '%s'.", - s_dns1); - goto error; - } - - if (s_dns2 && !nm_utils_ipaddr_valid (AF_INET, s_dns2)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid DNS2 address '%s'.", - s_dns2); - goto error; - } - - addr = nm_ip_address_new (AF_INET, s_ipaddr, prefix, error); - if (!addr) { - g_prefix_error (error, "iBFT: malformed iscsiadm record: "); - goto error; - } - - nm_setting_ip_config_add_address (s_ip4, addr); - nm_ip_address_unref (addr); - - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, s_gateway, NULL); - - if (s_dns1) - nm_setting_ip_config_add_dns (s_ip4, s_dns1); - if (s_dns2) - nm_setting_ip_config_add_dns (s_ip4, s_dns2); - -success: - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - return TRUE; - -error: - g_clear_object (&s_ip4); - return FALSE; -} - -static gboolean -connection_setting_add (const GPtrArray *block, - NMConnection *connection, - const char *type, - const char *prefix, - const char *iface, - GError **error) -{ - NMSetting *s_con; - char *id, *uuid; - const char *s_hwaddr = NULL, *s_ip4addr = NULL, *s_vlanid; - - if (!nms_ibft_reader_parse_block (block, error, - ISCSI_VLAN_ID_TAG, &s_vlanid, - ISCSI_HWADDR_TAG, &s_hwaddr, - ISCSI_IPADDR_TAG, &s_ip4addr, - NULL)) - return FALSE; - if (!s_hwaddr) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG); - return FALSE; - } - - id = g_strdup_printf ("iBFT%s%s %s", - prefix ? " " : "", - prefix ?: "", - iface); - - uuid = _nm_utils_uuid_generate_from_strings ("ibft", - s_hwaddr, - s_vlanid ? "V" : "v", - s_vlanid ?: "", - s_ip4addr ? "A" : "DHCP", - s_ip4addr ?: "", - NULL); - - s_con = nm_setting_connection_new (); - g_object_set (s_con, - NM_SETTING_CONNECTION_TYPE, type, - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_ID, id, - NM_SETTING_CONNECTION_READ_ONLY, TRUE, - NULL); - - g_free (uuid); - g_free (id); - - nm_connection_add_setting (connection, NM_SETTING (s_con)); - return TRUE; -} - -static gboolean -is_ibft_vlan_device (const GPtrArray *block) -{ - char *s_vlan_id = NULL; - - if (nms_ibft_reader_parse_block (block, NULL, ISCSI_VLAN_ID_TAG, &s_vlan_id, NULL)) { - g_assert (s_vlan_id); - - /* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it - * means "no VLAN". - */ - if (_nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1) - return TRUE; - } - return FALSE; -} - -static gboolean -vlan_setting_add_from_block (const GPtrArray *block, - NMConnection *connection, - GError **error) -{ - NMSetting *s_vlan = NULL; - const char *vlan_id_str = NULL; - gint64 vlan_id = -1; - gboolean success; - - g_assert (block); - g_assert (connection); - - /* This won't fail since this function shouldn't be called unless the - * iBFT VLAN ID exists and is > 0. - */ - success = nms_ibft_reader_parse_block (block, NULL, ISCSI_VLAN_ID_TAG, &vlan_id_str, NULL); - g_assert (success); - g_assert (vlan_id_str); - - /* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it means "no VLAN" */ - vlan_id = _nm_utils_ascii_str_to_int64 (vlan_id_str, 10, 1, 4095, -1); - if (vlan_id == -1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid VLAN_ID '%s'", vlan_id_str); - return FALSE; - } - - s_vlan = nm_setting_vlan_new (); - g_object_set (s_vlan, NM_SETTING_VLAN_ID, (guint32) vlan_id, NULL); - nm_connection_add_setting (connection, NM_SETTING (s_vlan)); - - return TRUE; -} - -static gboolean -wired_setting_add_from_block (const GPtrArray *block, - NMConnection *connection, - GError **error) -{ - NMSetting *s_wired = NULL; - const char *hwaddr = NULL; - - g_assert (block); - g_assert (connection); - - if (!nms_ibft_reader_parse_block (block, NULL, ISCSI_HWADDR_TAG, &hwaddr, NULL)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG); - return FALSE; - } - - if (!nm_utils_hwaddr_valid (hwaddr, ETH_ALEN)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid " ISCSI_HWADDR_TAG " '%s'.", - hwaddr); - return FALSE; - } - - s_wired = nm_setting_wired_new (); - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, hwaddr, NULL); - - nm_connection_add_setting (connection, s_wired); - return TRUE; -} - -NMConnection * -nms_ibft_reader_get_connection_from_block (const GPtrArray *block, GError **error) -{ - NMConnection *connection = NULL; - gboolean is_vlan = FALSE; - const char *iface = NULL; - - g_assert (block); - - if (!nms_ibft_reader_parse_block (block, error, ISCSI_IFACE_TAG, &iface, NULL)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_IFACE_TAG); - return NULL; - } - - connection = nm_simple_connection_new (); - - is_vlan = is_ibft_vlan_device (block); - if (is_vlan && !vlan_setting_add_from_block (block, connection, error)) - goto error; - - /* Always have a wired setting; for VLAN it defines the parent */ - if (!wired_setting_add_from_block (block, connection, error)) - goto error; - - if (!ip4_setting_add_from_block (block, connection, error)) - goto error; - - if (!connection_setting_add (block, - connection, - is_vlan ? NM_SETTING_VLAN_SETTING_NAME : NM_SETTING_WIRED_SETTING_NAME, - is_vlan ? "VLAN" : NULL, - iface, - error)) - goto error; - - if (!nm_connection_normalize (connection, NULL, NULL, error)) - goto error; - - return connection; - -error: - g_object_unref (connection); - return NULL; -} - diff --git a/src/settings/plugins/ibft/nms-ibft-reader.h b/src/settings/plugins/ibft/nms-ibft-reader.h deleted file mode 100644 index baa81e99..00000000 --- a/src/settings/plugins/ibft/nms-ibft-reader.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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 2014 Red Hat, Inc. - */ - -#ifndef __NMS_IBFT_READER_H__ -#define __NMS_IBFT_READER_H__ - -#include "nm-connection.h" - -static inline void -_nm_auto_free_ibft_blocks (GSList **p_blocks) -{ - if (*p_blocks) - g_slist_free_full (*p_blocks, (GDestroyNotify) g_ptr_array_unref); -} -#define nm_auto_free_ibft_blocks nm_auto (_nm_auto_free_ibft_blocks) - -gboolean nms_ibft_reader_load_blocks (const char *iscsiadm_path, - GSList **out_blocks, - GError **error); - -NMConnection *nms_ibft_reader_get_connection_from_block (const GPtrArray *block, GError **error); - -gboolean nms_ibft_reader_parse_block (const GPtrArray *block, GError **error, ...) G_GNUC_NULL_TERMINATED; - -#endif /* __NMS_IBFT_READER_H__ */ diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 deleted file mode 100755 index 54f02da6..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10000.500.250.1 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 deleted file mode 100755 index ebd7a9ca..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = blah.foo.bar.baz -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry deleted file mode 100755 index 4e326048..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway deleted file mode 100755 index 5390a6c3..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = bb.cc.dd.ee -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr deleted file mode 100755 index b41cd1f1..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = aa.bb.cc.dd -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record deleted file mode 100755 index 22b34e6f..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = DHCP -iface.gateway = 10.16.52.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp b/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp deleted file mode 100755 index 556b0586..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = DHCP -iface.gateway = 10.16.52.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f1 -iface.bootproto = DHCP -iface.gateway = 10.16.52.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth1 -node.name = iqn.1.2008-11.com.blahblah:iscsi1 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-static b/src/settings/plugins/ibft/tests/iscsiadm-test-static deleted file mode 100755 index 51711480..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-static +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.32.72 -iface.subnet_mask = 255.255.252.0 -iface.gateway = 192.168.35.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth0 -node.name = iqn.0.2008-11.com.blahblah:iscsi0 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -# BEGIN RECORD -iface.initiatorname = iqn.pjones6 -iface.hwaddress = 00:33:21:98:b9:f1 -iface.bootproto = DHCP -iface.gateway = 10.16.52.254 -iface.primary_dns = 10.16.255.2 -iface.secondary_dns = 10.16.255.3 -iface.vlan_id = 0 -iface.net_ifacename = eth1 -node.name = iqn.1.2008-11.com.blahblah:iscsi1 -node.conn[0].address = 10.16.52.16 -node.conn[0].port = 3260 -node.boot_lun = 00000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan b/src/settings/plugins/ibft/tests/iscsiadm-test-vlan deleted file mode 100755 index 59b80bd0..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -cat << EOF -# BEGIN RECORD 6.2.0.873-21 -iface.initiatorname = iqn.2010-04.org.ipxe:d05faa97-c4be-44f6-a723-efde9aa399a0 -iface.transport_name = tcp -iface.hwaddress = 00:33:21:98:b9:f0 -iface.bootproto = STATIC -iface.ipaddress = 192.168.6.200 -iface.subnet_mask = 255.255.255.0 -iface.vlan_id = 123 -iface.net_ifacename = eth0 -node.name = iqn.2003-01.org.x:disk1 -node.conn[0].address = 192.168.6.32 -node.conn[0].port = 3260 -node.boot_lun = 01000000 -# END RECORD -EOF - diff --git a/src/settings/plugins/ibft/tests/meson.build b/src/settings/plugins/ibft/tests/meson.build deleted file mode 100644 index e2f9ca7e..00000000 --- a/src/settings/plugins/ibft/tests/meson.build +++ /dev/null @@ -1,16 +0,0 @@ -test_unit = 'test-ibft' - -test_ibft_dir = meson.current_source_dir() - -exe = executable( - test_unit, - test_unit + '.c', - dependencies: test_nm_dep, - link_with: libnms_ibft_core, -) - -test( - 'ibft/' + test_unit, - test_script, - args: test_args + [exe.full_path()], -) diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c deleted file mode 100644 index f5b584a1..00000000 --- a/src/settings/plugins/ibft/tests/test-ibft.c +++ /dev/null @@ -1,292 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" - -#include "settings/plugins/ibft/nms-ibft-reader.h" - -#include "nm-test-utils-core.h" - -#define TEST_DIR NM_BUILD_SRCDIR"/src/settings/plugins/ibft/tests" - -static GPtrArray * -read_block (const char *iscsiadm_path, const char *expected_mac) -{ - nm_auto_free_ibft_blocks GSList *blocks = NULL; - GSList *iter; - GPtrArray *block = NULL; - GError *error = NULL; - gboolean success; - - success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error); - nmtst_assert_success (success, error); - - g_assert (blocks); - for (iter = blocks; iter; iter = iter->next) { - const char *s_hwaddr = NULL; - - if (!nms_ibft_reader_parse_block (iter->data, NULL, "iface.hwaddress", &s_hwaddr, NULL)) - continue; - g_assert (s_hwaddr); - if (nm_utils_hwaddr_matches (s_hwaddr, -1, expected_mac, -1)) { - block = g_ptr_array_ref (iter->data); - break; - } - } - g_assert (block); - - return block; -} - -static void -test_read_ibft_dhcp (void) -{ - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - GError *error = NULL; - const char *mac_address; - const char *expected_mac_address = "00:33:21:98:b9:f1"; - GPtrArray *block; - - block = read_block (TEST_DIR "/iscsiadm-test-dhcp", expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_no_error (error); - nmtst_assert_connection_verifies_without_normalization (connection); - - g_assert (!nm_connection_get_setting_vlan (connection)); - - /* ===== CONNECTION SETTING ===== */ - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "iBFT eth1"); - g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0); - g_assert (nm_setting_connection_get_autoconnect (s_con)); - g_assert (nm_setting_connection_get_read_only (s_con)); - - /* ===== WIRED SETTING ===== */ - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac_address = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac_address); - g_assert (nm_utils_hwaddr_matches (mac_address, -1, expected_mac_address, -1)); - g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0); - - /* ===== IPv4 SETTING ===== */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - - g_object_unref (connection); - g_ptr_array_unref (block); -} - -static void -test_read_ibft_static (void) -{ - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - GError *error = NULL; - const char *mac_address; - const char *expected_mac_address = "00:33:21:98:b9:f0"; - NMIPAddress *ip4_addr; - GPtrArray *block; - - block = read_block (TEST_DIR "/iscsiadm-test-static", expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_no_error (error); - nmtst_assert_connection_verifies_without_normalization (connection); - - g_assert (!nm_connection_get_setting_vlan (connection)); - - /* ===== CONNECTION SETTING ===== */ - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "iBFT eth0"); - g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0); - g_assert (nm_setting_connection_get_autoconnect (s_con)); - g_assert (nm_setting_connection_get_read_only (s_con)); - - /* ===== WIRED SETTING ===== */ - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac_address = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac_address); - g_assert (nm_utils_hwaddr_matches (mac_address, -1, expected_mac_address, -1)); - g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0); - - /* ===== IPv4 SETTING ===== */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); - - g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 2); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "10.16.255.2"); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 1), ==, "10.16.255.3"); - - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1); - ip4_addr = nm_setting_ip_config_get_address (s_ip4, 0); - g_assert (ip4_addr); - g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "192.168.32.72"); - g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 22); - - g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.168.35.254"); - - g_object_unref (connection); - g_ptr_array_unref (block); -} - -static void -test_read_ibft_malformed (gconstpointer user_data) -{ - const char *iscsiadm_path = user_data; - nm_auto_free_ibft_blocks GSList *blocks = NULL; - GError *error = NULL; - gboolean success; - - g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS)); - - NMTST_EXPECT_NM_WARN ("*malformed iscsiadm record*"); - - success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error); - nmtst_assert_success (success, error); - - g_assert (!blocks); - - g_test_assert_expected_messages (); -} - -static void -test_read_ibft_bad_address (gconstpointer user_data) -{ - const char *iscsiadm_path = user_data; - NMConnection *connection; - const char *expected_mac_address = "00:33:21:98:b9:f0"; - GPtrArray *block; - GError *error = NULL; - - g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS)); - - block = read_block (iscsiadm_path, expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); - g_assert (strstr (error->message, "iBFT: malformed iscsiadm record: invalid")); - g_clear_error (&error); - g_assert (connection == NULL); - - g_ptr_array_unref (block); -} - -static void -test_read_ibft_vlan (void) -{ - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingVlan *s_vlan; - NMSettingIPConfig *s_ip4; - const char *mac_address; - const char *expected_mac_address = "00:33:21:98:b9:f0"; - NMIPAddress *ip4_addr; - GError *error = NULL; - GPtrArray *block; - - block = read_block (TEST_DIR "/iscsiadm-test-vlan", expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_no_error (error); - nmtst_assert_connection_verifies_without_normalization (connection); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_VLAN_SETTING_NAME); - - /* ===== WIRED SETTING ===== */ - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac_address = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac_address); - g_assert (nm_utils_hwaddr_matches (mac_address, -1, expected_mac_address, -1)); - - /* ===== VLAN SETTING ===== */ - s_vlan = nm_connection_get_setting_vlan (connection); - g_assert (s_vlan); - g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 123); - g_assert_cmpstr (nm_setting_vlan_get_parent (s_vlan), ==, NULL); - - /* ===== IPv4 SETTING ===== */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); - - g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0); - - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1); - ip4_addr = nm_setting_ip_config_get_address (s_ip4, 0); - g_assert (ip4_addr); - g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "192.168.6.200"); - g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 24); - - g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, NULL); - - g_object_unref (connection); - g_ptr_array_unref (block); -} - -NMTST_DEFINE (); - -#define TPATH "/settings/plugins/ibft/" - -int main (int argc, char **argv) -{ - nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); - - g_test_add_func (TPATH "ibft/dhcp", test_read_ibft_dhcp); - g_test_add_func (TPATH "ibft/static", test_read_ibft_static); - g_test_add_func (TPATH "ibft/vlan", test_read_ibft_vlan); - g_test_add_data_func (TPATH "ibft/bad-record-read", TEST_DIR "/iscsiadm-test-bad-record", test_read_ibft_malformed); - g_test_add_data_func (TPATH "ibft/bad-entry-read", TEST_DIR "/iscsiadm-test-bad-entry", test_read_ibft_malformed); - g_test_add_data_func (TPATH "ibft/bad-ipaddr-read", TEST_DIR "/iscsiadm-test-bad-ipaddr", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-gateway-read", TEST_DIR "/iscsiadm-test-bad-gateway", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-dns1-read", TEST_DIR "/iscsiadm-test-bad-dns1", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-dns2-read", TEST_DIR "/iscsiadm-test-bad-dns2", test_read_ibft_bad_address); - - return g_test_run (); -} - diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build index 9024782a..58acdcfc 100644 --- a/src/settings/plugins/ifcfg-rh/meson.build +++ b/src/settings/plugins/ifcfg-rh/meson.build @@ -19,7 +19,6 @@ libnmdbus_ifcfg_rh = static_library( ) core_sources = files( - 'nm-inotify-helper.c', 'nms-ifcfg-rh-reader.c', 'nms-ifcfg-rh-utils.c', 'nms-ifcfg-rh-writer.c', @@ -36,7 +35,7 @@ libnms_ifcfg_rh_core = static_library( dependencies: deps, ) -sources = [dbus_sources] + core_sources + files('nms-ifcfg-rh-connection.c', 'nms-ifcfg-rh-plugin.c') +sources = [dbus_sources] + core_sources + files('nms-ifcfg-rh-storage.c', 'nms-ifcfg-rh-plugin.c') libnm_settings_plugin_ifcfg_rh = shared_module( 'nm-settings-plugin-ifcfg-rh', diff --git a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c deleted file mode 100644 index 04cbb5bc..00000000 --- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c +++ /dev/null @@ -1,213 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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. - * - * (C) Copyright 2008 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-inotify-helper.h" - -#include -#include - -#include "NetworkManagerUtils.h" - -/* NOTE: this code should be killed once we depend on a new enough glib to - * include the patches from https://bugzilla.gnome.org/show_bug.cgi?id=532815 - */ - -/*****************************************************************************/ - -enum { - EVENT, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct { - int ifd; - guint inotify_id; - GHashTable *wd_refs; -} NMInotifyHelperPrivate; - -struct _NMInotifyHelper { - GObject parent; - NMInotifyHelperPrivate _priv; -}; - -struct _NMInotifyHelperClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMInotifyHelper, nm_inotify_helper, G_TYPE_OBJECT) - -#define NM_INOTIFY_HELPER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMInotifyHelper, NM_IS_INOTIFY_HELPER) - -/*****************************************************************************/ - -NM_DEFINE_SINGLETON_GETTER (NMInotifyHelper, nm_inotify_helper_get, NM_TYPE_INOTIFY_HELPER); - -/*****************************************************************************/ - -int -nm_inotify_helper_add_watch (NMInotifyHelper *self, const char *path) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - int wd; - guint refcount; - - if (priv->ifd < 0) - return -1; - - /* We only care about modifications since we're just trying to get change - * notifications on hardlinks. - */ - - wd = inotify_add_watch (priv->ifd, path, IN_CLOSE_WRITE); - if (wd < 0) - return -1; - - refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd))); - refcount++; - g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount)); - - return wd; -} - -void -nm_inotify_helper_remove_watch (NMInotifyHelper *self, int wd) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - guint refcount; - - if (priv->ifd < 0) - return; - - refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd))); - if (!refcount) - return; - - refcount--; - if (!refcount) { - g_hash_table_remove (priv->wd_refs, GINT_TO_POINTER (wd)); - inotify_rm_watch (priv->ifd, wd); - } else - g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount)); -} - -static gboolean -inotify_event_handler (GIOChannel *channel, GIOCondition cond, gpointer user_data) -{ - NMInotifyHelper *self = NM_INOTIFY_HELPER (user_data); - struct inotify_event evt; - - /* read the notifications from the watch descriptor */ - while (g_io_channel_read_chars (channel, (char *) &evt, sizeof (struct inotify_event), NULL, NULL) == G_IO_STATUS_NORMAL) { - char filename[PATH_MAX + 1]; - - filename[0] = '\0'; - if (evt.len > 0) { - g_io_channel_read_chars (channel, - filename, - evt.len > PATH_MAX ? PATH_MAX : evt.len, - NULL, NULL); - } - - if (!(evt.mask & IN_IGNORED)) - g_signal_emit (self, signals[EVENT], 0, &evt, &filename[0]); - } - - return TRUE; -} - -static gboolean -init_inotify (NMInotifyHelper *self) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - GIOChannel *channel; - int errsv; - - priv->ifd = inotify_init1 (IN_CLOEXEC); - if (priv->ifd == -1) { - errsv = errno; - nm_log_warn (LOGD_SETTINGS, "couldn't initialize inotify: %s (%d)", nm_strerror_native (errsv), errsv); - return FALSE; - } - - /* Watch the inotify descriptor for file/directory change events */ - channel = g_io_channel_unix_new (priv->ifd); - g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL); - g_io_channel_set_encoding (channel, NULL, NULL); - - priv->inotify_id = g_io_add_watch (channel, - G_IO_IN | G_IO_ERR, - (GIOFunc) inotify_event_handler, - (gpointer) self); - g_io_channel_unref (channel); - return TRUE; -} - -/*****************************************************************************/ - -static void -nm_inotify_helper_init (NMInotifyHelper *self) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - - priv->wd_refs = g_hash_table_new (nm_direct_hash, NULL); -} - -static void -constructed (GObject *object) -{ - G_OBJECT_CLASS (nm_inotify_helper_parent_class)->constructed (object); - - init_inotify (NM_INOTIFY_HELPER (object)); -} - -static void -finalize (GObject *object) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE ((NMInotifyHelper *) object); - - nm_clear_g_source (&priv->inotify_id); - nm_close (priv->ifd); - - g_hash_table_destroy (priv->wd_refs); - - G_OBJECT_CLASS (nm_inotify_helper_parent_class)->finalize (object); -} - -static void -nm_inotify_helper_class_init (NMInotifyHelperClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = constructed; - object_class->finalize = finalize; - - signals[EVENT] = - g_signal_new ("event", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_STRING); -} - diff --git a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h deleted file mode 100644 index b887ae37..00000000 --- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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. - * - * (C) Copyright 2008 Red Hat, Inc. - */ - -#ifndef __NM_INOTIFY_HELPER_H__ -#define __NM_INOTIFY_HELPER_H__ - -/* NOTE: this code should be killed once we depend on a new enough glib to - * include the patches from https://bugzilla.gnome.org/show_bug.cgi?id=532815 - */ - -#define NM_TYPE_INOTIFY_HELPER (nm_inotify_helper_get_type ()) -#define NM_INOTIFY_HELPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelper)) -#define NM_INOTIFY_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass)) -#define NM_IS_INOTIFY_HELPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_INOTIFY_HELPER)) -#define NM_IS_INOTIFY_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_INOTIFY_HELPER)) -#define NM_INOTIFY_HELPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass)) - -typedef struct _NMInotifyHelper NMInotifyHelper; -typedef struct _NMInotifyHelperClass NMInotifyHelperClass; - -GType nm_inotify_helper_get_type (void); - -NMInotifyHelper * nm_inotify_helper_get (void); - -int nm_inotify_helper_add_watch (NMInotifyHelper *helper, const char *path); - -void nm_inotify_helper_remove_watch (NMInotifyHelper *helper, int wd); - -static inline gboolean -nm_inotify_helper_clear_watch (NMInotifyHelper *helper, int *wd) -{ - int x; - - if (wd && ((x = *wd) >= 0)) { - *wd = -1; - nm_inotify_helper_remove_watch (helper, x); - return TRUE; - } - return FALSE; -} - -#endif /* __NM_INOTIFY_HELPER_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h index eaff2db4..ff01fc7a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c deleted file mode 100644 index 4f769c5f..00000000 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c +++ /dev/null @@ -1,511 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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 (C) 2008 - 2011 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ifcfg-rh-connection.h" - -#include -#include - -#include "nm-dbus-interface.h" -#include "nm-setting-connection.h" -#include "nm-setting-wired.h" -#include "nm-setting-wireless.h" -#include "nm-setting-gsm.h" -#include "nm-setting-cdma.h" -#include "nm-setting-pppoe.h" -#include "nm-setting-wireless-security.h" -#include "nm-setting-8021x.h" -#include "platform/nm-platform.h" -#include "nm-config.h" - -#include "nms-ifcfg-rh-common.h" -#include "nms-ifcfg-rh-reader.h" -#include "nms-ifcfg-rh-writer.h" -#include "nms-ifcfg-rh-utils.h" -#include "nm-inotify-helper.h" - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_UNMANAGED_SPEC, - PROP_UNRECOGNIZED_SPEC, -); - -enum { - IFCFG_CHANGED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct { - gulong ih_event_id; - - int file_wd; - - char *keyfile; - int keyfile_wd; - - char *routefile; - int routefile_wd; - - char *route6file; - int route6file_wd; - - char *unmanaged_spec; - char *unrecognized_spec; - - gulong devtimeout_link_changed_handler; - guint devtimeout_timeout_id; - - NMInotifyHelper *inotify_helper; -} NMIfcfgConnectionPrivate; - -struct _NMIfcfgConnection { - NMSettingsConnection parent; - NMIfcfgConnectionPrivate _priv; -}; - -struct _NMIfcfgConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMIfcfgConnection, nm_ifcfg_connection, NM_TYPE_SETTINGS_CONNECTION) - -#define NM_IFCFG_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIfcfgConnection, NM_IS_IFCFG_CONNECTION) - -/*****************************************************************************/ - -static gboolean -devtimeout_ready (gpointer user_data) -{ - NMIfcfgConnection *self = user_data; - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - priv->devtimeout_timeout_id = 0; - nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), TRUE); - return FALSE; -} - -static void -link_changed (NMPlatform *platform, int obj_type_i, int ifindex, const NMPlatformLink *link, - int change_type_i, - NMConnection *self) -{ - const NMPlatformSignalChangeType change_type = change_type_i; - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) self); - const char *ifname; - - ifname = nm_connection_get_interface_name (self); - if (g_strcmp0 (link->name, ifname) != 0) - return; - - if (change_type == NM_PLATFORM_SIGNAL_REMOVED) - return; - - nm_log_info (LOGD_SETTINGS, "Device %s appeared; connection '%s' now ready", - ifname, nm_connection_get_id (self)); - - g_signal_handler_disconnect (platform, priv->devtimeout_link_changed_handler); - priv->devtimeout_link_changed_handler = 0; - g_source_remove (priv->devtimeout_timeout_id); - - /* Don't declare the connection ready right away, since NMManager may not have - * started processing the device yet. - */ - priv->devtimeout_timeout_id = g_idle_add (devtimeout_ready, self); -} - -static gboolean -devtimeout_expired (gpointer user_data) -{ - NMIfcfgConnection *self = user_data; - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - nm_log_info (LOGD_SETTINGS, "Device for connection '%s' did not appear before timeout", - nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self))); - - g_signal_handler_disconnect (NM_PLATFORM_GET, priv->devtimeout_link_changed_handler); - priv->devtimeout_link_changed_handler = 0; - priv->devtimeout_timeout_id = 0; - - nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), TRUE); - return FALSE; -} - -static void -nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - NMSettingConnection *s_con; - const char *ifname; - const char *filename; - guint devtimeout; - const NMPlatformLink *pllink; - - s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (self))); - - if (!nm_setting_connection_get_autoconnect (s_con)) - return; - ifname = nm_setting_connection_get_interface_name (s_con); - if (!ifname) - return; - filename = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (self)); - if (!filename) - return; - - pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname); - if (pllink && pllink->initialized) - return; - - devtimeout = devtimeout_from_file (filename); - if (!devtimeout) - return; - - /* ONBOOT=yes, DEVICE and DEVTIMEOUT are set, but device is not present */ - nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), FALSE); - - nm_log_info (LOGD_SETTINGS, "Waiting %u seconds for %s to appear for connection '%s'", - devtimeout, ifname, nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self))); - - priv->devtimeout_link_changed_handler = - g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED, - G_CALLBACK (link_changed), self); - priv->devtimeout_timeout_id = g_timeout_add_seconds (devtimeout, devtimeout_expired, self); -} - -static void -files_changed_cb (NMInotifyHelper *ih, - struct inotify_event *evt, - const char *path, - gpointer user_data) -{ - NMIfcfgConnection *self = NM_IFCFG_CONNECTION (user_data); - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - if ( (evt->wd != priv->file_wd) - && (evt->wd != priv->keyfile_wd) - && (evt->wd != priv->routefile_wd) - && (evt->wd != priv->route6file_wd)) - return; - - /* push the event up to the plugin */ - g_signal_emit (self, signals[IFCFG_CHANGED], 0); -} - -static void -path_watch_stop (NMIfcfgConnection *self) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - nm_clear_g_signal_handler (priv->inotify_helper, &priv->ih_event_id); - - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->file_wd); - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->keyfile_wd); - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->routefile_wd); - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->route6file_wd); - - nm_clear_g_free (&priv->keyfile); - nm_clear_g_free (&priv->routefile); - nm_clear_g_free (&priv->route6file); -} - -static void -filename_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - NMIfcfgConnection *self = NM_IFCFG_CONNECTION (object); - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - const char *ifcfg_path; - - path_watch_stop (self); - - ifcfg_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (self)); - if (!ifcfg_path) - return; - - priv->keyfile = utils_get_keys_path (ifcfg_path); - priv->routefile = utils_get_route_path (ifcfg_path); - priv->route6file = utils_get_route6_path (ifcfg_path); - - if (nm_config_get_monitor_connection_files (nm_config_get ())) { - NMInotifyHelper *ih; - - if (!priv->inotify_helper) - priv->inotify_helper = g_object_ref (nm_inotify_helper_get ()); - ih = priv->inotify_helper; - - priv->ih_event_id = g_signal_connect (ih, "event", G_CALLBACK (files_changed_cb), self); - priv->file_wd = nm_inotify_helper_add_watch (ih, ifcfg_path); - priv->keyfile_wd = nm_inotify_helper_add_watch (ih, priv->keyfile); - priv->routefile_wd = nm_inotify_helper_add_watch (ih, priv->routefile); - priv->route6file_wd = nm_inotify_helper_add_watch (ih, priv->route6file); - } -} - -const char * -nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self) -{ - g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), NULL); - - return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unmanaged_spec; -} - -const char * -nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self) -{ - g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), NULL); - - return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unrecognized_spec; -} - -static gboolean -commit_changes (NMSettingsConnection *connection, - NMConnection *new_connection, - NMSettingsConnectionCommitReason commit_reason, - NMConnection **out_reread_connection, - char **out_logmsg_change, - GError **error) -{ - const char *filename; - gs_unref_object NMConnection *reread = NULL; - gboolean reread_same = TRUE; - const char *operation_message; - gs_free char *ifcfg_path = NULL; - - nm_assert (out_reread_connection && !*out_reread_connection); - nm_assert (!out_logmsg_change || !*out_logmsg_change); - - filename = nm_settings_connection_get_filename (connection); - if (!nms_ifcfg_rh_writer_write_connection (new_connection, - IFCFG_DIR, - filename, - &ifcfg_path, - &reread, - &reread_same, - error)) - return FALSE; - - nm_assert ((!filename && ifcfg_path) || (filename && !ifcfg_path)); - if (ifcfg_path) { - nm_settings_connection_set_filename (connection, ifcfg_path); - operation_message = "persist"; - } else - operation_message = "update"; - - if (reread && !reread_same) - *out_reread_connection = g_steal_pointer (&reread); - - NM_SET_OUT (out_logmsg_change, - g_strdup_printf ("ifcfg-rh: %s %s", - operation_message, filename)); - return TRUE; -} - -static gboolean -delete (NMSettingsConnection *connection, - GError **error) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) connection); - const char *filename; - - filename = nm_settings_connection_get_filename (connection); - if (filename) { - g_unlink (filename); - if (priv->keyfile) - g_unlink (priv->keyfile); - if (priv->routefile) - g_unlink (priv->routefile); - if (priv->route6file) - g_unlink (priv->route6file); - } - - return TRUE; -} - -/*****************************************************************************/ - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object); - - switch (prop_id) { - case PROP_UNMANAGED_SPEC: - g_value_set_string (value, priv->unmanaged_spec); - break; - case PROP_UNRECOGNIZED_SPEC: - g_value_set_string (value, priv->unrecognized_spec); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object); - - switch (prop_id) { - case PROP_UNMANAGED_SPEC: - priv->unmanaged_spec = g_value_dup_string (value); - break; - case PROP_UNRECOGNIZED_SPEC: - priv->unrecognized_spec = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_ifcfg_connection_init (NMIfcfgConnection *connection) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (connection); - - priv->file_wd = -1; - priv->keyfile_wd = -1; - priv->routefile_wd = -1; - priv->route6file_wd = -1; - - g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_FILENAME, - G_CALLBACK (filename_changed), NULL); -} - -NMIfcfgConnection * -nm_ifcfg_connection_new (NMConnection *source, - const char *full_path, - GError **error, - gboolean *out_ignore_error) -{ - GObject *object; - NMConnection *tmp; - char *unhandled_spec = NULL; - const char *unmanaged_spec = NULL, *unrecognized_spec = NULL; - - g_assert (source || full_path); - - if (out_ignore_error) - *out_ignore_error = FALSE; - - /* If we're given a connection already, prefer that instead of re-reading */ - if (source) - tmp = g_object_ref (source); - else { - tmp = connection_from_file (full_path, - &unhandled_spec, - error, - out_ignore_error); - if (!tmp) - return NULL; - } - - if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unmanaged:")) - unmanaged_spec = unhandled_spec + strlen ("unmanaged:"); - else if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unrecognized:")) - unrecognized_spec = unhandled_spec + strlen ("unrecognized:"); - - object = (GObject *) g_object_new (NM_TYPE_IFCFG_CONNECTION, - NM_SETTINGS_CONNECTION_FILENAME, full_path, - NM_IFCFG_CONNECTION_UNMANAGED_SPEC, unmanaged_spec, - NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, unrecognized_spec, - NULL); - /* Update our settings with what was read from the file */ - if (nm_settings_connection_update (NM_SETTINGS_CONNECTION (object), - tmp, - full_path - ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED - : NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - NULL, - error)) - nm_ifcfg_connection_check_devtimeout (NM_IFCFG_CONNECTION (object)); - else - g_clear_object (&object); - - g_object_unref (tmp); - g_free (unhandled_spec); - return (NMIfcfgConnection *) object; -} - -static void -dispose (GObject *object) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object); - - path_watch_stop (NM_IFCFG_CONNECTION (object)); - - nm_clear_g_signal_handler (NM_PLATFORM_GET, &priv->devtimeout_link_changed_handler); - nm_clear_g_source (&priv->devtimeout_timeout_id); - - g_clear_object (&priv->inotify_helper); - - g_clear_pointer (&priv->unmanaged_spec, g_free); - g_clear_pointer (&priv->unrecognized_spec, g_free); - - G_OBJECT_CLASS (nm_ifcfg_connection_parent_class)->dispose (object); -} - -static void -nm_ifcfg_connection_class_init (NMIfcfgConnectionClass *ifcfg_connection_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (ifcfg_connection_class); - NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (ifcfg_connection_class); - - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->dispose = dispose; - - settings_class->delete = delete; - settings_class->commit_changes = commit_changes; - - obj_properties[PROP_UNMANAGED_SPEC] = - g_param_spec_string (NM_IFCFG_CONNECTION_UNMANAGED_SPEC, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_UNRECOGNIZED_SPEC] = - g_param_spec_string (NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - signals[IFCFG_CHANGED] = - g_signal_new ("ifcfg-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h deleted file mode 100644 index fd68f2d3..00000000 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * 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 (C) 2008 - 2011 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_IFCFG_CONNECTION_H__ -#define __NETWORKMANAGER_IFCFG_CONNECTION_H__ - -#include "nm-dbus-interface.h" -#include "settings/nm-settings-connection.h" - -#define NM_TYPE_IFCFG_CONNECTION (nm_ifcfg_connection_get_type ()) -#define NM_IFCFG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnection)) -#define NM_IFCFG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass)) -#define NM_IS_IFCFG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFCFG_CONNECTION)) -#define NM_IS_IFCFG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFCFG_CONNECTION)) -#define NM_IFCFG_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass)) - -#define NM_IFCFG_CONNECTION_UNMANAGED_SPEC "unmanaged-spec" -#define NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC "unrecognized-spec" - -typedef struct _NMIfcfgConnection NMIfcfgConnection; -typedef struct _NMIfcfgConnectionClass NMIfcfgConnectionClass; - -GType nm_ifcfg_connection_get_type (void); - -NMIfcfgConnection *nm_ifcfg_connection_new (NMConnection *source, - const char *full_path, - GError **error, - gboolean *out_ignore_error); - -const char *nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self); -const char *nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self); - -gboolean nm_ifcfg_connection_update (NMIfcfgConnection *self, - GHashTable *new_settings, - GError **error); - -#endif /* __NETWORKMANAGER_IFCFG_CONNECTION_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c index 5160dbf0..cc4fe4ce 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Dan Williams @@ -25,23 +24,26 @@ #include "nms-ifcfg-rh-plugin.h" -#include #include #include -#include +#include +#include "nm-std-aux/c-list-util.h" +#include "nm-glib-aux/nm-c-list.h" +#include "nm-glib-aux/nm-io-utils.h" #include "nm-std-aux/nm-dbus-compat.h" -#include "nm-setting-connection.h" -#include "settings/nm-settings-plugin.h" +#include "nm-utils.h" +#include "nm-core-internal.h" #include "nm-config.h" +#include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-utils.h" #include "NetworkManagerUtils.h" -#include "nms-ifcfg-rh-connection.h" +#include "nms-ifcfg-rh-storage.h" #include "nms-ifcfg-rh-common.h" +#include "nms-ifcfg-rh-utils.h" #include "nms-ifcfg-rh-reader.h" #include "nms-ifcfg-rh-writer.h" -#include "nms-ifcfg-rh-utils.h" -#include "shvar.h" #define IFCFGRH1_BUS_NAME "com.redhat.ifcfgrh1" #define IFCFGRH1_OBJECT_PATH "/com/redhat/ifcfgrh1" @@ -60,31 +62,25 @@ typedef struct { guint regist_id; } dbus; - GHashTable *connections; /* uuid::connection */ - gboolean initialized; + NMSettUtilStorages storages; - GFileMonitor *ifcfg_monitor; - gulong ifcfg_monitor_id; -} SettingsPluginIfcfgPrivate; + GHashTable *unmanaged_specs; + GHashTable *unrecognized_specs; -struct _SettingsPluginIfcfg { +} NMSIfcfgRHPluginPrivate; + +struct _NMSIfcfgRHPlugin { NMSettingsPlugin parent; - SettingsPluginIfcfgPrivate _priv; + NMSIfcfgRHPluginPrivate _priv; }; -struct _SettingsPluginIfcfgClass { +struct _NMSIfcfgRHPluginClass { NMSettingsPluginClass parent; }; -G_DEFINE_TYPE (SettingsPluginIfcfg, settings_plugin_ifcfg, NM_TYPE_SETTINGS_PLUGIN) +G_DEFINE_TYPE (NMSIfcfgRHPlugin, nms_ifcfg_rh_plugin, NM_TYPE_SETTINGS_PLUGIN) -#define SETTINGS_PLUGIN_IFCFG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfcfg, SETTINGS_IS_PLUGIN_IFCFG) - -/*****************************************************************************/ - -static SettingsPluginIfcfg *settings_plugin_ifcfg_get (void); - -NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfcfg, settings_plugin_ifcfg_get, SETTINGS_TYPE_PLUGIN_IFCFG); +#define NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfcfgRHPlugin, NMS_IS_IFCFG_RH_PLUGIN, NMSettingsPlugin) /*****************************************************************************/ @@ -99,610 +95,811 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfcfg, settings_plugin_ifcfg_get, SETT /*****************************************************************************/ -static NMIfcfgConnection *update_connection (SettingsPluginIfcfg *plugin, - NMConnection *source, - const char *full_path, - NMIfcfgConnection *connection, - gboolean protect_existing_connection, - GHashTable *protected_connections, - GError **error); +static void _unhandled_specs_reset (NMSIfcfgRHPlugin *self); + +static void _unhandled_specs_merge_storages (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages); /*****************************************************************************/ static void -connection_ifcfg_changed (NMIfcfgConnection *connection, gpointer user_data) +nm_assert_self (NMSIfcfgRHPlugin *self, gboolean unhandled_specs_consistent) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - const char *path; + nm_assert (NMS_IS_IFCFG_RH_PLUGIN (self)); - path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection)); - g_return_if_fail (path != NULL); +#if NM_MORE_ASSERTS > 5 + { + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + NMSIfcfgRHStorage *storage; + gsize n_uuid; + gs_unref_hashtable GHashTable *h_unmanaged = NULL; + gs_unref_hashtable GHashTable *h_unrecognized = NULL; - if (!priv->ifcfg_monitor) { - _LOGD ("connection_ifcfg_changed("NM_IFCFG_CONNECTION_LOG_FMTD"): %s", NM_IFCFG_CONNECTION_LOG_ARGD (connection), "ignore event"); - return; - } + nm_assert (g_hash_table_size (priv->storages.idx_by_filename) == c_list_length (&priv->storages._storage_lst_head)); + + h_unmanaged = g_hash_table_new (nm_str_hash, g_str_equal); + h_unrecognized = g_hash_table_new (nm_str_hash, g_str_equal); + + n_uuid = 0; + + c_list_for_each_entry (storage, &priv->storages._storage_lst_head, parent._storage_lst) { + const char *uuid; + const char *filename; + + filename = nms_ifcfg_rh_storage_get_filename (storage); + + nm_assert (filename && NM_STR_HAS_PREFIX (filename, IFCFG_DIR"/")); + + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + + nm_assert ((!!uuid) + (!!storage->unmanaged_spec) + (!!storage->unrecognized_spec) == 1); - _LOGD ("connection_ifcfg_changed("NM_IFCFG_CONNECTION_LOG_FMTD"): %s", NM_IFCFG_CONNECTION_LOG_ARGD (connection), "reload"); + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, filename)); + + if (uuid) { + NMSettUtilStorageByUuidHead *sbuh; + NMSettUtilStorageByUuidHead *sbuh2; + + if (storage->connection) + nm_assert (nm_streq0 (nm_connection_get_uuid (storage->connection), uuid)); + + if (!g_hash_table_lookup_extended (priv->storages.idx_by_uuid, &uuid, (gpointer *) &sbuh, (gpointer *) &sbuh2)) + nm_assert_not_reached (); + + nm_assert (sbuh); + nm_assert (nm_streq (uuid, sbuh->uuid)); + nm_assert (sbuh == sbuh2); + nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &storage->parent._storage_by_uuid_lst)); + + if (c_list_first (&sbuh->_storage_by_uuid_lst_head) == &storage->parent._storage_by_uuid_lst) + n_uuid++; + } else if (storage->unmanaged_spec) { + nm_assert (strlen (storage->unmanaged_spec) > 0); + g_hash_table_add (h_unmanaged, storage->unmanaged_spec); + } else if (storage->unrecognized_spec) { + nm_assert (strlen (storage->unrecognized_spec) > 0); + g_hash_table_add (h_unrecognized, storage->unrecognized_spec); + } else + nm_assert_not_reached (); + + nm_assert (!storage->connection); + } - update_connection (self, NULL, path, connection, TRUE, NULL, NULL); + nm_assert (g_hash_table_size (priv->storages.idx_by_uuid) == n_uuid); + + if (unhandled_specs_consistent) { + nm_assert (nm_utils_hashtable_same_keys (h_unmanaged, priv->unmanaged_specs)); + nm_assert (nm_utils_hashtable_same_keys (h_unrecognized, priv->unrecognized_specs)); + } + } +#endif } -static void -connection_removed_cb (NMSettingsConnection *obj, gpointer user_data) +/*****************************************************************************/ + +static NMSIfcfgRHStorage * +_load_file (NMSIfcfgRHPlugin *self, + const char *filename, + GError **error) { - g_hash_table_remove (SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) user_data)->connections, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (obj))); + gs_unref_object NMConnection *connection = NULL; + gs_free_error GError *load_error = NULL; + gs_free char *unhandled_spec = NULL; + gboolean load_error_ignore; + struct stat st; + + if (stat (filename, &st) != 0) { + int errsv = errno; + + if (error) { + nm_utils_error_set_errno (error, errsv, + "failure to stat file \%s\": %s", + filename); + } else + _LOGT ("load[%s]: failure to stat file: %s", filename, nm_strerror_native (errsv)); + return NULL; + } + + connection = connection_from_file (filename, + &unhandled_spec, + &load_error, + &load_error_ignore); + if (load_error) { + if (error) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, + "failure to read file \"%s\": %s", + filename, load_error->message); + } else { + _NMLOG (load_error_ignore ? LOGL_TRACE : LOGL_WARN, + "load[%s]: failure to read file: %s", filename, load_error->message); + } + return NULL; + } + + if (unhandled_spec) { + const char *unmanaged_spec; + const char *unrecognized_spec; + + if (!nms_ifcfg_rh_util_parse_unhandled_spec (unhandled_spec, + &unmanaged_spec, + &unrecognized_spec)) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, + "invalid unhandled spec \"%s\"", + unhandled_spec); + nm_assert_not_reached (); + return NULL; + } + return nms_ifcfg_rh_storage_new_unhandled (self, + filename, + unmanaged_spec, + unrecognized_spec); + } + + return nms_ifcfg_rh_storage_new_connection (self, + filename, + g_steal_pointer (&connection), + &st.st_mtim); } static void -remove_connection (SettingsPluginIfcfg *self, NMIfcfgConnection *connection) +_load_dir (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - gboolean unmanaged, unrecognized; + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_free_error GError *local = NULL; + const char *f_filename; + GDir *dir; - g_return_if_fail (self != NULL); - g_return_if_fail (connection != NULL); + dir = g_dir_open (IFCFG_DIR, 0, &local); + if (!dir) { + _LOGT ("Could not read directory '%s': %s", IFCFG_DIR, local->message); + return; + } - _LOGI ("remove "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection)); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); - unmanaged = !!nm_ifcfg_connection_get_unmanaged_spec (connection); - unrecognized = !!nm_ifcfg_connection_get_unrecognized_spec (connection); + while ((f_filename = g_dir_read_name (dir))) { + gs_free char *full_path = NULL; + NMSIfcfgRHStorage *storage; + char *full_filename; - g_object_ref (connection); - g_hash_table_remove (priv->connections, nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))); - if (!unmanaged && !unrecognized) - nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection)); - g_object_unref (connection); + full_path = g_build_filename (IFCFG_DIR, f_filename, NULL); + full_filename = utils_detect_ifcfg_path (full_path, TRUE); + if (!full_filename) + continue; - /* Emit changes _after_ removing the connection */ - if (unmanaged) - _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); - if (unrecognized) - _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); + if (!g_hash_table_add (dupl_filenames, full_filename)) + continue; + + nm_assert (!nm_sett_util_storages_lookup_by_filename (storages, full_filename)); + + storage = _load_file (self, + full_filename, + NULL); + if (storage) + nm_sett_util_storages_add_take (storages, storage); + } + g_dir_close (dir); } -static NMIfcfgConnection * -find_by_path (SettingsPluginIfcfg *self, const char *path) +static void +_storages_consolidate (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages_new, + gboolean replace_all, + GHashTable *storages_replaced, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - GHashTableIter iter; - NMSettingsConnection *candidate = NULL; + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted); + gs_unref_ptrarray GPtrArray *storages_modified = NULL; + CList storages_deleted; + NMSIfcfgRHStorage *storage_safe; + NMSIfcfgRHStorage *storage_new; + NMSIfcfgRHStorage *storage_old; + NMSIfcfgRHStorage *storage; + guint i; - g_return_val_if_fail (path != NULL, NULL); + /* when we reload all files, we must signal add/update/modify of profiles one-by-one. + * NMSettings then goes ahead and emits further signals and a lot of things happen. + * + * So, first, emit an update of the unmanaged/unrecognized specs that contains *all* + * the unmanaged/unrecognized devices from before and after. Since both unmanaged/unrecognized + * specs have the meaning of "not doing something", it makes sense that we temporarily + * disable that action for the sum of before and after. */ + _unhandled_specs_merge_storages (self, storages_new); + + storages_modified = g_ptr_array_new_with_free_func (g_object_unref); + c_list_init (&storages_deleted); + + c_list_for_each_entry (storage_old, &priv->storages._storage_lst_head, parent._storage_lst) + storage_old->dirty = TRUE; + + c_list_for_each_entry_safe (storage_new, storage_safe, &storages_new->_storage_lst_head, parent._storage_lst) { + storage_old = nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_ifcfg_rh_storage_get_filename (storage_new)); + + nm_sett_util_storages_steal (storages_new, storage_new); + + if ( !storage_old + || !nms_ifcfg_rh_storage_equal_type (storage_new, storage_old)) { + if (storage_old) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + if (nms_ifcfg_rh_storage_get_uuid_opt (storage_old)) + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst); + else + nms_ifcfg_rh_storage_destroy (storage_old); + } + storage_new->dirty = FALSE; + nm_sett_util_storages_add_take (&priv->storages, storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_new)); + continue; + } - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) { - if (g_strcmp0 (path, nm_settings_connection_get_filename (candidate)) == 0) - return NM_IFCFG_CONNECTION (candidate); + storage_old->dirty = FALSE; + nms_ifcfg_rh_storage_copy_content (storage_old, storage_new); + nms_ifcfg_rh_storage_destroy (storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_old)); } - return NULL; -} -static NMIfcfgConnection * -update_connection (SettingsPluginIfcfg *self, - NMConnection *source, - const char *full_path, - NMIfcfgConnection *connection, - gboolean protect_existing_connection, - GHashTable *protected_connections, - GError **error) -{ - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - NMIfcfgConnection *connection_new; - NMIfcfgConnection *connection_by_uuid; - GError *local = NULL; - const char *new_unmanaged = NULL, *old_unmanaged = NULL; - const char *new_unrecognized = NULL, *old_unrecognized = NULL; - gboolean unmanaged_changed = FALSE, unrecognized_changed = FALSE; - const char *uuid; - gboolean ignore_error = FALSE; - - g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL); - g_return_val_if_fail (full_path || source, NULL); - - if (full_path) - _LOGD ("loading from file \"%s\"...", full_path); - - /* Create a NMIfcfgConnection instance, either by reading from @full_path or - * based on @source. */ - connection_new = nm_ifcfg_connection_new (source, full_path, &local, &ignore_error); - if (!connection_new) { - /* Unexpected failure. Probably the file is invalid? */ - if ( connection - && !protect_existing_connection - && (!protected_connections || !g_hash_table_contains (protected_connections, connection))) - remove_connection (self, connection); - if (!source) { - _NMLOG (ignore_error ? LOGL_DEBUG : LOGL_WARN, - "loading \"%s\" fails: %s", full_path, local ? local->message : "(unknown reason)"); + c_list_for_each_entry_safe (storage_old, storage_safe, &priv->storages._storage_lst_head, parent._storage_lst) { + if (!storage_old->dirty) + continue; + if ( replace_all + || ( storages_replaced + && g_hash_table_contains (storages_replaced, storage_old))) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + if (nms_ifcfg_rh_storage_get_uuid_opt (storage_old)) + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst); + else + nms_ifcfg_rh_storage_destroy (storage_old); } - g_propagate_error (error, local); - return NULL; } - uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new)); - connection_by_uuid = g_hash_table_lookup (priv->connections, uuid); + /* raise events. */ - if ( connection - && connection != connection_by_uuid) { + for (i = 0; i < storages_modified->len; i++) { + storage = storages_modified->pdata[i]; + storage->dirty = TRUE; + } - if ( (protect_existing_connection && connection_by_uuid != NULL) - || (protected_connections && g_hash_table_contains (protected_connections, connection))) { - NMIfcfgConnection *conflicting = (protect_existing_connection && connection_by_uuid != NULL) ? connection_by_uuid : connection; + for (i = 0; i < storages_modified->len; i++) { + gs_unref_object NMConnection *connection = NULL; + storage = storages_modified->pdata[i]; - if (source) - _LOGW ("cannot update protected connection "NM_IFCFG_CONNECTION_LOG_FMT" due to conflicting UUID %s", NM_IFCFG_CONNECTION_LOG_ARG (conflicting), uuid); - else - _LOGW ("cannot load %s due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, full_path, NM_IFCFG_CONNECTION_LOG_ARG (conflicting)); - g_object_unref (connection_new); - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Cannot update protected connection due to conflicting UUID"); - return NULL; + if (!storage->dirty) { + /* the entry is no longer dirty. In the meantime we already emited + * another signal for it. */ + continue; + } + storage->dirty = FALSE; + if (storage != nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_ifcfg_rh_storage_get_filename (storage))) { + /* hm? The profile was deleted in the meantime? That is only possible + * if the signal handler called again into the plugin. In any case, the event + * was already emitted. Skip. */ + continue; } - /* The new connection has a different UUID then the original one that we - * are about to update. Remove @connection. */ - remove_connection (self, connection); + connection = nms_ifcfg_rh_storage_steal_connection (storage); + if (!connection) { + nm_assert (!nms_ifcfg_rh_storage_get_uuid_opt (storage)); + continue; + } + + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (nms_ifcfg_rh_storage_get_uuid_opt (storage)); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + connection, + user_data); } - /* Check if the found connection with the same UUID is not protected from updating. */ - if ( connection_by_uuid - && ( (!connection && protect_existing_connection) - || (protected_connections && g_hash_table_contains (protected_connections, connection_by_uuid)))) { - if (source) - _LOGW ("cannot update connection due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_by_uuid)); - else - _LOGW ("cannot load %s due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, full_path, NM_IFCFG_CONNECTION_LOG_ARG (connection_by_uuid)); - g_object_unref (connection_new); - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Skip updating protected connection during reload"); - return NULL; + while ((storage = c_list_first_entry (&storages_deleted, NMSIfcfgRHStorage, parent._storage_lst))) { + c_list_unlink (&storage->parent._storage_lst); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + NULL, + user_data); + nms_ifcfg_rh_storage_destroy (storage); } +} - /* Evaluate unmanaged/unrecognized flags. */ - if (connection_by_uuid) - old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (connection_by_uuid); - new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (connection_new); - unmanaged_changed = g_strcmp0 (old_unmanaged, new_unmanaged); - - if (connection_by_uuid) - old_unrecognized = nm_ifcfg_connection_get_unrecognized_spec (connection_by_uuid); - new_unrecognized = nm_ifcfg_connection_get_unrecognized_spec (connection_new); - unrecognized_changed = g_strcmp0 (old_unrecognized, new_unrecognized); - - if (connection_by_uuid) { - const char *old_path; - - old_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)); - - if ( !unmanaged_changed - && !unrecognized_changed - && nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)), - nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)), - NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | - NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) { - if ( old_path - && !nm_streq0 (old_path, full_path)) { - _LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT" without other changes", - nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)), - NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - } - } else { +/*****************************************************************************/ + +static void +load_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_ifcfg_rh_storage_destroy); + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_unref_hashtable GHashTable *storages_replaced = NULL; + gs_unref_hashtable GHashTable *loaded_uuids = NULL; + const char *loaded_uuid; + GHashTableIter h_iter; + gsize i; + + if (n_entries == 0) + return; - /******************************************************* - * UPDATE - *******************************************************/ + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - if (source) - _LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT" from %s", NM_IFCFG_CONNECTION_LOG_ARG (connection_new), NM_IFCFG_CONNECTION_LOG_PATH (old_path)); - else if (nm_streq0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new)))) - _LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - else if (old_path) - _LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT, old_path, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - else - _LOGI ("update and persist "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - - g_object_set (connection_by_uuid, - NM_IFCFG_CONNECTION_UNMANAGED_SPEC, new_unmanaged, - NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, new_unrecognized, - NULL); - - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid), - nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)), - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - "ifcfg-update", - &local)) { - /* Shouldn't ever get here as 'connection_new' was verified by the reader already - * and the UUID did not change. */ - g_assert_not_reached (); - } - g_assert_no_error (local); - - if (new_unmanaged || new_unrecognized) { - if (!old_unmanaged && !old_unrecognized) { - /* ref connection first, because we put it into priv->connections below. - * Emitting signal-removed might otherwise delete it. */ - g_object_ref (connection_by_uuid); - - /* Unexport the connection by telling the settings service it's - * been removed. - */ - nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection_by_uuid)); - - /* signal_remove() will end up removing the connection from our hash, - * so add it back now. - */ - g_hash_table_insert (priv->connections, - g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_by_uuid))), - connection_by_uuid /* we took reference above and pass it on */); - } - } else { - if (old_unmanaged /* && !new_unmanaged */) { - _LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" and its device because NM_CONTROLLED was true.", - NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (connection_by_uuid)); - } else if (old_unrecognized /* && !new_unrecognized */) { - _LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" because it is now a recognized type.", - NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (connection_by_uuid)); - } - } + loaded_uuids = g_hash_table_new (nm_str_hash, g_str_equal); + + storages_replaced = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL); + + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *const entry = &entries[i]; + gs_free_error GError *local = NULL; + const char *full_filename; + const char *uuid; + gs_free char *full_filename_keep = NULL; + NMSettingsPluginConnectionLoadEntry *dupl_content_entry; + gs_unref_object NMSIfcfgRHStorage *storage = NULL; + + if (entry->handled) + continue; - if (unmanaged_changed) - _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); - if (unrecognized_changed) - _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); + if (entry->filename[0] != '/') + continue; + + full_filename_keep = utils_detect_ifcfg_path (entry->filename, FALSE); + + if (!full_filename_keep) { + if (nm_utils_file_is_in_path (entry->filename, IFCFG_DIR)) { + nm_utils_error_set (&entry->error, + NM_UTILS_ERROR_UNKNOWN, + ("path is not a valid name for an ifcfg-rh file")); + entry->handled = TRUE; + } + continue; } - nm_settings_connection_set_filename (NM_SETTINGS_CONNECTION (connection_by_uuid), full_path); - g_object_unref (connection_new); - return connection_by_uuid; - } else { - /******************************************************* - * ADD - *******************************************************/ + if ((dupl_content_entry = g_hash_table_lookup (dupl_filenames, full_filename_keep))) { + /* we already visited this file. */ + entry->handled = dupl_content_entry->handled; + if (dupl_content_entry->error) { + g_set_error_literal (&entry->error, + dupl_content_entry->error->domain, + dupl_content_entry->error->code, + dupl_content_entry->error->message); + } + continue; + } - if (source) - _LOGI ("add connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - else - _LOGI ("new connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - g_hash_table_insert (priv->connections, - g_strdup (uuid), - connection_new /* take reference */); - - g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED, - G_CALLBACK (connection_removed_cb), - self); - - if (nm_ifcfg_connection_get_unmanaged_spec (connection_new)) { - _LOGI ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" due to NM_CONTROLLED=no. Unmanaged: %s.", - NM_IFCFG_CONNECTION_LOG_ARG (connection_new), - nm_ifcfg_connection_get_unmanaged_spec (connection_new)); - } else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new)) - _LOGW ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" of unrecognized type.", NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - - /* watch changes of ifcfg hardlinks */ - g_signal_connect (G_OBJECT (connection_new), "ifcfg-changed", - G_CALLBACK (connection_ifcfg_changed), self); - - if (!source) { - /* Only raise the signal if we were called without source, i.e. if we read the connection from file. - * Otherwise, we were called by add_connection() which does not expect the signal. */ - if (nm_ifcfg_connection_get_unmanaged_spec (connection_new)) - _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); - else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new)) - _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); - else { - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (connection_new)); + entry->handled = TRUE; + + full_filename = full_filename_keep; + if (!g_hash_table_insert (dupl_filenames, g_steal_pointer (&full_filename_keep), entry)) + nm_assert_not_reached (); + + storage = _load_file (self, + full_filename, + &local); + if (!storage) { + if (nm_utils_file_stat (full_filename, NULL) == -ENOENT) { + NMSIfcfgRHStorage *storage2; + + /* the file does not exist. We take that as indication to unload the file + * that was previously loaded... */ + storage2 = nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename); + if (storage2) + g_hash_table_add (storages_replaced, g_object_ref (storage2)); + continue; } + g_propagate_error (&entry->error, g_steal_pointer (&local)); + continue; } - return connection_new; + + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + if (uuid) + g_hash_table_add (loaded_uuids, (char *) uuid); + + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage)); } -} -static void -ifcfg_dir_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) -{ - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (user_data); - char *path, *ifcfg_path; - NMIfcfgConnection *connection; - - path = g_file_get_path (file); - - ifcfg_path = utils_detect_ifcfg_path (path, FALSE); - _LOGD ("ifcfg_dir_changed(%s) = %d // %s", path, event_type, ifcfg_path ?: "(none)"); - if (ifcfg_path) { - connection = find_by_path (plugin, ifcfg_path); - switch (event_type) { - case G_FILE_MONITOR_EVENT_DELETED: - if (connection) - remove_connection (plugin, connection); - break; - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - /* Update or new */ - update_connection (plugin, NULL, ifcfg_path, connection, TRUE, NULL, NULL); - break; - default: - break; + /* now we visit all UUIDs that are about to change... */ + g_hash_table_iter_init (&h_iter, loaded_uuids); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &loaded_uuid, NULL)) { + NMSIfcfgRHStorage *storage; + NMSettUtilStorageByUuidHead *sbuh; + + sbuh = nm_sett_util_storages_lookup_by_uuid (&priv->storages, loaded_uuid); + if (!sbuh) + continue; + + c_list_for_each_entry (storage, &sbuh->_storage_by_uuid_lst_head, parent._storage_by_uuid_lst) { + const char *full_filename = nms_ifcfg_rh_storage_get_filename (storage); + gs_unref_object NMSIfcfgRHStorage *storage_new = NULL; + gs_free_error GError *local = NULL; + + if (g_hash_table_contains (dupl_filenames, full_filename)) { + /* already re-loaded. */ + continue; + } + + /* @storage has a UUID that was just loaded from disk, but we have an entry in cache. + * Reload that file too despite not being told to do so. The reason is to get + * the latest file timestamp so that we get the priorities right. */ + + storage_new = _load_file (self, + full_filename, + &local); + if ( storage_new + && !nm_streq0 (loaded_uuid, nms_ifcfg_rh_storage_get_uuid_opt (storage_new))) { + /* the file now references a different UUID. We are not told to reload + * that file, so this means the existing storage (with the previous + * filename and UUID tuple) is no longer valid. */ + g_clear_object (&storage_new); + } + + g_hash_table_add (storages_replaced, g_object_ref (storage)); + if (storage_new) + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage_new)); } - g_free (ifcfg_path); } - g_free (path); + + nm_clear_pointer (&loaded_uuids, g_hash_table_destroy); + nm_clear_pointer (&dupl_filenames, g_hash_table_destroy); + + _storages_consolidate (self, + &storages_new, + FALSE, + storages_replaced, + callback, + user_data); } static void -setup_ifcfg_monitoring (SettingsPluginIfcfg *plugin) +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin); - GFile *file; - GFileMonitor *monitor; - - file = g_file_new_for_path (IFCFG_DIR "/"); - monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - - if (monitor) { - priv->ifcfg_monitor_id = g_signal_connect (monitor, "changed", - G_CALLBACK (ifcfg_dir_changed), plugin); - priv->ifcfg_monitor = monitor; - } -} + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_ifcfg_rh_storage_destroy); -static GHashTable * -_paths_from_connections (GHashTable *connections) -{ - GHashTableIter iter; - NMIfcfgConnection *connection; - GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); + nm_assert_self (self, TRUE); - g_hash_table_iter_init (&iter, connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { - const char *path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection)); + _load_dir (self, &storages_new); - if (path) - g_hash_table_add (paths, (void *) path); - } - return paths; + _storages_consolidate (self, + &storages_new, + TRUE, + NULL, + callback, + user_data); + + nm_assert_self (self, FALSE); } -static int -_sort_paths (const char **f1, const char **f2, GHashTable *paths) +static void +load_connections_done (NMSettingsPlugin *plugin) { - struct stat st; - gboolean c1, c2; - gint64 m1, m2; - - c1 = !!g_hash_table_contains (paths, *f1); - c2 = !!g_hash_table_contains (paths, *f2); - if (c1 != c2) - return c1 ? -1 : 1; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); - m1 = stat (*f1, &st) == 0 ? (gint64) st.st_mtime : G_MININT64; - m2 = stat (*f2, &st) == 0 ? (gint64) st.st_mtime : G_MININT64; - if (m1 != m2) - return m1 > m2 ? -1 : 1; + /* at the beginning of a load, we emit a change signal for unmanaged/unrecognized + * specs that contain the sum of before and after (_unhandled_specs_merge_storages()). + * + * The idea is that while we emit signals about changes to connection, we have + * the sum of all unmanaged/unrecognized devices from before and after. + * + * This if triggered at the end, to reset the specs. */ + _unhandled_specs_reset (self); - return strcmp (*f1, *f2); + nm_assert_self (self, TRUE); } -static void -read_connections (SettingsPluginIfcfg *plugin) -{ - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin); - GDir *dir; - GError *err = NULL; - const char *item; - GHashTable *alive_connections; - GHashTableIter iter; - NMIfcfgConnection *connection; - GPtrArray *dead_connections = NULL; - guint i; - GPtrArray *filenames; - GHashTable *paths; +/*****************************************************************************/ - dir = g_dir_open (IFCFG_DIR, 0, &err); - if (!dir) { - _LOGW ("Could not read directory '%s': %s", IFCFG_DIR, err->message); - g_error_free (err); - return; +static gboolean +add_connection (NMSettingsPlugin *plugin, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) +{ + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + gs_unref_object NMSIfcfgRHStorage *storage = NULL; + gs_unref_object NMConnection *reread = NULL; + gs_free char *full_filename = NULL; + GError *local = NULL; + gboolean reread_same; + struct timespec mtime; + + nm_assert_self (self, TRUE); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (out_storage && !*out_storage); + nm_assert (out_connection && !*out_connection); + + if (!nms_ifcfg_rh_writer_write_connection (connection, + IFCFG_DIR, + NULL, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, NULL), + &full_filename, + &reread, + &reread_same, + &local)) { + _LOGT ("commit: %s (%s): failed to add: %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + local->message); + g_propagate_error (error, local); + return FALSE; } - alive_connections = g_hash_table_new (nm_direct_hash, NULL); + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - filenames = g_ptr_array_new_with_free_func (g_free); - while ((item = g_dir_read_name (dir))) { - char *full_path, *real_path; + nm_assert (full_filename && full_filename[0] == '/'); - full_path = g_build_filename (IFCFG_DIR, item, NULL); - real_path = utils_detect_ifcfg_path (full_path, TRUE); + _LOGT ("commit: %s (%s) added as \"%s\"", + nm_connection_get_uuid (reread), + nm_connection_get_id (reread), + full_filename); - if (real_path) - g_ptr_array_add (filenames, real_path); - g_free (full_path); - } - g_dir_close (dir); + storage = nms_ifcfg_rh_storage_new_connection (self, + full_filename, + g_steal_pointer (&reread), + nm_sett_util_stat_mtime (full_filename, FALSE, &mtime)); - /* While reloading, we don't replace connections that we already loaded while - * iterating over the files. - * - * To have sensible, reproducible behavior, sort the paths by last modification - * time preferring older files. - */ - paths = _paths_from_connections (priv->connections); - g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths); - g_hash_table_destroy (paths); - - for (i = 0; i < filenames->len; i++) { - connection = update_connection (plugin, NULL, filenames->pdata[i], NULL, FALSE, alive_connections, NULL); - if (connection) - g_hash_table_add (alive_connections, connection); - } - g_ptr_array_free (filenames, TRUE); - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { - if ( !g_hash_table_contains (alive_connections, connection) - && nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection))) { - if (!dead_connections) - dead_connections = g_ptr_array_new (); - g_ptr_array_add (dead_connections, connection); - } - } - g_hash_table_destroy (alive_connections); + nm_sett_util_storages_add_take (&priv->storages, g_object_ref (storage)); - if (dead_connections) { - for (i = 0; i < dead_connections->len; i++) - remove_connection (plugin, dead_connections->pdata[i]); - g_ptr_array_free (dead_connections, TRUE); - } + *out_connection = nms_ifcfg_rh_storage_steal_connection (storage); + *out_storage = NM_SETTINGS_STORAGE (g_steal_pointer (&storage)); + + nm_assert_self (self, TRUE); + + return TRUE; } -static GSList * -get_connections (NMSettingsPlugin *config) +static gboolean +update_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage_x, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) { - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin); - GSList *list = NULL; - GHashTableIter iter; - NMIfcfgConnection *connection; - - if (!priv->initialized) { - if (nm_config_get_monitor_connection_files (nm_config_get ())) - setup_ifcfg_monitoring (plugin); - read_connections (plugin); - priv->initialized = TRUE; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + NMSIfcfgRHStorage *storage = NMS_IFCFG_RH_STORAGE (storage_x); + const char *full_filename; + const char *uuid; + GError *local = NULL; + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same; + struct timespec mtime; + + nm_assert_self (self, TRUE); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (NMS_IS_IFCFG_RH_STORAGE (storage)); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (!error || !*error); + + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + + nm_assert (uuid && nm_streq0 (uuid, nm_connection_get_uuid (connection))); + + full_filename = nms_ifcfg_rh_storage_get_filename (storage); + + nm_assert (full_filename); + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); + + if (!nms_ifcfg_rh_writer_write_connection (connection, + IFCFG_DIR, + full_filename, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, full_filename), + NULL, + &reread, + &reread_same, + &local)) { + _LOGT ("commit: failure to write %s (%s) to \"%s\": %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + full_filename, + local->message); + g_propagate_error (error, local); + return FALSE; } - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) { - if ( !nm_ifcfg_connection_get_unmanaged_spec (connection) - && !nm_ifcfg_connection_get_unrecognized_spec (connection)) - list = g_slist_prepend (list, connection); - } + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - return list; + _LOGT ("commit: \"%s\": profile %s (%s) written", + full_filename, + uuid, + nm_connection_get_id (connection)); + + storage->stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime); + + *out_storage = NM_SETTINGS_STORAGE (g_object_ref (storage)); + *out_connection = g_steal_pointer (&reread); + + nm_assert_self (self, TRUE); + + return TRUE; } static gboolean -load_connection (NMSettingsPlugin *config, - const char *filename) +delete_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage_x, + GError **error) { - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config); - NMIfcfgConnection *connection; - char *ifcfg_path; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + NMSIfcfgRHStorage *storage = NMS_IFCFG_RH_STORAGE (storage_x); + const char *operation_message; + const char *full_filename; - if (!nm_utils_file_is_in_path (filename, IFCFG_DIR)) - return FALSE; + nm_assert_self (self, TRUE); + nm_assert (!error || !*error); + nm_assert (NMS_IS_IFCFG_RH_STORAGE (storage)); - /* get the real ifcfg-path. This allows us to properly - * handle load command using a route-* file etc. */ - ifcfg_path = utils_detect_ifcfg_path (filename, FALSE); - if (!ifcfg_path) - return FALSE; + full_filename = nms_ifcfg_rh_storage_get_filename (storage); + nm_assert (full_filename); + + nm_assert (nms_ifcfg_rh_storage_get_uuid_opt (storage)); + + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); + + { + gs_free char *keyfile = utils_get_keys_path (full_filename); + gs_free char *routefile = utils_get_route_path (full_filename); + gs_free char *route6file = utils_get_route6_path (full_filename); + const char *const files[] = { full_filename, keyfile, routefile, route6file }; + gboolean any_deleted = FALSE; + gboolean any_failure = FALSE; + int i; + + for (i = 0; i < G_N_ELEMENTS (files); i++) { + int errsv; + + if (unlink (files[i]) == 0) { + any_deleted = TRUE; + continue; + } + errsv = errno; + if (errsv == ENOENT) + continue; + + _LOGW ("commit: failure to delete file \"%s\": %s", + files[i], + nm_strerror_native (errsv)); + any_failure = TRUE; + } + if (any_failure) + operation_message = "failed to delete files from disk"; + else if (any_deleted) + operation_message = "deleted from disk"; + else + operation_message = "does not exist on disk"; + } + + _LOGT ("commit: deleted \"%s\", profile %s (%s)", + full_filename, + nms_ifcfg_rh_storage_get_uuid_opt (storage), + operation_message); - connection = find_by_path (plugin, ifcfg_path); - update_connection (plugin, NULL, ifcfg_path, connection, TRUE, NULL, NULL); - if (!connection) - connection = find_by_path (plugin, ifcfg_path); + nm_sett_util_storages_steal (&priv->storages, storage); + nms_ifcfg_rh_storage_destroy (storage); - g_free (ifcfg_path); - return (connection != NULL); + nm_assert_self (self, TRUE); + + return TRUE; } +/*****************************************************************************/ + static void -reload_connections (NMSettingsPlugin *config) +_unhandled_specs_reset (NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + gs_unref_hashtable GHashTable *unmanaged_specs = NULL; + gs_unref_hashtable GHashTable *unrecognized_specs = NULL; + NMSIfcfgRHStorage *storage; + + unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + + c_list_for_each_entry (storage, &priv->storages._storage_lst_head, parent._storage_lst) { + if (storage->unmanaged_spec) + g_hash_table_add (unmanaged_specs, g_strdup (storage->unmanaged_spec)); + if (storage->unrecognized_spec) + g_hash_table_add (unrecognized_specs, g_strdup (storage->unrecognized_spec)); + } - read_connections (plugin); + if (!nm_utils_hashtable_same_keys (unmanaged_specs, priv->unmanaged_specs)) { + g_hash_table_unref (priv->unmanaged_specs); + priv->unmanaged_specs = g_steal_pointer (&unmanaged_specs); + } + if (!nm_utils_hashtable_same_keys (unrecognized_specs, priv->unrecognized_specs)) { + g_hash_table_unref (priv->unrecognized_specs); + priv->unrecognized_specs = g_steal_pointer (&unrecognized_specs); + } + + if (!unmanaged_specs) + _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); + if (!unrecognized_specs) + _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); } -static GSList * -get_unhandled_specs (NMSettingsPlugin *config, - const char *property) +static void +_unhandled_specs_merge_storages (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) config); - GSList *list = NULL, *list_iter; - GHashTableIter iter; - gpointer connection; - char *spec; - gboolean found; - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, &connection)) { - g_object_get (connection, property, &spec, NULL); - if (spec) { - /* Ignore duplicates */ - for (list_iter = list, found = FALSE; list_iter; list_iter = g_slist_next (list_iter)) { - if (g_str_equal (list_iter->data, spec)) { - found = TRUE; - break; - } - } - if (found) - g_free (spec); - else - list = g_slist_prepend (list, spec); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + gboolean unmanaged_changed = FALSE; + gboolean unrecognized_changed = FALSE; + NMSIfcfgRHStorage *storage; + + c_list_for_each_entry (storage, &storages->_storage_lst_head, parent._storage_lst) { + if ( storage->unmanaged_spec + && !g_hash_table_contains (priv->unmanaged_specs, storage->unmanaged_spec)) { + unmanaged_changed = TRUE; + g_hash_table_add (priv->unmanaged_specs, g_strdup (storage->unmanaged_spec)); + } + if ( storage->unrecognized_spec + && !g_hash_table_contains (priv->unrecognized_specs, storage->unrecognized_spec)) { + unrecognized_changed = TRUE; + g_hash_table_add (priv->unrecognized_specs, g_strdup (storage->unrecognized_spec)); } } - return list; + + if (unmanaged_changed) + _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); + if (unrecognized_changed) + _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); } static GSList * -get_unmanaged_specs (NMSettingsPlugin *config) +_unhandled_specs_from_hashtable (GHashTable *hash) { - return get_unhandled_specs (config, NM_IFCFG_CONNECTION_UNMANAGED_SPEC); + gs_free const char **keys = NULL; + GSList *list = NULL; + guint i, l; + + keys = nm_utils_strdict_get_keys (hash, TRUE, &l); + for (i = l; i > 0; ) { + i--; + list = g_slist_prepend (list, g_strdup (keys[i])); + } + return list; } static GSList * -get_unrecognized_specs (NMSettingsPlugin *config) +get_unmanaged_specs (NMSettingsPlugin *plugin) { - return get_unhandled_specs (config, NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC); + return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unmanaged_specs); } -static NMSettingsConnection * -add_connection (NMSettingsPlugin *config, - NMConnection *connection, - gboolean save_to_disk, - GError **error) +static GSList * +get_unrecognized_specs (NMSettingsPlugin *plugin) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (config); - gs_free char *path = NULL; - gs_unref_object NMConnection *reread = NULL; - - if (save_to_disk) { - if (!nms_ifcfg_rh_writer_write_connection (connection, IFCFG_DIR, NULL, &path, &reread, NULL, error)) - return NULL; - } else { - if (!nms_ifcfg_rh_writer_can_write_connection (connection, error)) - return NULL; - } - return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error)); + return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unrecognized_specs); } +/*****************************************************************************/ + static void -impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin, +impl_ifcfgrh_get_ifcfg_details (NMSIfcfgRHPlugin *self, GDBusMethodInvocation *context, const char *in_ifcfg) { - NMIfcfgConnection *connection; - NMSettingConnection *s_con; + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + gs_free char *ifcfg_path = NULL; + NMSIfcfgRHStorage *storage; const char *uuid; const char *path; - gs_free char *ifcfg_path = NULL; - if (!g_path_is_absolute (in_ifcfg)) { + if (in_ifcfg[0] != '/') { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -719,10 +916,8 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin, return; } - connection = find_by_path (plugin, ifcfg_path); - if ( !connection - || nm_ifcfg_connection_get_unmanaged_spec (connection) - || nm_ifcfg_connection_get_unrecognized_spec (connection)) { + storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, ifcfg_path); + if (!storage) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -730,25 +925,23 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin, return; } - s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection))); - if (!s_con) { - g_dbus_method_invocation_return_error (context, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "unable to retrieve the connection setting"); - return; - } - - uuid = nm_setting_connection_get_uuid (s_con); + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); if (!uuid) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "unable to get the UUID"); + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "ifcfg file '%s' not managed by NetworkManager", in_ifcfg); return; } - path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); + /* It is ugly that the ifcfg-rh plugin needs to call back into NMSettings this + * way. + * There are alternatives (like invoking a signal), but they are all significant + * extra code (and performance overhead). So the quick and dirty solution here + * is likely to be simpler than getting this right (also from point of readability!). + */ + path = nm_settings_get_dbus_path_for_uuid (nm_settings_get (), uuid); + if (!path) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, @@ -764,9 +957,9 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin, /*****************************************************************************/ static void -_dbus_clear (SettingsPluginIfcfg *self) +_dbus_clear (NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); guint id; nm_clear_g_signal_handler (priv->dbus.connection, &priv->dbus.signal_id); @@ -788,7 +981,7 @@ _dbus_connection_closed (GDBusConnection *connection, gpointer user_data) { _LOGW ("dbus: %s bus closed", IFCFGRH1_BUS_NAME); - _dbus_clear (SETTINGS_PLUGIN_IFCFG (user_data)); + _dbus_clear (NMS_IFCFG_RH_PLUGIN (user_data)); /* Retry or recover? */ } @@ -803,25 +996,27 @@ _method_call (GDBusConnection *connection, GDBusMethodInvocation *invocation, gpointer user_data) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data); - const char *ifcfg; - - if ( !nm_streq (interface_name, IFCFGRH1_IFACE1_NAME) - || !nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) { - g_dbus_method_invocation_return_error (invocation, - G_DBUS_ERROR, - G_DBUS_ERROR_UNKNOWN_METHOD, - "Unknown method %s", - method_name); - return; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (user_data); + + if (nm_streq (interface_name, IFCFGRH1_IFACE1_NAME)) { + if (nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) { + const char *ifcfg; + + g_variant_get (parameters, "(&s)", &ifcfg); + impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg); + return; + } } - g_variant_get (parameters, "(&s)", &ifcfg); - impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg); + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s", + method_name); } static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO ( - IFCFGRH1_BUS_NAME, + IFCFGRH1_IFACE1_NAME, .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( NM_DEFINE_GDBUS_METHOD_INFO ( IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS, @@ -842,8 +1037,8 @@ _dbus_request_name_done (GObject *source_object, gpointer user_data) { GDBusConnection *connection = G_DBUS_CONNECTION (source_object); - SettingsPluginIfcfg *self; - SettingsPluginIfcfgPrivate *priv; + NMSIfcfgRHPlugin *self; + NMSIfcfgRHPluginPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *ret = NULL; guint32 result; @@ -852,8 +1047,8 @@ _dbus_request_name_done (GObject *source_object, if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; - self = SETTINGS_PLUGIN_IFCFG (user_data); - priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + self = NMS_IFCFG_RH_PLUGIN (user_data); + priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); g_clear_object (&priv->dbus.cancellable); @@ -900,8 +1095,8 @@ _dbus_create_done (GObject *source_object, GAsyncResult *res, gpointer user_data) { - SettingsPluginIfcfg *self; - SettingsPluginIfcfgPrivate *priv; + NMSIfcfgRHPlugin *self; + NMSIfcfgRHPluginPrivate *priv; gs_free_error GError *error = NULL; GDBusConnection *connection; @@ -909,8 +1104,8 @@ _dbus_create_done (GObject *source_object, if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; - self = SETTINGS_PLUGIN_IFCFG (user_data); - priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + self = NMS_IFCFG_RH_PLUGIN (user_data); + priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); g_clear_object (&priv->dbus.cancellable); @@ -944,9 +1139,9 @@ _dbus_create_done (GObject *source_object, } static void -_dbus_setup (SettingsPluginIfcfg *self) +_dbus_setup (NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); gs_free char *address = NULL; gs_free_error GError *error = NULL; @@ -974,9 +1169,9 @@ config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, - SettingsPluginIfcfg *self) + NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfgPrivate *priv; + NMSIfcfgRHPluginPrivate *priv; /* If the dbus connection for some reason is borked the D-Bus service * won't be offered. @@ -988,7 +1183,7 @@ config_changed_cb (NMConfig *config, | NM_CONFIG_CHANGE_CAUSE_SIGUSR1)) return; - priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); if ( !priv->dbus.connection && !priv->dbus.cancellable) _dbus_setup (self); @@ -997,23 +1192,26 @@ config_changed_cb (NMConfig *config, /*****************************************************************************/ static void -settings_plugin_ifcfg_init (SettingsPluginIfcfg *plugin) +nms_ifcfg_rh_plugin_init (NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + + priv->config = g_object_ref (nm_config_get ()); + + priv->unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + priv->unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); + priv->storages = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT (priv->storages, nms_ifcfg_rh_storage_destroy); } static void constructed (GObject *object) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (object); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); - G_OBJECT_CLASS (settings_plugin_ifcfg_parent_class)->constructed (object); + G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->constructed (object); - priv->config = nm_config_get (); - g_object_add_weak_pointer ((GObject *) priv->config, (gpointer *) &priv->config); g_signal_connect (priv->config, NM_CONFIG_SIGNAL_CONFIG_CHANGED, G_CALLBACK (config_changed_cb), @@ -1025,48 +1223,44 @@ constructed (GObject *object) static void dispose (GObject *object) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (object); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); - if (priv->config) { - g_object_remove_weak_pointer ((GObject *) priv->config, (gpointer *) &priv->config); + if (priv->config) g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); - priv->config = NULL; - } + /* FIXME(shutdown) we need a stop method so that we can unregistering the D-Bus service + * when NMSettings is shutting down, and not when the instance gets destroyed. */ _dbus_clear (self); - if (priv->connections) { - g_hash_table_destroy (priv->connections); - priv->connections = NULL; - } + nm_sett_util_storages_clear (&priv->storages); - if (priv->ifcfg_monitor) { - if (priv->ifcfg_monitor_id) - g_signal_handler_disconnect (priv->ifcfg_monitor, priv->ifcfg_monitor_id); + g_clear_object (&priv->config); - g_file_monitor_cancel (priv->ifcfg_monitor); - g_object_unref (priv->ifcfg_monitor); - } + G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->dispose (object); - G_OBJECT_CLASS (settings_plugin_ifcfg_parent_class)->dispose (object); + nm_clear_pointer (&priv->unmanaged_specs, g_hash_table_destroy); + nm_clear_pointer (&priv->unrecognized_specs, g_hash_table_destroy); } static void -settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass) +nms_ifcfg_rh_plugin_class_init (NMSIfcfgRHPluginClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass); object_class->constructed = constructed; - object_class->dispose = dispose; + object_class->dispose = dispose; - plugin_class->get_connections = get_connections; - plugin_class->add_connection = add_connection; - plugin_class->load_connection = load_connection; - plugin_class->reload_connections = reload_connections; - plugin_class->get_unmanaged_specs = get_unmanaged_specs; + plugin_class->plugin_name = "ifcfg-rh"; + plugin_class->get_unmanaged_specs = get_unmanaged_specs; plugin_class->get_unrecognized_specs = get_unrecognized_specs; + plugin_class->reload_connections = reload_connections; + plugin_class->load_connections = load_connections; + plugin_class->load_connections_done = load_connections_done; + plugin_class->add_connection = add_connection; + plugin_class->update_connection = update_connection; + plugin_class->delete_connection = delete_connection; } /*****************************************************************************/ @@ -1074,5 +1268,5 @@ settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass) G_MODULE_EXPORT NMSettingsPlugin * nm_settings_plugin_factory (void) { - return NM_SETTINGS_PLUGIN (g_object_ref (settings_plugin_ifcfg_get ())); + return g_object_new (NMS_TYPE_IFCFG_RH_PLUGIN, NULL); } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h index d815cc51..1db36083 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Dan Williams @@ -21,19 +20,19 @@ * Copyright (C) 2007 - 2008 Red Hat, Inc. */ -#ifndef _PLUGIN_H_ -#define _PLUGIN_H_ +#ifndef __NMS_IFCFG_RH_PLUGIN_H__ +#define __NMS_IFCFG_RH_PLUGIN_H__ -#define SETTINGS_TYPE_PLUGIN_IFCFG (settings_plugin_ifcfg_get_type ()) -#define SETTINGS_PLUGIN_IFCFG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfg)) -#define SETTINGS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfgClass)) -#define SETTINGS_IS_PLUGIN_IFCFG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFCFG)) -#define SETTINGS_IS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFCFG)) -#define SETTINGS_PLUGIN_IFCFG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfgClass)) +#define NMS_TYPE_IFCFG_RH_PLUGIN (nms_ifcfg_rh_plugin_get_type ()) +#define NMS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPlugin)) +#define NMS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass)) +#define NMS_IS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFCFG_RH_PLUGIN)) +#define NMS_IS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFCFG_RH_PLUGIN)) +#define NMS_IFCFG_RH_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass)) -typedef struct _SettingsPluginIfcfg SettingsPluginIfcfg; -typedef struct _SettingsPluginIfcfgClass SettingsPluginIfcfgClass; +typedef struct _NMSIfcfgRHPlugin NMSIfcfgRHPlugin; +typedef struct _NMSIfcfgRHPluginClass NMSIfcfgRHPluginClass; -GType settings_plugin_ifcfg_get_type (void); +GType nms_ifcfg_rh_plugin_get_type (void); -#endif /* _PLUGIN_H_ */ +#endif /* __NMS_IFCFG_RH_PLUGIN_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index e5423b18..900a3fc1 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -543,6 +542,37 @@ make_connection_setting (const char *file, vint64 = svGetValueInt64 (ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1); g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (int) vint64, NULL); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DEVTIMEOUT", &value); + if (v) { + vint64 = _nm_utils_ascii_str_to_int64 (v, 10, 0, ((gint64) G_MAXINT32) / 1000, -1); + if (vint64 != -1) + vint64 *= 1000; + else { + char *endptr; + double d; + + d = g_ascii_strtod (v, &endptr); + if ( errno == 0 + && endptr[0] == '\0' + && d >= 0.0) { + d *= 1000.0; + + /* We round. Yes, this is not correct to round IEEE 754 floats in general, + * but sufficient for our case where we know that NetworkManager wrote the + * setting with up to 3 digits for the milliseconds. */ + d += 0.5; + if ( d >= 0.0 + && d <= (double) G_MAXINT32) + vint64 = (gint64) d; + } + } + if (vint64 == -1) + PARSE_WARNING ("invalid DEVTIMEOUT setting"); + else + g_object_set (s_con, NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, (int) vint64, NULL); + } + i_val = NM_SETTING_CONNECTION_MDNS_DEFAULT; if (!svGetValueEnum (ifcfg, "MDNS", nm_setting_connection_mdns_get_type (), @@ -1906,7 +1936,10 @@ make_ip6_setting (shvarFile *ifcfg, gs_unref_object NMSettingIPConfig *s_ip6 = NULL; const char *v; gs_free char *value = NULL; - gboolean ipv6init, ipv6forwarding, dhcp6 = FALSE; + gboolean ipv6init; + gboolean ipv6forwarding; + gboolean disabled; + gboolean dhcp6 = FALSE; char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; const char *ipv6addr, *ipv6addr_secondaries; gs_free char *ipv6addr_to_free = NULL; @@ -1964,6 +1997,7 @@ make_ip6_setting (shvarFile *ifcfg, /* Find out method property */ /* Is IPV6 enabled? Set method to "ignored", when not enabled */ + disabled = svGetValueBoolean(ifcfg, "IPV6_DISABLED", FALSE); nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "IPV6INIT", &value); ipv6init = svGetValueBoolean (ifcfg, "IPV6INIT", FALSE); @@ -1972,8 +2006,10 @@ make_ip6_setting (shvarFile *ifcfg, ipv6init = svGetValueBoolean (network_ifcfg, "IPV6INIT", FALSE); } - if (!ipv6init) - method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; /* IPv6 is disabled */ + if (disabled) + method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED; + else if (!ipv6init) + method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; else { ipv6forwarding = svGetValueBoolean (ifcfg, "IPV6FORWARDING", FALSE); nm_clear_g_free (&value); @@ -2039,7 +2075,8 @@ make_ip6_setting (shvarFile *ifcfg, NULL); /* Don't bother to read IP, DNS and routes when IPv6 is disabled */ - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) return NM_SETTING (g_steal_pointer (&s_ip6)); nm_clear_g_free (&value); @@ -3071,12 +3108,17 @@ eap_tls_reader (const char *eap_method, gs_unref_bytes GBytes *privkey = NULL; gs_unref_bytes GBytes *client_cert = NULL; gs_free char *identity_free = NULL; + gs_free char *value_to_free = NULL; + const char *client_cert_var; + const char *client_cert_prop; + NMSetting8021xCKFormat format; g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free), NULL); + /* CA certificate */ if (!_cert_set_from_ifcfg (s_8021x, ifcfg, phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT", @@ -3090,6 +3132,7 @@ eap_tls_reader (const char *eap_method, phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD", phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD); + /* Private key */ if (!_cert_set_from_ifcfg (s_8021x, ifcfg, phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", @@ -3102,35 +3145,39 @@ eap_tls_reader (const char *eap_method, keys_ifcfg, phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : "IEEE_8021X_PRIVATE_KEY_PASSWORD", phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); - if (!privkey) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing %s for EAP method '%s'.", - phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", - eap_method); - return FALSE; - } + /* Client certificate */ + client_cert_var = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT"; + client_cert_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT; if (!_cert_set_from_ifcfg (s_8021x, ifcfg, - phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", - phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT, + client_cert_var, + client_cert_prop, &client_cert, error)) return FALSE; - /* FIXME: writer does not actually write IEEE_8021X_CLIENT_CERT_PASSWORD and other - * certificate related passwords. It should, because otherwise persisting such profiles - * to ifcfg looses information. As this currently only matters for PKCS11 URIs, it seems - * a seldom used feature so that it is not fixed yet. */ _secret_set_from_ifcfg (s_8021x, ifcfg, keys_ifcfg, phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD", phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD); - if (!client_cert) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing certificate for EAP method '%s'.", - eap_method); - return FALSE; + + /* In the past when the private key and client certificate + * were the same PKCS #12 file we used to write only the + * private key variable. Still support that even if it means + * that we have to look into the file content, which makes + * the connection not self-contained. + */ + if ( !client_cert + && privkey + && !svGetValue (ifcfg, client_cert_var, &value_to_free)) { + if (phase2) + format = nm_setting_802_1x_get_phase2_private_key_format (s_8021x); + else + format = nm_setting_802_1x_get_private_key_format (s_8021x); + + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) + g_object_set (s_8021x, client_cert_prop, privkey, NULL); } return TRUE; @@ -4369,9 +4416,11 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) gboolean autoneg = FALSE; guint32 speed = 0; const char *duplex = NULL; + gboolean wired_found = FALSE; ethtool_opts = svGetValue (ifcfg, "ETHTOOL_OPTS", ðtool_opts_free); if (ethtool_opts) { + wired_found = TRUE; /* WAKE_ON_LAN_IGNORE is inferred from a specified but empty ETHTOOL_OPTS */ if (!ethtool_opts[0]) wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; @@ -4395,7 +4444,9 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) } /* ETHTOOL_WAKE_ON_LAN = ignore overrides WoL settings in ETHTOOL_OPTS */ - tmp = svGetValueStr (ifcfg, "ETHTOOL_WAKE_ON_LAN", &wol_value_free); + tmp = svGetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN", &wol_value_free); + if (tmp) + wired_found = TRUE; if (nm_streq0 (tmp, "ignore")) wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; else if (tmp) @@ -4408,6 +4459,10 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) } s_wired = nm_connection_get_setting_wired (connection); + if (!s_wired && wired_found) { + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + } if (s_wired) { g_object_set (s_wired, NM_SETTING_WIRED_WAKE_ON_LAN, wol_flags, @@ -4433,86 +4488,100 @@ make_wired_setting (shvarFile *ifcfg, gs_unref_object NMSettingWired *s_wired = NULL; const char *cvalue; gs_free char *value = NULL; - char *nettype; + gboolean found = FALSE; s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); - value = svGetValueStr_cp (ifcfg, "MTU"); - if (value) { + cvalue = svGetValue (ifcfg, "MTU", &value); + if (cvalue) { int mtu; - mtu = _nm_utils_ascii_str_to_int64 (value, 0, 0, 65535, -1); + mtu = _nm_utils_ascii_str_to_int64 (cvalue, 0, 0, 65535, -1); if (mtu >= 0) g_object_set (s_wired, NM_SETTING_WIRED_MTU, (guint) mtu, NULL); else - PARSE_WARNING ("invalid MTU '%s'", value); + PARSE_WARNING ("invalid MTU '%s'", cvalue); nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "HWADDR"); + value = svGetValue_cp (ifcfg, "HWADDR"); if (value) { - value = g_strstrip (value); - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL); + if (value[0] != '\0') { + value = g_strstrip (value); + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL); + } nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "SUBCHANNELS"); - if (value) { - const char *p = value; - gboolean success = TRUE; - - /* basic sanity checks */ - while (*p) { - if (!g_ascii_isxdigit (*p) && (*p != ',') && (*p != '.')) { - PARSE_WARNING ("invalid SUBCHANNELS '%s'", value); - success = FALSE; - break; + cvalue = svGetValue (ifcfg, "SUBCHANNELS", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + const char *p = cvalue; + gboolean success = TRUE; + + /* basic sanity checks */ + while (*p) { + if (!g_ascii_isxdigit (*p) && (*p != ',') && (*p != '.')) { + PARSE_WARNING ("invalid SUBCHANNELS '%s'", cvalue); + success = FALSE; + break; + } + p++; } - p++; - } - if (success) { - gs_free const char **chans = NULL; - guint32 num_chans; + if (success) { + gs_free const char **chans = NULL; + guint32 num_chans; - chans = nm_utils_strsplit_set (value, ","); - num_chans = NM_PTRARRAY_LEN (chans); - if (num_chans < 2 || num_chans > 3) { - PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", - value, (unsigned) NM_PTRARRAY_LEN (chans)); - } else - g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, chans, NULL); + chans = nm_utils_strsplit_set (cvalue, ","); + num_chans = NM_PTRARRAY_LEN (chans); + if (num_chans < 2 || num_chans > 3) { + PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", + cvalue, (unsigned) NM_PTRARRAY_LEN (chans)); + } else + g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, chans, NULL); + } } nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "PORTNAME"); - if (value) { - nm_setting_wired_add_s390_option (s_wired, "portname", value); + cvalue = svGetValue (ifcfg, "PORTNAME", &value); + if (cvalue) { + if (cvalue[0] != '\0') + nm_setting_wired_add_s390_option (s_wired, "portname", cvalue); + found = TRUE; nm_clear_g_free (&value); } - value = svGetValueStr_cp (ifcfg, "CTCPROT"); - if (value) { - nm_setting_wired_add_s390_option (s_wired, "ctcprot", value); + cvalue = svGetValue (ifcfg, "CTCPROT", &value); + if (cvalue) { + if (cvalue[0] != '\0') + nm_setting_wired_add_s390_option (s_wired, "ctcprot", cvalue); nm_clear_g_free (&value); + found = TRUE; } - nettype = svGetValueStr_cp (ifcfg, "NETTYPE"); - if (nettype) { - if (!strcmp (nettype, "qeth") || !strcmp (nettype, "lcs") || !strcmp (nettype, "ctc")) - g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, nettype, NULL); + cvalue = svGetValue (ifcfg, "NETTYPE", &value); + if (cvalue) { + if (NM_IN_STRSET (cvalue, "qeth", "lcs", "ctc")) + g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, cvalue, NULL); else - PARSE_WARNING ("unknown s390 NETTYPE '%s'", nettype); - g_free (nettype); + PARSE_WARNING ("unknown s390 NETTYPE '%s'", cvalue); + nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "OPTIONS"); - if (value) { + cvalue = svGetValue (ifcfg, "OPTIONS", &value); + if (cvalue) + found = TRUE; + if (cvalue && cvalue[0]) { gs_free const char **options = NULL; gsize i; - options = nm_utils_strsplit_set_with_empty (value, " "); + options = nm_utils_escaped_tokens_split (cvalue, NM_ASCII_SPACES); for (i = 0; options && options[i]; i++) { const char *line = options[i]; const char *equals; @@ -4526,20 +4595,33 @@ make_wired_setting (shvarFile *ifcfg, if (!valid) PARSE_WARNING ("invalid s390 OPTION '%s'", line); } - nm_clear_g_free (&value); + found = TRUE; } - - g_object_set (s_wired, - NM_SETTING_WIRED_CLONED_MAC_ADDRESS, - svGetValueStr (ifcfg, "MACADDR", &value), - NULL); nm_clear_g_free (&value); - g_object_set (s_wired, - NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, - svGetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK", &value), - NULL); - nm_clear_g_free (&value); + cvalue = svGetValueStr (ifcfg, "MACADDR", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + g_object_set (s_wired, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + cvalue, + NULL); + } + nm_clear_g_free (&value); + found = TRUE; + } + + cvalue = svGetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + g_object_set (s_wired, + NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, + cvalue, + NULL); + } + nm_clear_g_free (&value); + found = TRUE; + } cvalue = svGetValueStr (ifcfg, "HWADDR_BLACKLIST", &value); if (cvalue) { @@ -4548,20 +4630,31 @@ make_wired_setting (shvarFile *ifcfg, strv = transform_hwaddr_blacklist (cvalue); g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, strv, NULL); nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "KEY_MGMT"); - if (value) { - if (!strcmp (value, "IEEE8021X")) { - *s_8021x = fill_8021x (ifcfg, file, value, FALSE, error); + cvalue = svGetValue (ifcfg, "KEY_MGMT", &value); + if (cvalue) + found = TRUE; + if (cvalue && cvalue[0] != '\0') { + if (!strcmp (cvalue, "IEEE8021X")) { + *s_8021x = fill_8021x (ifcfg, file, cvalue, FALSE, error); if (!*s_8021x) return NULL; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown wired KEY_MGMT type '%s'", value); + "Unknown wired KEY_MGMT type '%s'", cvalue); return NULL; } - nm_clear_g_free (&value); + } + nm_clear_g_free (&value); + + if (!found) { + g_set_error (error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_SETTING_MISSING, + "The setting is missing."); + return NULL; } return (NMSetting *) g_steal_pointer (&s_wired); @@ -4576,6 +4669,7 @@ wired_connection_from_ifcfg (const char *file, NMSetting *con_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -4591,12 +4685,16 @@ wired_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, con_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -4699,8 +4797,8 @@ make_infiniband_setting (shvarFile *ifcfg, g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL); if (svGetValueBoolean (ifcfg, "PKEY", FALSE)) { + gs_free char *parent = NULL; int p_key; - char *parent; if (!parse_infiniband_p_key (ifcfg, &p_key, &parent, error)) { g_object_unref (s_infiniband); @@ -4827,6 +4925,7 @@ bond_connection_from_ifcfg (const char *file, NMSetting *bond_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -4849,12 +4948,16 @@ bond_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, bond_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -4862,62 +4965,27 @@ bond_connection_from_ifcfg (const char *file, return connection; } -/* Check 'error' for errors. Missing config (NULL return value) is a valid case. */ -static char * -read_team_config (shvarFile *ifcfg, const char *key, GError **error) -{ - gs_free_error GError *local_error = NULL; - gs_free char *value = NULL; - size_t l; - - value = svGetValueStr_cp (ifcfg, key); - if (!value) - return NULL; - - l = strlen (value); - if (l > 1*1024*1024) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "%s too long (size %zd)", key, l); - return NULL; - } - - if (!nm_utils_is_json_object (value, &local_error)) { - PARSE_WARNING ("ignoring invalid team configuration: %s", local_error->message); - return NULL; - } - - return g_steal_pointer (&value); -} - static NMSetting * make_team_setting (shvarFile *ifcfg, const char *file, GError **error) { - NMSettingTeam *s_team; - char *value; - GError *local_err = NULL; + NMSetting *s_team; + gs_free char *value_device = NULL; + gs_free char *value = NULL; - value = svGetValueStr_cp (ifcfg, "DEVICE"); - if (!value) { + if (!svGetValueStr (ifcfg, "DEVICE", &value_device)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "mandatory DEVICE keyword missing"); return NULL; } - g_free (value); - - value = read_team_config (ifcfg, "TEAM_CONFIG", &local_err); - if (local_err) { - g_propagate_error (error, local_err); - return NULL; - } - s_team = NM_SETTING_TEAM (nm_setting_team_new ()); - - g_object_set (s_team, NM_SETTING_TEAM_CONFIG, value, NULL); - g_free (value); - - return (NMSetting *) s_team; + s_team = nm_setting_team_new (); + g_object_set (s_team, + NM_SETTING_TEAM_CONFIG, + svGetValue (ifcfg, "TEAM_CONFIG", &value), + NULL); + return s_team; } static NMConnection * @@ -4930,6 +4998,7 @@ team_connection_from_ifcfg (const char *file, NMSetting *team_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -4952,12 +5021,16 @@ team_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, team_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -5207,6 +5280,7 @@ bridge_connection_from_ifcfg (const char *file, NMSetting *bridge_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -5229,12 +5303,16 @@ bridge_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, bridge_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -5276,20 +5354,18 @@ make_bridge_port_setting (shvarFile *ifcfg) static NMSetting * make_team_port_setting (shvarFile *ifcfg) { - NMSetting *s_port = NULL; - char *value; - GError *error = NULL; + NMSetting *s_port; + gs_free char *value = NULL; - value = read_team_config (ifcfg, "TEAM_PORT_CONFIG", &error); - if (value) { - s_port = nm_setting_team_port_new (); - g_object_set (s_port, NM_SETTING_TEAM_PORT_CONFIG, value, NULL); - g_free (value); - } else if (error) { - PARSE_WARNING ("%s", error->message); - g_error_free (error); - } + value = svGetValueStr_cp (ifcfg, "TEAM_PORT_CONFIG"); + if (!value) + return NULL; + s_port = nm_setting_team_port_new (); + g_object_set (s_port, + NM_SETTING_TEAM_PORT_CONFIG, + value, + NULL); return s_port; } @@ -5486,6 +5562,7 @@ vlan_connection_from_ifcfg (const char *file, NMSetting *wired_setting = NULL; NMSetting *vlan_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -5508,12 +5585,16 @@ vlan_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, vlan_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -5549,21 +5630,21 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg, if (v) { gs_free char *lower = g_ascii_strdown (v, -1); - *out_spec = g_strdup_printf ("%s:mac:%s", type, lower); + *out_spec = g_strdup_printf ("%s:"NM_MATCH_SPEC_MAC_TAG"%s", type, lower); return connection; } nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "SUBCHANNELS", &value); if (v) { - *out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, v); + *out_spec = g_strdup_printf ("%s:"NM_MATCH_SPEC_S390_SUBCHANNELS_TAG"%s", type, v); return connection; } nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "DEVICE", &value); if (v) { - *out_spec = g_strdup_printf ("%s:interface-name:%s", type, v); + *out_spec = g_strdup_printf ("%s:"NM_MATCH_SPEC_INTERFACE_NAME_TAG"=%s", type, v); return connection; } @@ -5654,7 +5735,7 @@ connection_from_file_full (const char *filename, return g_steal_pointer (&connection); } - /* iBFT is handled by the iBFT settings plugin */ + /* iBFT is handled by nm-initrd-generator during boot. */ bootproto = svGetValueStr_cp (main_ifcfg, "BOOTPROTO"); if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) { NM_SET_OUT (out_ignore_error, TRUE); @@ -5902,12 +5983,10 @@ connection_from_file_full (const char *filename, if (s_match) nm_connection_add_setting (connection, s_match); - /* Bridge port? */ s_port = make_bridge_port_setting (main_ifcfg); if (s_port) nm_connection_add_setting (connection, s_port); - /* Team port? */ s_port = make_team_port_setting (main_ifcfg); if (s_port) nm_connection_add_setting (connection, s_port); @@ -5949,20 +6028,3 @@ nmtst_connection_from_file (const char *filename, error, NULL); } - -guint -devtimeout_from_file (const char *filename) -{ - shvarFile *ifcfg; - guint devtimeout; - - g_return_val_if_fail (filename != NULL, 0); - - ifcfg = svOpenFile (filename, NULL); - if (!ifcfg) - return 0; - - devtimeout = svGetValueInt64 (ifcfg, "DEVTIMEOUT", 10, 0, G_MAXUINT, 0); - svCloseFile (ifcfg); - return devtimeout; -} diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h index a8937ac8..8008e052 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -18,8 +17,8 @@ * Copyright (C) 2008 Red Hat, Inc. */ -#ifndef __READER_H__ -#define __READER_H__ +#ifndef __NMS_IFCFG_RH_READER_H__ +#define __NMS_IFCFG_RH_READER_H__ #include "nm-connection.h" @@ -28,12 +27,10 @@ NMConnection *connection_from_file (const char *filename, GError **error, gboolean *out_ignore_error); -guint devtimeout_from_file (const char *filename); - NMConnection *nmtst_connection_from_file (const char *filename, const char *network_file, const char *test_type, char **out_unhandled, GError **error); -#endif /* __READER_H__ */ +#endif /* __NMS_IFCFG_RH_READER_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c new file mode 100644 index 00000000..2841bedb --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c @@ -0,0 +1,198 @@ +/* NetworkManager + * + * 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 (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nms-ifcfg-rh-storage.h" + +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "nm-connection.h" +#include "nms-ifcfg-rh-plugin.h" + +/*****************************************************************************/ + +struct _NMSIfcfgRHStorageClass { + NMSettingsStorageClass parent; +}; + +G_DEFINE_TYPE (NMSIfcfgRHStorage, nms_ifcfg_rh_storage, NM_TYPE_SETTINGS_STORAGE) + +/*****************************************************************************/ + +gboolean +nms_ifcfg_rh_storage_equal_type (const NMSIfcfgRHStorage *self_a, + const NMSIfcfgRHStorage *self_b) +{ + return (self_a == self_b) + || ( self_a + && self_b + && nm_streq0 (nms_ifcfg_rh_storage_get_uuid_opt (self_a), + nms_ifcfg_rh_storage_get_uuid_opt (self_b)) + && nm_streq0 (self_a->unmanaged_spec, + self_b->unmanaged_spec) + && nm_streq0 (self_a->unrecognized_spec, + self_b->unrecognized_spec)); +} + +void +nms_ifcfg_rh_storage_copy_content (NMSIfcfgRHStorage *dst, + const NMSIfcfgRHStorage *src) +{ + nm_assert (src != dst); + nm_assert (src && dst); + nm_assert (nms_ifcfg_rh_storage_equal_type (dst, src)); + nm_assert ( nms_ifcfg_rh_storage_get_filename (dst) + && nm_streq (nms_ifcfg_rh_storage_get_filename (dst), + nms_ifcfg_rh_storage_get_filename (src))); + + nm_g_object_ref_set (&dst->connection, src->connection); + g_free (dst->unmanaged_spec); + g_free (dst->unrecognized_spec); + dst->unmanaged_spec = g_strdup (src->unmanaged_spec); + dst->unrecognized_spec = g_strdup (src->unrecognized_spec); + dst->stat_mtime = src->stat_mtime; +} + +NMConnection * +nms_ifcfg_rh_storage_steal_connection (NMSIfcfgRHStorage *self) +{ + nm_assert (NMS_IS_IFCFG_RH_STORAGE (self)); + + return g_steal_pointer (&self->connection); +} + +/*****************************************************************************/ + +static int +cmp_fcn (const NMSIfcfgRHStorage *a, + const NMSIfcfgRHStorage *b) +{ + nm_assert (NMS_IS_IFCFG_RH_STORAGE (a)); + nm_assert (NMS_IS_IFCFG_RH_STORAGE (b)); + nm_assert (a != b); + + /* newer files are more important. */ + NM_CMP_FIELD (a, b, stat_mtime.tv_sec); + NM_CMP_FIELD (a, b, stat_mtime.tv_nsec); + + NM_CMP_DIRECT_STRCMP (nms_ifcfg_rh_storage_get_filename (a), nms_ifcfg_rh_storage_get_filename (b)); + + return 0; +} + +/*****************************************************************************/ + +static void +nms_ifcfg_rh_storage_init (NMSIfcfgRHStorage *self) +{ +} + +static NMSIfcfgRHStorage * +_storage_new (NMSIfcfgRHPlugin *plugin, + const char *uuid, + const char *filename) +{ + nm_assert (NMS_IS_IFCFG_RH_PLUGIN (plugin)); + nm_assert (!uuid || nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + + return g_object_new (NMS_TYPE_IFCFG_RH_STORAGE, + NM_SETTINGS_STORAGE_PLUGIN, plugin, + NM_SETTINGS_STORAGE_UUID, uuid, + NM_SETTINGS_STORAGE_FILENAME, filename, + NULL); +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_connection (NMSIfcfgRHPlugin *plugin, + const char *filename, + NMConnection *connection_take, + const struct timespec *mtime) +{ + NMSIfcfgRHStorage *self; + + nm_assert (NM_IS_CONNECTION (connection_take)); + nm_assert (_nm_connection_verify (connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS); + nmtst_connection_assert_unchanging (connection_take); + + self = _storage_new (plugin, + nm_connection_get_uuid (connection_take), + filename); + self->connection = connection_take; + if (mtime) + self->stat_mtime = *mtime; + return self; +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_unhandled (NMSIfcfgRHPlugin *plugin, + const char *filename, + const char *unmanaged_spec, + const char *unrecognized_spec) +{ + NMSIfcfgRHStorage *self; + + nm_assert (unmanaged_spec || unrecognized_spec); + + self = _storage_new (plugin, + NULL, + filename); + self->unmanaged_spec = g_strdup (unmanaged_spec); + self->unrecognized_spec = g_strdup (unrecognized_spec); + return self; +} + +static void +_storage_clear (NMSIfcfgRHStorage *self) +{ + c_list_unlink (&self->parent._storage_lst); + c_list_unlink (&self->parent._storage_by_uuid_lst); + nm_clear_g_free (&self->unmanaged_spec); + nm_clear_g_free (&self->unrecognized_spec); + g_clear_object (&self->connection); +} + +static void +dispose (GObject *object) +{ + NMSIfcfgRHStorage *self = NMS_IFCFG_RH_STORAGE (object); + + _storage_clear (self); + + G_OBJECT_CLASS (nms_ifcfg_rh_storage_parent_class)->dispose (object); +} + +void +nms_ifcfg_rh_storage_destroy (NMSIfcfgRHStorage *self) +{ + _storage_clear (self); + g_object_unref (self); +} + +static void +nms_ifcfg_rh_storage_class_init (NMSIfcfgRHStorageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingsStorageClass *storage_class = NM_SETTINGS_STORAGE_CLASS (klass); + + object_class->dispose = dispose; + + storage_class->cmp_fcn = (int (*) (NMSettingsStorage *, NMSettingsStorage *)) cmp_fcn; +} diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h new file mode 100644 index 00000000..e1165f50 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h @@ -0,0 +1,93 @@ +/* NetworkManager + * + * 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 (C) 2019 Red Hat, Inc. + */ + +#ifndef __NMS_IFCFG_RH_STORAGE_H__ +#define __NMS_IFCFG_RH_STORAGE_H__ + +#include "c-list/src/c-list.h" +#include "settings/nm-settings-storage.h" + +/*****************************************************************************/ + +#define NMS_TYPE_IFCFG_RH_STORAGE (nms_ifcfg_rh_storage_get_type ()) +#define NMS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorage)) +#define NMS_IFCFG_RH_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) +#define NMS_IS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IS_IFCFG_RH_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IFCFG_RH_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) + +typedef struct { + NMSettingsStorage parent; + + NMConnection *connection; + + char *unmanaged_spec; + char *unrecognized_spec; + + /* The timestamp (stat's mtime) of the file. Newer files have + * higher priority. */ + struct timespec stat_mtime; + + bool dirty:1; + +} NMSIfcfgRHStorage; + +typedef struct _NMSIfcfgRHStorageClass NMSIfcfgRHStorageClass; + +GType nms_ifcfg_rh_storage_get_type (void); + +struct _NMSIfcfgRHPlugin; + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_connection (struct _NMSIfcfgRHPlugin *plugin, + const char *filename, + NMConnection *connection_take, + const struct timespec *mtime); + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_unhandled (struct _NMSIfcfgRHPlugin *plugin, + const char *filename, + const char *unmanaged_spec, + const char *unrecognized_spec); + +void nms_ifcfg_rh_storage_destroy (NMSIfcfgRHStorage *self); + +/*****************************************************************************/ + +gboolean nms_ifcfg_rh_storage_equal_type (const NMSIfcfgRHStorage *self_a, + const NMSIfcfgRHStorage *self_b); + +void nms_ifcfg_rh_storage_copy_content (NMSIfcfgRHStorage *dst, + const NMSIfcfgRHStorage *src); + +NMConnection *nms_ifcfg_rh_storage_steal_connection (NMSIfcfgRHStorage *self); + +/*****************************************************************************/ + +static inline const char * +nms_ifcfg_rh_storage_get_uuid_opt (const NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_uuid_opt ((const NMSettingsStorage *) self); +} + +static inline const char * +nms_ifcfg_rh_storage_get_filename (const NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_filename ((const NMSettingsStorage *) self); +} + +#endif /* __NMS_IFCFG_RH_STORAGE_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index 22c9061b..cb1fc23a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -29,6 +28,32 @@ #include "nms-ifcfg-rh-common.h" +/*****************************************************************************/ + +gboolean +nms_ifcfg_rh_util_parse_unhandled_spec (const char *unhandled_spec, + const char **out_unmanaged_spec, + const char **out_unrecognized_spec) +{ + if (unhandled_spec) { + if (NM_STR_HAS_PREFIX (unhandled_spec, "unmanaged:")) { + NM_SET_OUT (out_unmanaged_spec, &unhandled_spec[NM_STRLEN ("unmanaged:")]); + NM_SET_OUT (out_unrecognized_spec, NULL); + return TRUE; + } + if (NM_STR_HAS_PREFIX (unhandled_spec, "unrecognized:")) { + NM_SET_OUT (out_unmanaged_spec, NULL); + NM_SET_OUT (out_unrecognized_spec, &unhandled_spec[NM_STRLEN ("unrecognized:")]); + return TRUE; + } + } + NM_SET_OUT (out_unmanaged_spec, NULL); + NM_SET_OUT (out_unrecognized_spec, NULL); + return FALSE; +} + +/*****************************************************************************/ + /* * Check ';[a-fA-F0-9]{8}' file suffix used for temporary files by rpm when * installing packages. @@ -317,36 +342,41 @@ utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg) char * utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg) { - gs_free char *base = NULL; - char *ptr, *ifcfg = NULL; + const char *base; g_return_val_if_fail (path != NULL, NULL); if (utils_should_ignore_file (path, only_ifcfg)) return NULL; - base = g_path_get_basename (path); + base = strrchr (path, '/'); + if (!base) + base = path; + else + base += 1; - if (strncmp (base, IFCFG_TAG, NM_STRLEN (IFCFG_TAG)) == 0) { + if (NM_STR_HAS_PREFIX (base, IFCFG_TAG)) { if (base[NM_STRLEN (IFCFG_TAG)] == '\0') return NULL; if (utils_is_ifcfg_alias_file (base, NULL)) { + gs_free char *ifcfg = NULL; + char *ptr; + ifcfg = g_strdup (path); ptr = strrchr (ifcfg, ':'); - if (ptr && ptr > ifcfg) { + if ( ptr + && ptr > ifcfg + && !strchr (ptr, '/')) { *ptr = '\0'; if (g_file_test (ifcfg, G_FILE_TEST_EXISTS)) { /* the file has a colon, so it is probably an alias. * To be ~more~ certain that this is an alias file, * check whether a corresponding base file exists. */ - if (only_ifcfg) { - g_free (ifcfg); + if (only_ifcfg) return NULL; - } - return ifcfg; + return g_steal_pointer (&ifcfg); } } - g_free (ifcfg); } return g_strdup (path); } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index c7729df5..20d6f72d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -26,6 +25,10 @@ #include "shvar.h" +gboolean nms_ifcfg_rh_util_parse_unhandled_spec (const char *unhandled_spec, + const char **out_unmanaged_spec, + const char **out_unrecognized_spec); + #define NM_IFCFG_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory") #define NM_IFCFG_CONNECTION_LOG_FMT "%s (%s,\"%s\")" #define NM_IFCFG_CONNECTION_LOG_ARG(con) NM_IFCFG_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 80b1bffe..38dc5c8d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -177,30 +176,18 @@ typedef struct { } Setting8021xSchemeVtable; static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = { - [NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], - .ifcfg_rh_key = "IEEE_8021X_CA_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT], - .ifcfg_rh_key = "IEEE_8021X_INNER_CA_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], - .ifcfg_rh_key = "IEEE_8021X_CLIENT_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT], - .ifcfg_rh_key = "IEEE_8021X_INNER_CLIENT_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY], - .ifcfg_rh_key = "IEEE_8021X_PRIVATE_KEY", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY], - .ifcfg_rh_key = "IEEE_8021X_INNER_PRIVATE_KEY", - }, +#define _D(_scheme_type, _ifcfg_rh_key) \ + [(_scheme_type)] = { \ + .vtable = &nm_setting_8021x_scheme_vtable[(_scheme_type)], \ + .ifcfg_rh_key = ""_ifcfg_rh_key"", \ + } + _D (NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, "IEEE_8021X_CA_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, "IEEE_8021X_INNER_CA_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, "IEEE_8021X_CLIENT_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, "IEEE_8021X_INNER_CLIENT_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, "IEEE_8021X_PRIVATE_KEY"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, "IEEE_8021X_INNER_PRIVATE_KEY"), +#undef _D }; static gboolean @@ -209,6 +196,7 @@ write_object (NMSetting8021x *s_8021x, GHashTable *secrets, GHashTable *blobs, const Setting8021xSchemeVtable *objtype, + gboolean force_write, GError **error) { NMSetting8021xCKScheme scheme; @@ -216,7 +204,8 @@ write_object (NMSetting8021x *s_8021x, GBytes *blob = NULL; const char *password = NULL; NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; - char *secret_name, *secret_flags; + char secret_name[100]; + char secret_flags[sizeof (secret_name) + NM_STRLEN ("_FLAGS")]; const char *extension; char *standard_file; @@ -243,13 +232,11 @@ write_object (NMSetting8021x *s_8021x, } /* Set the password for certificate/private key. */ - secret_name = g_strdup_printf ("%s_PASSWORD", objtype->ifcfg_rh_key); - secret_flags = g_strdup_printf ("%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key); + nm_sprintf_buf (secret_name, "%s_PASSWORD", objtype->ifcfg_rh_key); + nm_sprintf_buf (secret_flags, "%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key); password = (*(objtype->vtable->passwd_func))(s_8021x); flags = (*(objtype->vtable->pwflag_func))(s_8021x); set_secret (ifcfg, secrets, secret_name, password, secret_flags, flags); - g_free (secret_name); - g_free (secret_flags); if (!objtype->vtable->format_func) extension = "der"; @@ -287,7 +274,7 @@ write_object (NMSetting8021x *s_8021x, */ standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension); g_hash_table_replace (blobs, standard_file, NULL); - svUnsetValue (ifcfg, objtype->ifcfg_rh_key); + svSetValue (ifcfg, objtype->ifcfg_rh_key, force_write ? "" : NULL); return TRUE; } @@ -338,43 +325,41 @@ write_8021x_certs (NMSetting8021x *s_8021x, shvarFile *ifcfg, GError **error) { - const Setting8021xSchemeVtable *otype = NULL; + const Setting8021xSchemeVtable *pk_otype = NULL; + gs_free char *value_to_free = NULL; /* CA certificate */ if (!write_object (s_8021x, ifcfg, secrets, blobs, phase2 ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], + FALSE, error)) return FALSE; /* Private key */ if (phase2) - otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]; + pk_otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]; else - otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; + pk_otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; /* Save the private key */ - if (!write_object (s_8021x, ifcfg, secrets, blobs, otype, error)) + if (!write_object (s_8021x, ifcfg, secrets, blobs, pk_otype, FALSE, error)) return FALSE; - /* Client certificate */ - if (otype->vtable->format_func (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { - /* Don't need a client certificate with PKCS#12 since the file is both - * the client certificate and the private key in one file. - */ - svSetValueStr (ifcfg, - phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", - NULL); - } else { - /* Save the client certificate */ - if (!write_object (s_8021x, ifcfg, secrets, blobs, - phase2 - ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] - : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], - error)) - return FALSE; - } + /* Save the client certificate. + * If there is a private key, always write a property for the + * client certificate even if it is empty, so that the reader + * doesn't have to read the private key file to determine if it + * is a PKCS #12 one which serves also as client certificate. + */ + if (!write_object (s_8021x, ifcfg, secrets, blobs, + phase2 + ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], + !!svGetValue (ifcfg, pk_otype->ifcfg_rh_key, &value_to_free), + error)) + return FALSE; return TRUE; } @@ -1118,14 +1103,22 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) nm_setting_wired_get_s390_option (s_wired, i, &s390_key, &s390_val); /* portname is handled separately */ - if (!strcmp (s390_key, "portname") || !strcmp (s390_key, "ctcprot")) + if (NM_IN_STRSET (s390_key, "portname", "ctcprot")) continue; + if (strchr (s390_key, '=')) { + /* this key cannot be expressed. But after all, it's not valid anyway + * and the connection shouldn't even verify. */ + continue; + } + if (!tmp) tmp = g_string_sized_new (30); else g_string_append_c (tmp, ' '); - g_string_append_printf (tmp, "%s=%s", s390_key, s390_val); + nm_utils_escaped_tokens_escape_gstr (s390_key, NM_ASCII_SPACES, tmp); + g_string_append_c (tmp, '='); + nm_utils_escaped_tokens_escape_gstr (s390_val, NM_ASCII_SPACES, tmp); } if (tmp) svSetValueStr (ifcfg, "OPTIONS", tmp->str); @@ -1302,7 +1295,7 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) has_wired = TRUE; device_mac = nm_setting_wired_get_mac_address (s_wired); - svSetValueStr (ifcfg, "HWADDR", device_mac); + svSetValue (ifcfg, "HWADDR", device_mac ?: ""); cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); svSetValueStr (ifcfg, "MACADDR", cloned_mac); @@ -1856,6 +1849,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) GString *str; const char *master, *master_iface = NULL, *type; int vint; + gint32 vint32; NMSettingConnectionMdns mdns; NMSettingConnectionLlmnr llmnr; guint32 vuint32; @@ -2022,6 +2016,19 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) vint = nm_setting_connection_get_auth_retries (s_con); svSetValueInt64_cond (ifcfg, "AUTH_RETRIES", vint >= 0, vint); + vint32 = nm_setting_connection_get_wait_device_timeout (s_con); + if (vint32 == -1) + svUnsetValue (ifcfg, "DEVTIMEOUT"); + else if ((vint32 % 1000) == 0) + svSetValueInt64 (ifcfg, "DEVTIMEOUT", vint32 / 1000); + else { + char b[100]; + + svSetValueStr (ifcfg, + "DEVTIMEOUT", + nm_sprintf_buf (b, "%.3f", ((double) vint) / 1000.0)); + } + mdns = nm_setting_connection_get_mdns (s_con); if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) { svSetValueEnum (ifcfg, "MDNS", nm_setting_connection_mdns_get_type (), @@ -2797,10 +2804,17 @@ write_ip6_setting (NMConnection *connection, value = nm_setting_ip_config_get_method (s_ip6); g_assert (value); + svUnsetValue (ifcfg, "IPV6_DISABLED"); if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { svSetValueStr (ifcfg, "IPV6INIT", "no"); svUnsetValue (ifcfg, "DHCPV6C"); return TRUE; + } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { + svSetValueStr (ifcfg, "IPV6_DISABLED", "yes"); + svSetValueStr (ifcfg, "IPV6INIT", "no"); + svUnsetValue (ifcfg, "DHCPV6C"); + svUnsetValue (ifcfg, "IPV6_AUTOCONF"); + return TRUE; } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { svSetValueStr (ifcfg, "IPV6INIT", "yes"); svSetValueStr (ifcfg, "IPV6_AUTOCONF", "yes"); @@ -3024,6 +3038,8 @@ static gboolean do_write_construct (NMConnection *connection, const char *ifcfg_dir, const char *filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, shvarFile **out_ifcfg, GHashTable **out_blobs, GHashTable **out_secrets, @@ -3067,30 +3083,31 @@ do_write_construct (NMConnection *connection, ifcfg_name = g_strdup (filename); } else if (ifcfg_dir) { - char *escaped; + gs_free char *escaped = NULL; + int i_path; escaped = escape_id (nm_setting_connection_get_id (s_con)); - ifcfg_name = g_strdup_printf ("%s/ifcfg-%s", ifcfg_dir, escaped); - /* If a file with this path already exists then we need another name. - * Multiple connections can have the same ID (ie if two connections with - * the same ID are visible to different users) but of course can't have - * the same path. - */ - if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS)) { - guint32 idx = 0; - - nm_clear_g_free (&ifcfg_name); - while (idx++ < 500) { - ifcfg_name = g_strdup_printf ("%s/ifcfg-%s-%u", ifcfg_dir, escaped, idx); - if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS) == FALSE) - break; - nm_clear_g_free (&ifcfg_name); - } + for (i_path = 0; i_path < 10000; i_path++) { + gs_free char *path_candidate = NULL; + + if (i_path == 0) + path_candidate = g_strdup_printf ("%s/ifcfg-%s", ifcfg_dir, escaped); + else + path_candidate = g_strdup_printf ("%s/ifcfg-%s-%d", ifcfg_dir, escaped, i_path); + + if ( allow_filename_cb + && !allow_filename_cb (path_candidate, allow_filename_user_data)) + continue; + + if (g_file_test (path_candidate, G_FILE_TEST_EXISTS)) + continue; + + ifcfg_name = g_steal_pointer (&path_candidate); + break; } - g_free (escaped); - if (ifcfg_name == NULL) { + if (!ifcfg_name) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Failed to find usable ifcfg file name"); return FALSE; @@ -3329,54 +3346,12 @@ do_write_to_disk (NMConnection *connection, return TRUE; } -static gboolean -do_write_reread (NMConnection *connection, - const char *ifcfg_name, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error) -{ - gs_unref_object NMConnection *reread = NULL; - gs_free_error GError *local = NULL; - gs_free char *unhandled = NULL; - gboolean reread_same = FALSE; - - nm_assert (!out_reread || !*out_reread); - - reread = connection_from_file (ifcfg_name, &unhandled, &local, NULL); - - if (!reread) { - g_propagate_error (error, local); - local = NULL; - return FALSE; - } - if (unhandled) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "connection is unhandled"); - return FALSE; - } - if (out_reread_same) { - if (nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT)) - reread_same = TRUE; - - nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); - nm_assert (reread_same == ({ - gs_unref_hashtable GHashTable *_settings = NULL; - - ( nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings) - && !_settings); - })); - } - - NM_SET_OUT (out_reread, g_steal_pointer (&reread)); - NM_SET_OUT (out_reread_same, reread_same); - return TRUE; -} - gboolean nms_ifcfg_rh_writer_write_connection (NMConnection *connection, const char *ifcfg_dir, const char *filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_filename, NMConnection **out_reread, gboolean *out_reread_same, @@ -3389,13 +3364,14 @@ nms_ifcfg_rh_writer_write_connection (NMConnection *connection, nm_auto_free_gstring GString *route6_content = NULL; gs_unref_hashtable GHashTable *secrets = NULL; gs_unref_hashtable GHashTable *blobs = NULL; - GError *local = NULL; nm_assert (!out_reread || !*out_reread); if (!do_write_construct (connection, ifcfg_dir, filename, + allow_filename_cb, + allow_filename_user_data, &ifcfg, &blobs, &secrets, @@ -3424,28 +3400,46 @@ nms_ifcfg_rh_writer_write_connection (NMConnection *connection, /* Note that we just wrote the connection to disk, and re-read it from there. * That is racy if somebody else modifies the connection. + * That race is why we must not tread a failure to re-read the profile + * as an error. * - * A better solution might be, to re-read the connection only based on the - * in-memory representation of what we collected above. But the reader + * FIXME: a much better solution might be, to re-read the connection only based + * on the in-memory representation of what we collected above. But the reader * does not yet allow to inject the configuration. */ - if (out_reread || out_reread_same) { - if (!do_write_reread (connection, - svFileGetName (ifcfg), - out_reread, - out_reread_same, - &local)) { + if ( out_reread + || out_reread_same) { + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = FALSE; + gs_free_error GError *local = NULL; + gs_free char *unhandled = NULL; + + reread = connection_from_file (svFileGetName (ifcfg), + &unhandled, + &local, + NULL); + nm_assert ((NM_IS_CONNECTION (reread) && !local) || (!reread && local)); + + if (!reread) { _LOGW ("write: failure to re-read connection \"%s\": %s", svFileGetName (ifcfg), local->message); - g_clear_error (&local); + } else if (unhandled) { + g_clear_object (&reread); + _LOGW ("write: failure to re-read connection \"%s\": %s", + svFileGetName (ifcfg), "connection is unhandled"); } else { - if ( out_reread_same - && !*out_reread_same) { - _LOGD ("write: connection %s (%s) was modified by persisting it to \"%s\" ", - nm_connection_get_id (connection), - nm_connection_get_uuid (connection), - svFileGetName (ifcfg)); + if (out_reread_same) { + reread_same = nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT); + if (!reread_same) { + _LOGD ("write: connection %s (%s) was modified by persisting it to \"%s\" ", + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + svFileGetName (ifcfg)); + } } } + + NM_SET_OUT (out_reread, g_steal_pointer (&reread)); + NM_SET_OUT (out_reread_same, reread_same); } /* Only return the filename if this was a newly written ifcfg */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h index d7a255a9..0902daee 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -23,12 +22,18 @@ #include "nm-connection.h" + +typedef gboolean (*NMSIfcfgRHWriterAllowFilenameCb) (const char *check_filename, + gpointer allow_filename_user_data); + gboolean nms_ifcfg_rh_writer_can_write_connection (NMConnection *connection, GError **error); gboolean nms_ifcfg_rh_writer_write_connection (NMConnection *connection, const char *ifcfg_dir, const char *filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_filename, NMConnection **out_reread, gboolean *out_reread_same, diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index b399a17f..94e31aac 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * shvar.c * diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index b38a8557..67fb5404 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * shvar.h * diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected index 5d81dfef..36df7712 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected @@ -1,6 +1,7 @@ BONDING_OPTS="downdelay=5 miimon=100 mode=balance-rr updelay=10" TYPE=Bond BONDING_MASTER=yes +HWADDR= PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=none diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected index 339f8107..9c2a1ff0 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected @@ -5,6 +5,7 @@ VLAN_ID=444 REORDER_HDR=yes GVRP=no MVRP=no +HWADDR= PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=dhcp diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected index 793713ea..44eb777c 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected @@ -8,6 +8,7 @@ VLAN_FLAGS=LOOSE_BINDING MVRP=no VLAN_INGRESS_PRIORITY_MAP=0:1,2:5 VLAN_EGRESS_PRIORITY_MAP=3:1,12:3,14:7 +HWADDR= PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=none diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected index 87980dc4..b01372af 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected @@ -4,6 +4,7 @@ VLAN_ID=142 REORDER_HDR=yes GVRP=no MVRP=no +HWADDR= NAME=team-slave-enp31s0f1-142 UUID=74f435bb-ede4-415a-9d48-f580b60eba04 DEVICE=enp31s0f1-142 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected new file mode 100644 index 00000000..cae51b89 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected @@ -0,0 +1,11 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6_DISABLED=yes +IPV6INIT=no +NAME="Test Write Wired Disabled IP6" +UUID=${UUID} +ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert new file mode 100644 index 00000000..24397473 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert @@ -0,0 +1,13 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TLS +IEEE_8021X_IDENTITY="David Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_PRIVATE_KEY=test_client.p12 +IEEE_8021X_PRIVATE_KEY_PASSWORD="test1" diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 new file mode 100644 index 00000000..edc2af75 Binary files /dev/null and b/src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 differ diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 49ab04d4..45e90b91 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -239,7 +238,7 @@ _assert_expected_content (NMConnection *connection, const char *filename, const g_assert (_ifcfg_dir && _ifcfg_dir[0]); \ g_assert (_filename && _filename[0]); \ \ - _success = nms_ifcfg_rh_writer_write_connection (_connection, _ifcfg_dir, _filename, NULL, _out_reread, _out_reread_same, &_error); \ + _success = nms_ifcfg_rh_writer_write_connection (_connection, _ifcfg_dir, _filename, NULL, NULL, NULL, _out_reread, _out_reread_same, &_error); \ nmtst_assert_success (_success, _error); \ _assert_expected_content (_connection, _filename, _expected); \ } G_STMT_END @@ -310,7 +309,7 @@ _writer_new_connection_reread (NMConnection *connection, char *filename = NULL; gs_unref_object NMConnection *con_verified = NULL; gs_unref_object NMConnection *reread_copy = NULL; - NMConnection **reread = out_reread ?: ((nmtst_get_rand_int () % 2) ? &reread_copy : NULL); + NMConnection **reread = out_reread ?: ((nmtst_get_rand_uint32 () % 2) ? &reread_copy : NULL); g_assert (NM_IS_CONNECTION (connection)); g_assert (ifcfg_dir); @@ -320,6 +319,8 @@ _writer_new_connection_reread (NMConnection *connection, success = nms_ifcfg_rh_writer_write_connection (con_verified, ifcfg_dir, NULL, + NULL, + NULL, &filename, reread, out_reread_same, @@ -395,6 +396,8 @@ _writer_new_connection_fail (NMConnection *connection, success = nms_ifcfg_rh_writer_write_connection (connection_normalized, ifcfg_dir, NULL, + NULL, + NULL, &filename, &reread, NULL, @@ -718,7 +721,7 @@ test_read_unmanaged_unrecognized (void) connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-nm-controlled-unrecognized", NULL, NULL, &unhandled_spec); - g_assert_cmpstr (unhandled_spec, ==, "unmanaged:interface-name:ipoac0"); + g_assert_cmpstr (unhandled_spec, ==, "unmanaged:interface-name:=ipoac0"); /* ===== CONNECTION SETTING ===== */ s_con = nm_connection_get_setting_connection (connection); @@ -1973,6 +1976,27 @@ test_read_802_1x_ttls_eapgtc (void) g_object_unref (connection); } +static void +test_read_802_1x_tls_p12_no_client_cert (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSetting8021x *s_8021x; + const char *path; + + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-8021x-tls-p12-no-client-cert", + NULL, TYPE_ETHERNET, NULL); + + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x); + + g_assert_cmpint (nm_setting_802_1x_get_private_key_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH); + path = nm_setting_802_1x_get_private_key_path (s_8021x); + g_assert (path); + + g_assert_cmpint (nm_setting_802_1x_get_client_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH); + g_assert_cmpstr (path, ==, nm_setting_802_1x_get_client_cert_path (s_8021x)); +} + static void test_read_write_802_1x_password_raw (void) { @@ -4768,6 +4792,48 @@ test_write_wired_static_ip6_only (void) nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); } +static void +test_write_ip6_disabled (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + + connection = nmtst_create_minimal_connection ("Test Write Wired Disabled IP6", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED, + NULL); + + nmtst_assert_connection_verifies (connection); + + _writer_new_connec_exp (connection, + TEST_SCRATCH_DIR_TMP, + TEST_IFCFG_DIR"/ifcfg-test-ip6-disabled.cexpected", + &testfile); + + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + /* Test writing an IPv6 config with varying gateway address. * For missing gateway (::), we expect no IPV6_DEFAULTGW to be written * to ifcfg-rh. @@ -8938,25 +9004,16 @@ static void test_read_team_master_invalid (gconstpointer user_data) { const char *const PATH_NAME = user_data; - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingTeam *s_team; - - NMTST_EXPECT_NM_WARN ("*ignoring invalid team configuration*"); - connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL); - g_test_assert_expected_messages (); - - g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "team0"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_TEAM_SETTING_NAME); + gs_free_error GError *error = NULL; + gs_unref_object NMConnection *connection = NULL; - s_team = nm_connection_get_setting_team (connection); - g_assert (s_team); - g_assert (nm_setting_team_get_config (s_team) == NULL); + if (WITH_JSON_VALIDATION) { + _connection_from_file_fail (PATH_NAME, NULL, TYPE_ETHERNET, &error); - g_object_unref (connection); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + g_assert (strstr (error->message, _("invalid json"))); + } else + connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL); } static void @@ -9237,19 +9294,19 @@ test_team_reread_slave (void) nmtst_assert_connection_equals (connection_1, FALSE, connection_2, FALSE); - _writer_new_connection_reread ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, + _writer_new_connection_reread ((nmtst_get_rand_uint32 () % 2) ? connection_1 : connection_2, TEST_SCRATCH_DIR, &testfile, TEST_IFCFG_DIR"/ifcfg-team-slave-enp31s0f1-142.cexpected", &reread, &reread_same); - _assert_reread_same ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, reread); + _assert_reread_same ((nmtst_get_rand_uint32 () % 2) ? connection_1 : connection_2, reread); g_assert (reread_same); g_clear_object (&reread); reread = _connection_from_file (testfile, NULL, TYPE_VLAN, NULL); - nmtst_assert_connection_equals ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, FALSE, + nmtst_assert_connection_equals ((nmtst_get_rand_uint32 () % 2) ? connection_1 : connection_2, FALSE, reread, FALSE); } @@ -9414,7 +9471,7 @@ do_svUnescape_combine_ansi (GString *str_val, GString *str_exp, const UnescapeTe g_string_append (str_val, "$'"); if (idx < 0) { for (i = -idx; i > 0; i--) { - j = nmtst_get_rand_int () % data_len; + j = nmtst_get_rand_uint32 () % data_len; if (!data_ansi[j].can_concat) { i++; continue; @@ -9613,7 +9670,7 @@ test_svUnescape (void) /* different values can be just concatenated... */ for (i = 0; i < 200; i++) { - gsize num_concat = (nmtst_get_rand_int () % 5) + 2; + gsize num_concat = (nmtst_get_rand_uint32 () % 5) + 2; g_string_set_size (str_val, 0); g_string_set_size (str_exp, 0); @@ -9621,12 +9678,12 @@ test_svUnescape (void) while (num_concat > 0) { gsize idx; - if ((nmtst_get_rand_int () % 3 == 0)) { - do_svUnescape_combine_ansi (str_val2, str_exp2, data_ansi, G_N_ELEMENTS (data_ansi), -((int) ((nmtst_get_rand_int () % 5) + 1))); + if ((nmtst_get_rand_uint32 () % 3 == 0)) { + do_svUnescape_combine_ansi (str_val2, str_exp2, data_ansi, G_N_ELEMENTS (data_ansi), -((int) ((nmtst_get_rand_uint32 () % 5) + 1))); continue; } - idx = nmtst_get_rand_int () % G_N_ELEMENTS (data_full); + idx = nmtst_get_rand_uint32 () % G_N_ELEMENTS (data_full); if (!data_full[idx].can_concat) continue; g_string_append (str_val, data_full[idx].val); @@ -9634,7 +9691,7 @@ test_svUnescape (void) num_concat--; } - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: g_string_append (str_val, " "); break; @@ -9642,7 +9699,7 @@ test_svUnescape (void) g_string_append (str_val, " "); break; } - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: g_string_append (str_val, " #"); break; @@ -10177,6 +10234,7 @@ int main (int argc, char **argv) g_test_add_data_func (TPATH "static-ip6-only-gw/::", "::", test_write_wired_static_ip6_only_gw); g_test_add_data_func (TPATH "static-ip6-only-gw/2001:db8:8:4::2", "2001:db8:8:4::2", test_write_wired_static_ip6_only_gw); g_test_add_data_func (TPATH "static-ip6-only-gw/::ffff:255.255.255.255", "::ffff:255.255.255.255", test_write_wired_static_ip6_only_gw); + g_test_add_func (TPATH "ip6/disabled", test_write_ip6_disabled); g_test_add_func (TPATH "read-dns-options", test_read_dns_options); g_test_add_func (TPATH "clear-master", test_clear_master); @@ -10223,6 +10281,8 @@ int main (int argc, char **argv) g_test_add_func (TPATH "802-1x/subj-matches", test_read_write_802_1X_subj_matches); g_test_add_func (TPATH "802-1x/ttls-eapgtc", test_read_802_1x_ttls_eapgtc); g_test_add_func (TPATH "802-1x/password_raw", test_read_write_802_1x_password_raw); + g_test_add_func (TPATH "802-1x/tls-p12-no-client-cert", test_read_802_1x_tls_p12_no_client_cert); + g_test_add_data_func (TPATH "wired/read/aliases/good/0", GINT_TO_POINTER (0), test_read_wired_aliases_good); g_test_add_data_func (TPATH "wired/read/aliases/good/3", GINT_TO_POINTER (3), test_read_wired_aliases_good); g_test_add_func (TPATH "wired/read/aliases/bad1", test_read_wired_aliases_bad_1); diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build index 42edd438..365ae1a9 100644 --- a/src/settings/plugins/ifupdown/meson.build +++ b/src/settings/plugins/ifupdown/meson.build @@ -15,7 +15,6 @@ libnms_ifupdown_core = static_library( ) sources = files( - 'nms-ifupdown-connection.c', 'nms-ifupdown-plugin.c', ) diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c b/src/settings/plugins/ifupdown/nms-ifupdown-connection.c deleted file mode 100644 index d06078a9..00000000 --- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service (ifupdown) - * - * Alexander Sack - * - * 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. - * - * (C) Copyright 2007,2008 Canonical Ltd. - */ - -#include "nm-default.h" - -#include "nms-ifupdown-connection.h" - -#include - -#include "nm-dbus-interface.h" -#include "nm-utils.h" -#include "nm-setting-wireless-security.h" -#include "settings/nm-settings-connection.h" -#include "settings/nm-settings-plugin.h" - -#include "nms-ifupdown-parser.h" - -/*****************************************************************************/ - -struct _NMIfupdownConnection { - NMSettingsConnection parent; -}; - -struct _NMIfupdownConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMIfupdownConnection, nm_ifupdown_connection, NM_TYPE_SETTINGS_CONNECTION) - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "ifupdown" -#define _NMLOG_DOMAIN LOGD_SETTINGS -#define _NMLOG(level, ...) \ - nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \ - "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - _NMLOG_PREFIX_NAME": " \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)) - -/*****************************************************************************/ - -static gboolean -supports_secrets (NMSettingsConnection *connection, const char *setting_name) -{ - _LOGI ("supports_secrets() for setting_name: '%s'", setting_name); - - return (strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0); -} - -/*****************************************************************************/ - -static void -nm_ifupdown_connection_init (NMIfupdownConnection *connection) -{ -} - -NMIfupdownConnection * -nm_ifupdown_connection_new (if_block *block) -{ - NMIfupdownConnection *connection; - GError *error = NULL; - - g_return_val_if_fail (block != NULL, NULL); - - connection = g_object_new (NM_TYPE_IFUPDOWN_CONNECTION, NULL); - - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - if (!ifupdown_update_connection_from_if_block (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection)), - block, - &error)) { - _LOGW ("invalid connection read from /etc/network/interfaces: %s", - error->message); - g_object_unref (connection); - return NULL; - } - - return connection; -} - -static void -nm_ifupdown_connection_class_init (NMIfupdownConnectionClass *ifupdown_connection_class) -{ - NMSettingsConnectionClass *connection_class = NM_SETTINGS_CONNECTION_CLASS (ifupdown_connection_class); - - connection_class->supports_secrets = supports_secrets; -} - diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.h b/src/settings/plugins/ifupdown/nms-ifupdown-connection.h deleted file mode 100644 index 7536c23e..00000000 --- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - -/* NetworkManager system settings service (ifupdown) - * - * Alexander Sack - * - * 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. - * - * (C) Copyright 2008 Canonical Ltd. - */ - -#ifndef __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__ -#define __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__ - -#include "settings/nm-settings-connection.h" - -#include "nms-ifupdown-interface-parser.h" - -#define NM_TYPE_IFUPDOWN_CONNECTION (nm_ifupdown_connection_get_type ()) -#define NM_IFUPDOWN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnection)) -#define NM_IFUPDOWN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionClass)) -#define NM_IS_IFUPDOWN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFUPDOWN_CONNECTION)) -#define NM_IS_IFUPDOWN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFUPDOWN_CONNECTION)) -#define NM_IFUPDOWN_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionClass)) - -typedef struct _NMIfupdownConnection NMIfupdownConnection; -typedef struct _NMIfupdownConnectionClass NMIfupdownConnectionClass; - -GType nm_ifupdown_connection_get_type (void); - -NMIfupdownConnection *nm_ifupdown_connection_new (if_block *block); - -#endif /* __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__ */ diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c index 6587fc84..75f29878 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * Tom Parker diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h index f367f626..308228a4 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * Tom Parker diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c index 239f6415..41b20850 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack @@ -653,22 +651,21 @@ update_ip6_setting_from_if_block (NMConnection *connection, return TRUE; } -gboolean -ifupdown_update_connection_from_if_block (NMConnection *connection, - if_block *block, - GError **error) +NMConnection * +ifupdown_new_connection_from_if_block (if_block *block, + gboolean autoconnect, + GError **error) { + gs_unref_object NMConnection *connection = NULL; const char *type; gs_free char *idstr = NULL; gs_free char *uuid = NULL; NMSettingConnection *s_con; - gboolean success = FALSE; - s_con = nm_connection_get_setting_connection (connection); - if (!s_con) { - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - } + connection = nm_simple_connection_new (); + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); type = _ifupdownplugin_guess_connection_type (block); idstr = g_strconcat ("Ifupdown (", block->name, ")", NULL); @@ -679,11 +676,10 @@ ifupdown_update_connection_from_if_block (NMConnection *connection, NM_SETTING_CONNECTION_INTERFACE_NAME, block->name, NM_SETTING_CONNECTION_ID, idstr, NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_READ_ONLY, TRUE, - NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_AUTOCONNECT, (gboolean) (!!autoconnect), NULL); - _LOGI ("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s", + _LOGD ("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s", block->name, type, idstr, nm_setting_connection_get_uuid (s_con)); if (nm_streq (type, NM_SETTING_WIRED_SETTING_NAME)) @@ -693,13 +689,16 @@ ifupdown_update_connection_from_if_block (NMConnection *connection, update_wireless_security_setting_from_if_block (connection, block); } - if (ifparser_haskey (block, "inet6")) - success = update_ip6_setting_from_if_block (connection, block, error); - else - success = update_ip4_setting_from_if_block (connection, block, error); + if (ifparser_haskey (block, "inet6")) { + if (!update_ip6_setting_from_if_block (connection, block, error)) + return FALSE; + } else { + if (!update_ip4_setting_from_if_block (connection, block, error)) + return FALSE; + } - if (success == TRUE) - success = nm_connection_verify (connection, error); + if (!nm_connection_normalize (connection, NULL, NULL, error)) + return NULL; - return success; + return g_steal_pointer (&connection); } diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h index 6a86bf86..7569648f 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack @@ -21,16 +19,14 @@ * (C) Copyright 2008 Canonical Ltd. */ -#ifndef __PARSER_H__ -#define __PARSER_H__ +#ifndef __NMS_IFUPDOWN_PARSER_H__ +#define __NMS_IFUPDOWN_PARSER_H__ #include "nm-connection.h" - #include "nms-ifupdown-interface-parser.h" -gboolean -ifupdown_update_connection_from_if_block (NMConnection *connection, - if_block *block, - GError **error); +NMConnection *ifupdown_new_connection_from_if_block (if_block *block, + gboolean autoconnect, + GError **error); -#endif /* __PARSER_H__ */ +#endif /* __NMS_IFUPDOWN_PARSER_H__ */ diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c index 04281f00..93f1813c 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack @@ -26,23 +24,13 @@ #include "nms-ifupdown-plugin.h" -#include -#include - -#include "nm-setting-connection.h" -#include "nm-dbus-interface.h" -#include "settings/nm-settings-plugin.h" -#include "nm-setting-ip4-config.h" -#include "nm-setting-wireless.h" -#include "nm-setting-wired.h" -#include "nm-setting-ppp.h" -#include "nm-utils.h" #include "nm-core-internal.h" -#include "NetworkManagerUtils.h" +#include "nm-core-utils.h" #include "nm-config.h" +#include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-storage.h" #include "nms-ifupdown-interface-parser.h" -#include "nms-ifupdown-connection.h" #include "nms-ifupdown-parser.h" #define ENI_INTERFACES_FILE "/etc/network/interfaces" @@ -51,32 +39,36 @@ /*****************************************************************************/ +typedef struct { + NMConnection *connection; + NMSettingsStorage *storage; +} StorageData; + typedef struct { /* Stores an entry for blocks/interfaces read from /e/n/i and (if exists) - * the NMIfupdownConnection associated with the block. + * the StorageData associated with the block. */ GHashTable *eni_ifaces; - bool ifupdown_managed; -} SettingsPluginIfupdownPrivate; + bool ifupdown_managed:1; -struct _SettingsPluginIfupdown { + bool initialized:1; + + bool already_reloaded:1; +} NMSIfupdownPluginPrivate; + +struct _NMSIfupdownPlugin { NMSettingsPlugin parent; - SettingsPluginIfupdownPrivate _priv; + NMSIfupdownPluginPrivate _priv; }; -struct _SettingsPluginIfupdownClass { +struct _NMSIfupdownPluginClass { NMSettingsPluginClass parent; }; -G_DEFINE_TYPE (SettingsPluginIfupdown, settings_plugin_ifupdown, NM_TYPE_SETTINGS_PLUGIN) - -#define SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfupdown, SETTINGS_IS_PLUGIN_IFUPDOWN) +G_DEFINE_TYPE (NMSIfupdownPlugin, nms_ifupdown_plugin, NM_TYPE_SETTINGS_PLUGIN) -/*****************************************************************************/ - -static SettingsPluginIfupdown *settings_plugin_ifupdown_get (void); -NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfupdown, settings_plugin_ifupdown_get, SETTINGS_TYPE_PLUGIN_IFUPDOWN); +#define NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfupdownPlugin, NMS_IS_IFUPDOWN_PLUGIN) /*****************************************************************************/ @@ -90,46 +82,146 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfupdown, settings_plugin_ifupdown_get /*****************************************************************************/ -/* Returns the plugins currently known list of connections. The returned - * list is freed by the system settings service. - */ -static GSList* -get_connections (NMSettingsPlugin *plugin) +static GHashTable *load_eni_ifaces (NMSIfupdownPlugin *self); + +/*****************************************************************************/ + +static void +_storage_data_destroy (StorageData *sd) +{ + nm_g_object_unref (sd->connection); + nm_g_object_unref (sd->storage); + g_slice_free (StorageData, sd); +} + +/*****************************************************************************/ + +static void +initialize (NMSIfupdownPlugin *self) { - SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); - GSList *list = NULL; + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self); + gboolean ifupdown_managed; + + nm_assert (!priv->initialized); + + priv->initialized = TRUE; + + ifupdown_managed = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, + NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED, + !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT); + _LOGI ("management mode: %s", ifupdown_managed ? "managed" : "unmanaged"); + priv->ifupdown_managed = ifupdown_managed; + + priv->eni_ifaces = load_eni_ifaces (self); +} + +static void +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self); + gs_unref_hashtable GHashTable *eni_ifaces_old = NULL; GHashTableIter iter; - void *value; + StorageData *sd; + StorageData *sd2; + const char *block_name; - if (!priv->ifupdown_managed) { - _LOGD ("get_connections: not connections due to managed=false"); - return NULL; + if (!priv->initialized) + initialize (self); + else if (!priv->already_reloaded) { + /* This is the first call to reload, but we are already initialized. + * + * This happens because during start NMSettings first queries unmanaged-specs, + * and then issues a reload call right away. + * + * On future reloads, we really want to load /e/n/i again. */ + priv->already_reloaded = TRUE; + } else { + eni_ifaces_old = priv->eni_ifaces; + priv->eni_ifaces = load_eni_ifaces (self); + + g_hash_table_iter_init (&iter, eni_ifaces_old); + while (g_hash_table_iter_next (&iter, (gpointer *) &block_name, (gpointer *) &sd)) { + if (!sd) + continue; + + sd2 = g_hash_table_lookup (priv->eni_ifaces, block_name); + if (!sd2) + continue; + + nm_assert (nm_streq (nm_settings_storage_get_uuid (sd->storage), nm_settings_storage_get_uuid (sd2->storage))); + nm_g_object_ref_set (&sd2->storage, sd->storage); + g_hash_table_iter_remove (&iter); + } } + if (!priv->ifupdown_managed) + _LOGD ("load: no connections due to managed=false"); + g_hash_table_iter_init (&iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - if (value) - list = g_slist_prepend (list, value); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &sd)) { + gs_unref_object NMConnection *connection = NULL; + + if (!sd) + continue; + + connection = g_steal_pointer (&sd->connection); + + if (!priv->ifupdown_managed) + continue; + + _LOGD ("load: %s (%s)", + nm_settings_storage_get_uuid (sd->storage), + nm_connection_get_id (connection)); + callback (plugin, + sd->storage, + connection, + user_data); } + if ( eni_ifaces_old + && priv->ifupdown_managed) { + g_hash_table_iter_init (&iter, eni_ifaces_old); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &sd)) { + if (!sd) + continue; + _LOGD ("unload: %s", + nm_settings_storage_get_uuid (sd->storage)); + callback (plugin, + sd->storage, + NULL, + user_data); + } + } +} + +/*****************************************************************************/ - _LOGD ("get_connections: %u connections", g_slist_length (list)); - return list; +static GSList * +_unmanaged_specs (GHashTable *eni_ifaces) +{ + gs_free const char **keys = NULL; + GSList *specs = NULL; + guint i, len; + + keys = nm_utils_strdict_get_keys (eni_ifaces, TRUE, &len); + for (i = len; i > 0; ) { + i--; + specs = g_slist_prepend (specs, g_strdup_printf (NM_MATCH_SPEC_INTERFACE_NAME_TAG"=%s", keys[i])); + } + return specs; } -/* - * Return a list of device specifications which NetworkManager should not - * manage. Returned list will be freed by the system settings service, and - * each element must be allocated using g_malloc() or its variants. - */ static GSList* get_unmanaged_specs (NMSettingsPlugin *plugin) { - SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); - GSList *specs = NULL; - GHashTableIter iter; - const char *iface; + NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self); + + if (G_UNLIKELY (!priv->initialized)) + initialize (self); if (priv->ifupdown_managed) return NULL; @@ -137,38 +229,46 @@ get_unmanaged_specs (NMSettingsPlugin *plugin) _LOGD ("unmanaged-specs: unmanaged devices count %u", g_hash_table_size (priv->eni_ifaces)); - g_hash_table_iter_init (&iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&iter, (gpointer) &iface, NULL)) - specs = g_slist_append (specs, g_strdup_printf ("interface-name:=%s", iface)); - return specs; + return _unmanaged_specs (priv->eni_ifaces); } /*****************************************************************************/ -static void -initialize (NMSettingsPlugin *plugin) +static GHashTable * +load_eni_ifaces (NMSIfupdownPlugin *self) { - SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); + gs_unref_hashtable GHashTable *eni_ifaces = NULL; gs_unref_hashtable GHashTable *auto_ifaces = NULL; nm_auto_ifparser if_parser *parser = NULL; if_block *block; - GHashTableIter con_iter; - const char *block_name; - NMIfupdownConnection *conn; + StorageData *sd; + + eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) _storage_data_destroy); parser = ifparser_parse (ENI_INTERFACES_FILE, 0); c_list_for_each_entry (block, &parser->block_lst_head, block_lst) { - - if (NM_IN_STRSET (block->type, "auto", "allow-hotplug")) { + if (NM_IN_STRSET (block->type, "auto", + "allow-hotplug")) { if (!auto_ifaces) - auto_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - g_hash_table_add (auto_ifaces, g_strdup (block->name)); - continue; + auto_ifaces = g_hash_table_new (nm_str_hash, g_str_equal); + g_hash_table_add (auto_ifaces, (char *) block->name); } + } + + c_list_for_each_entry (block, &parser->block_lst_head, block_lst) { + + if (NM_IN_STRSET (block->type, "auto", + "allow-hotplug")) + continue; if (nm_streq (block->type, "iface")) { + gs_free_error GError *local = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMSettingsStorage *storage = NULL; + const char *uuid = NULL; + StorageData *sd_repl; + /* Bridge configuration */ if (g_str_has_prefix (block->name, "br")) { /* Try to find bridge ports */ @@ -201,13 +301,13 @@ initialize (NMSettingsPlugin *plugin) if (nm_streq (token, "none")) continue; if (state == 0) { - conn = g_hash_table_lookup (priv->eni_ifaces, block->name); - if (!conn) { + sd = g_hash_table_lookup (eni_ifaces, block->name); + if (!sd) { _LOGD ("parse: adding bridge port \"%s\"", token); - g_hash_table_insert (priv->eni_ifaces, g_strdup (token), NULL); + g_hash_table_insert (eni_ifaces, g_strdup (token), NULL); } else { _LOGD ("parse: adding bridge port \"%s\" (have connection %s)", token, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn))); + nm_settings_storage_get_uuid (sd->storage)); } } } @@ -219,107 +319,91 @@ initialize (NMSettingsPlugin *plugin) if (nm_streq (block->name, "lo")) continue; - /* Remove any connection for this block that was previously found */ - conn = g_hash_table_lookup (priv->eni_ifaces, block->name); - if (conn) { + sd_repl = g_hash_table_lookup (eni_ifaces, block->name); + if (sd_repl) { + storage = g_steal_pointer (&sd_repl->storage); _LOGD ("parse: replace connection \"%s\" (%s)", block->name, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn))); - nm_settings_connection_delete (NM_SETTINGS_CONNECTION (conn), NULL); - g_hash_table_remove (priv->eni_ifaces, block->name); + nm_settings_storage_get_uuid (sd_repl->storage)); + g_hash_table_remove (eni_ifaces, block->name); + } + + connection = ifupdown_new_connection_from_if_block (block, + auto_ifaces + && g_hash_table_contains (auto_ifaces, block->name), + &local); + + if (!connection) { + _LOGD ("parse: adding place holder for \"%s\"%s%s%s", + block->name, + NM_PRINT_FMT_QUOTED (local, " (", local->message, ")", "")); + sd = NULL; + } else { + + nmtst_connection_assert_unchanging (connection); + uuid = nm_connection_get_uuid (connection); + + if (!storage) + storage = nm_settings_storage_new (NM_SETTINGS_PLUGIN (self), uuid, NULL); + + sd = g_slice_new (StorageData); + *sd = (StorageData) { + .connection = g_steal_pointer (&connection), + .storage = g_steal_pointer (&storage), + }; + _LOGD ("parse: adding connection \"%s\" (%s)", block->name, uuid); } - /* add the new connection */ - conn = nm_ifupdown_connection_new (block); - if (conn) { - _LOGD ("parse: adding connection \"%s\" (%s)", block->name, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn))); - } else - _LOGD ("parse: adding place holder for connection \"%s\"", block->name); - g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), conn); + g_hash_table_replace (eni_ifaces, g_strdup (block->name), sd); continue; } if (nm_streq (block->type, "mapping")) { - conn = g_hash_table_lookup (priv->eni_ifaces, block->name); - if (!conn) { + sd = g_hash_table_lookup (eni_ifaces, block->name); + if (!sd) { _LOGD ("parse: adding mapping \"%s\"", block->name); - g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), NULL); + g_hash_table_insert (eni_ifaces, g_strdup (block->name), NULL); } else { _LOGD ("parse: adding mapping \"%s\" (have connection %s)", block->name, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn))); + nm_settings_storage_get_uuid (sd->storage)); } continue; } } - /* Make 'auto' interfaces autoconnect=TRUE */ - g_hash_table_iter_init (&con_iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&con_iter, (gpointer) &block_name, (gpointer) &conn)) { - NMSettingConnection *setting; + nm_clear_pointer (&auto_ifaces, g_hash_table_destroy); - if ( !conn - || !auto_ifaces - || !g_hash_table_contains (auto_ifaces, block_name)) - continue; - - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - setting = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (conn))); - g_object_set (setting, NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, NULL); - } - - /* Check the config file to find out whether to manage interfaces */ - priv->ifupdown_managed = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, - NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, - NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED, - !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT); - _LOGI ("management mode: %s", priv->ifupdown_managed ? "managed" : "unmanaged"); - - /* Now if we're running in managed mode, let NM know there are new connections */ - if (priv->ifupdown_managed) { - GHashTableIter iter; - - g_hash_table_iter_init (&iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &conn)) { - if (conn) { - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (conn)); - } - } - } + return g_steal_pointer (&eni_ifaces); } /*****************************************************************************/ static void -settings_plugin_ifupdown_init (SettingsPluginIfupdown *self) +nms_ifupdown_plugin_init (NMSIfupdownPlugin *self) { - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); - - priv->eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void dispose (GObject *object) { - SettingsPluginIfupdown *plugin = SETTINGS_PLUGIN_IFUPDOWN (object); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (plugin); + NMSIfupdownPlugin *plugin = NMS_IFUPDOWN_PLUGIN (object); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (plugin); g_clear_pointer (&priv->eni_ifaces, g_hash_table_destroy); - G_OBJECT_CLASS (settings_plugin_ifupdown_parent_class)->dispose (object); + G_OBJECT_CLASS (nms_ifupdown_plugin_parent_class)->dispose (object); } static void -settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass) +nms_ifupdown_plugin_class_init (NMSIfupdownPluginClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass); object_class->dispose = dispose; - plugin_class->initialize = initialize; - plugin_class->get_connections = get_connections; + plugin_class->plugin_name = "ifupdown"; + plugin_class->reload_connections = reload_connections; plugin_class->get_unmanaged_specs = get_unmanaged_specs; } @@ -328,5 +412,5 @@ settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass) G_MODULE_EXPORT NMSettingsPlugin * nm_settings_plugin_factory (void) { - return NM_SETTINGS_PLUGIN (g_object_ref (settings_plugin_ifupdown_get ())); + return g_object_new (NMS_TYPE_IFUPDOWN_PLUGIN, NULL); } diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h index c2a3639c..10ea2be4 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack @@ -21,21 +19,21 @@ * (C) Copyright 2008 Canonical Ltd. */ -#ifndef _PLUGIN_H_ -#define _PLUGIN_H_ +#ifndef __NMS_IFUPDOWN_PLUGIN_H__ +#define __NMS_IFUPDOWN_PLUGIN_H__ #define PLUGIN_NAME "ifupdown" -#define SETTINGS_TYPE_PLUGIN_IFUPDOWN (settings_plugin_ifupdown_get_type ()) -#define SETTINGS_PLUGIN_IFUPDOWN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdown)) -#define SETTINGS_PLUGIN_IFUPDOWN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdownClass)) -#define SETTINGS_IS_PLUGIN_IFUPDOWN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN)) -#define SETTINGS_IS_PLUGIN_IFUPDOWN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFUPDOWN)) -#define SETTINGS_PLUGIN_IFUPDOWN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdownClass)) +#define NMS_TYPE_IFUPDOWN_PLUGIN (nms_ifupdown_plugin_get_type ()) +#define NMS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPlugin)) +#define NMS_IFUPDOWN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass)) +#define NMS_IS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFUPDOWN_PLUGIN)) +#define NMS_IS_IFUPDOWN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFUPDOWN_PLUGIN)) +#define NMS_IFUPDOWN_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass)) -typedef struct _SettingsPluginIfupdown SettingsPluginIfupdown; -typedef struct _SettingsPluginIfupdownClass SettingsPluginIfupdownClass; +typedef struct _NMSIfupdownPlugin NMSIfupdownPlugin; +typedef struct _NMSIfupdownPluginClass NMSIfupdownPluginClass; -GType settings_plugin_ifupdown_get_type (void); +GType nms_ifupdown_plugin_get_type (void); -#endif /* _PLUGIN_H_ */ +#endif /* __NMS_IFUPDOWN_PLUGIN_H__ */ diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c index 674cb19c..4adcf085 100644 --- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c +++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * 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 @@ -31,6 +30,29 @@ /*****************************************************************************/ +#define _connection_from_if_block(block) \ + ({ \ + NMConnection *_con; \ + if_block *_block = (block); \ + GError *_local = NULL; \ + \ + g_assert (_block); \ + _con = ifupdown_new_connection_from_if_block (_block, FALSE, &_local); \ + nmtst_assert_success (NM_IS_CONNECTION (_con), _local); \ + nmtst_assert_connection_verifies_without_normalization (_con); \ + _con; \ + }) + +#define _connection_first_from_parser(parser) \ + ({ \ + if_parser *_parser = (parser); \ + \ + g_assert (_parser); \ + _connection_from_if_block (ifparser_getfirst (_parser)); \ + }) + +/*****************************************************************************/ + typedef struct { char *key; char *data; @@ -453,26 +475,14 @@ test16_missing_newline (void) static void test17_read_static_ipv4 (void) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; NMSettingConnection *s_con; NMSettingIPConfig *s_ip4; NMSettingWired *s_wired; - GError *error = NULL; - gboolean success; NMIPAddress *ip4_addr; - if_block *block = NULL; nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test17-wired-static-verify-ip4"); - block = ifparser_getfirst (parser); - connection = nm_simple_connection_new(); - g_assert (connection); - - ifupdown_update_connection_from_if_block (connection, block, &error); - g_assert_no_error (error); - - success = nm_connection_verify (connection, &error); - g_assert_no_error (error); - g_assert (success); + connection = _connection_first_from_parser (parser); /* ===== CONNECTION SETTING ===== */ s_con = nm_connection_get_setting_connection (connection); @@ -501,32 +511,19 @@ test17_read_static_ipv4 (void) g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip4), ==, 2); g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 0), ==, "example.com"); g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 1), ==, "foo.example.com"); - - g_object_unref (connection); } static void test18_read_static_ipv6 (void) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; NMSettingConnection *s_con; NMSettingIPConfig *s_ip6; NMSettingWired *s_wired; - GError *error = NULL; - gboolean success; NMIPAddress *ip6_addr; - if_block *block = NULL; nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test18-wired-static-verify-ip6"); - block = ifparser_getfirst (parser); - connection = nm_simple_connection_new(); - g_assert (connection); - ifupdown_update_connection_from_if_block (connection, block, &error); - g_assert_no_error (error); - - success = nm_connection_verify (connection, &error); - g_assert_no_error (error); - g_assert (success); + connection = _connection_first_from_parser (parser); /* ===== CONNECTION SETTING ===== */ s_con = nm_connection_get_setting_connection (connection); @@ -555,30 +552,17 @@ test18_read_static_ipv6 (void) g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip6), ==, 2); g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 0), ==, "example.com"); g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 1), ==, "foo.example.com"); - - g_object_unref (connection); } static void test19_read_static_ipv4_plen (void) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; NMSettingIPConfig *s_ip4; - GError *error = NULL; NMIPAddress *ip4_addr; - if_block *block = NULL; - gboolean success; nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test19-wired-static-verify-ip4-plen"); - block = ifparser_getfirst (parser); - connection = nm_simple_connection_new(); - g_assert (connection); - ifupdown_update_connection_from_if_block (connection, block, &error); - g_assert_no_error (error); - - success = nm_connection_verify (connection, &error); - g_assert_no_error (error); - g_assert (success); + connection = _connection_first_from_parser (parser); /* ===== IPv4 SETTING ===== */ s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -589,8 +573,6 @@ test19_read_static_ipv4_plen (void) g_assert (ip4_addr != NULL); g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "10.0.0.3"); g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 8); - - g_object_unref (connection); } static void @@ -671,4 +653,3 @@ main (int argc, char **argv) return g_test_run (); } - diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c deleted file mode 100644 index 3b362978..00000000 --- a/src/settings/plugins/keyfile/nms-keyfile-connection.c +++ /dev/null @@ -1,186 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - keyfile plugin - * - * 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 (C) 2008 Novell, Inc. - * Copyright (C) 2008 - 2012 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-keyfile-connection.h" - -#include - -#include "nm-dbus-interface.h" -#include "nm-setting-connection.h" -#include "nm-utils.h" - -#include "settings/nm-settings-plugin.h" - -#include "nms-keyfile-reader.h" -#include "nms-keyfile-writer.h" -#include "nms-keyfile-utils.h" - -/*****************************************************************************/ - -struct _NMSKeyfileConnection { - NMSettingsConnection parent; -}; - -struct _NMSKeyfileConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMSKeyfileConnection, nms_keyfile_connection, NM_TYPE_SETTINGS_CONNECTION) - -/*****************************************************************************/ - -static gboolean -commit_changes (NMSettingsConnection *connection, - NMConnection *new_connection, - NMSettingsConnectionCommitReason commit_reason, - NMConnection **out_reread_connection, - char **out_logmsg_change, - GError **error) -{ - gs_free char *path = NULL; - gs_unref_object NMConnection *reread = NULL; - gboolean reread_same = FALSE; - - nm_assert (out_reread_connection && !*out_reread_connection); - nm_assert (!out_logmsg_change || !*out_logmsg_change); - - if (!nms_keyfile_writer_connection (new_connection, - TRUE, - nm_settings_connection_get_filename (connection), - NM_FLAGS_ALL (commit_reason, NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION - | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED), - &path, - &reread, - &reread_same, - error)) - return FALSE; - - if (!nm_streq0 (path, nm_settings_connection_get_filename (connection))) { - gs_free char *old_path = g_strdup (nm_settings_connection_get_filename (connection)); - - nm_settings_connection_set_filename (connection, path); - if (old_path) { - NM_SET_OUT (out_logmsg_change, - g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and rename from \"%s\"", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection), - old_path)); - } else { - NM_SET_OUT (out_logmsg_change, - g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and persist connection", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection))); - } - } else { - NM_SET_OUT (out_logmsg_change, - g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT, - NMS_KEYFILE_CONNECTION_LOG_ARG (connection))); - } - - if (reread && !reread_same) - *out_reread_connection = g_steal_pointer (&reread); - - return TRUE; -} - -static gboolean -delete (NMSettingsConnection *connection, - GError **error) -{ - const char *path; - - path = nm_settings_connection_get_filename (connection); - if (path) - g_unlink (path); - return TRUE; -} - -/*****************************************************************************/ - -static void -nms_keyfile_connection_init (NMSKeyfileConnection *connection) -{ -} - -NMSKeyfileConnection * -nms_keyfile_connection_new (NMConnection *source, - const char *full_path, - const char *profile_dir, - GError **error) -{ - GObject *object; - NMConnection *tmp; - const char *uuid; - gboolean update_unsaved = TRUE; - - nm_assert (source || full_path); - nm_assert (!full_path || full_path[0] == '/'); - nm_assert (!profile_dir || profile_dir[0] == '/'); - - /* If we're given a connection already, prefer that instead of re-reading */ - if (source) - tmp = g_object_ref (source); - else { - tmp = nms_keyfile_reader_from_file (full_path, profile_dir, error); - if (!tmp) - return NULL; - - uuid = nm_connection_get_uuid (tmp); - if (!uuid) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Connection in file %s had no UUID", full_path); - g_object_unref (tmp); - return NULL; - } - - /* If we just read the connection from disk, it's clearly not Unsaved */ - update_unsaved = FALSE; - } - - object = g_object_new (NMS_TYPE_KEYFILE_CONNECTION, - NM_SETTINGS_CONNECTION_FILENAME, full_path, - NULL); - - /* Update our settings with what was read from the file */ - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object), - tmp, - update_unsaved - ? NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED - : NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - NULL, - error)) { - g_object_unref (object); - object = NULL; - } - - g_object_unref (tmp); - return (NMSKeyfileConnection *) object; -} - -static void -nms_keyfile_connection_class_init (NMSKeyfileConnectionClass *keyfile_connection_class) -{ - NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (keyfile_connection_class); - - settings_class->commit_changes = commit_changes; - settings_class->delete = delete; -} diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.h b/src/settings/plugins/keyfile/nms-keyfile-connection.h deleted file mode 100644 index 0773ced0..00000000 --- a/src/settings/plugins/keyfile/nms-keyfile-connection.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - keyfile plugin - * - * 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 (C) 2008 Novell, Inc. - * Copyright (C) 2008 - 2012 Red Hat, Inc. - */ - -#ifndef __NMS_KEYFILE_CONNECTION_H__ -#define __NMS_KEYFILE_CONNECTION_H__ - -#include "settings/nm-settings-connection.h" - -#define NMS_TYPE_KEYFILE_CONNECTION (nms_keyfile_connection_get_type ()) -#define NMS_KEYFILE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnection)) -#define NMS_KEYFILE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnectionClass)) -#define NMS_IS_KEYFILE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_KEYFILE_CONNECTION)) -#define NMS_IS_KEYFILE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_KEYFILE_CONNECTION)) -#define NMS_KEYFILE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnectionClass)) - -typedef struct _NMSKeyfileConnection NMSKeyfileConnection; -typedef struct _NMSKeyfileConnectionClass NMSKeyfileConnectionClass; - -GType nms_keyfile_connection_get_type (void); - -NMSKeyfileConnection *nms_keyfile_connection_new (NMConnection *source, - const char *full_path, - const char *profile_dir, - GError **error); - -#endif /* __NMS_KEYFILE_CONNECTION_H__ */ diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index c13cc1ff..fbe70ef4 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -16,7 +15,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 - 2013 Red Hat, Inc. + * Copyright (C) 2008 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -26,7 +25,11 @@ #include #include #include -#include +#include + +#include "nm-std-aux/c-list-util.h" +#include "nm-glib-aux/nm-c-list.h" +#include "nm-glib-aux/nm-io-utils.h" #include "nm-connection.h" #include "nm-setting.h" @@ -36,22 +39,42 @@ #include "nm-core-internal.h" #include "nm-keyfile-internal.h" +#include "systemd/nm-sd-utils-shared.h" + #include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-storage.h" +#include "settings/nm-settings-utils.h" -#include "nms-keyfile-connection.h" +#include "nms-keyfile-storage.h" #include "nms-keyfile-writer.h" +#include "nms-keyfile-reader.h" #include "nms-keyfile-utils.h" /*****************************************************************************/ typedef struct { - GHashTable *connections; /* uuid::connection */ - - gboolean initialized; - GFileMonitor *monitor; - gulong monitor_id; NMConfig *config; + + /* there can/could be multiple read-only directories. For example, one + * could set dirname_libs to + * - /usr/lib/NetworkManager/profiles/ + * - /etc/NetworkManager/system-connections + * and leave dirname_etc unset. In this case, there would be multiple + * read-only directories. + * + * Directories that come later have higher priority and shadow profiles + * from earlier directories. + * + * Currently, this is only an array with zero or one elements. It could be + * easily extended to support multiple read-only directories. + */ + char *dirname_libs[2]; + char *dirname_etc; + char *dirname_run; + + NMSettUtilStorages storages; + } NMSKeyfilePluginPrivate; struct _NMSKeyfilePlugin { @@ -65,7 +88,7 @@ struct _NMSKeyfilePluginClass { G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN) -#define NMS_KEYFILE_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSKeyfilePlugin, NMS_IS_KEYFILE_PLUGIN) +#define NMS_KEYFILE_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSKeyfilePlugin, NMS_IS_KEYFILE_PLUGIN, NMSettingsPlugin) /*****************************************************************************/ @@ -79,494 +102,1112 @@ G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN) /*****************************************************************************/ -static void -connection_removed_cb (NMSettingsConnection *sett_conn, NMSKeyfilePlugin *self) +static const char * +_extra_flags_to_string (char *str, gsize str_len, gboolean is_nm_generated, gboolean is_volatile) { - g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections, - nm_settings_connection_get_uuid (sett_conn)); + const char *str0 = str; + + if ( !is_nm_generated + && !is_volatile) + nm_utils_strbuf_append_str (&str, &str_len, ""); + else { + nm_utils_strbuf_append_str (&str, &str_len, " ("); + if (is_nm_generated) { + nm_utils_strbuf_append_str (&str, &str_len, "nm-generated"); + if (is_volatile) + nm_utils_strbuf_append_c (&str, &str_len, ','); + } + if (is_volatile) + nm_utils_strbuf_append_str (&str, &str_len, "volatile"); + nm_utils_strbuf_append_c (&str, &str_len, ')'); + } + + return str0; } -/* Monitoring */ +static gboolean +_ignore_filename (NMSKeyfileStorageType storage_type, + const char *filename) +{ + /* for backward-compatibility, we don't require an extension for + * files under "/etc/...". */ + return nm_keyfile_utils_ignore_filename (filename, + (storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC)); +} -static void -remove_connection (NMSKeyfilePlugin *self, NMSKeyfileConnection *connection) +static const char * +_get_plugin_dir (NMSKeyfilePluginPrivate *priv) { - gboolean removed; + /* the plugin dir is only needed to generate connection.uuid value via + * nm_keyfile_read_ensure_uuid(). This is either the configured /etc + * directory, of the compile-time default (in case the /etc directory + * is disabled). */ + return priv->dirname_etc ?: NM_KEYFILE_PATH_NAME_ETC_DEFAULT; +} - g_return_if_fail (connection != NULL); +static gboolean +_path_detect_storage_type (const char *full_filename, + const char *const*dirname_libs, + const char *dirname_etc, + const char *dirname_run, + NMSKeyfileStorageType *out_storage_type, + const char **out_dirname, + const char **out_filename, + gboolean *out_is_nmmeta_file, + gboolean *out_failed_due_to_invalid_filename) +{ + NMSKeyfileStorageType storage_type; + const char *filename = NULL; + const char *dirname = NULL; + guint i; + gboolean is_nmmeta_file = FALSE; + + NM_SET_OUT (out_failed_due_to_invalid_filename, FALSE); + + if (full_filename[0] != '/') + return FALSE; + + if ( dirname_run + && (filename = nm_utils_file_is_in_path (full_filename, dirname_run))) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN; + dirname = dirname_run; + } else if ( dirname_etc + && (filename = nm_utils_file_is_in_path (full_filename, dirname_etc))) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC; + dirname = dirname_etc; + } else { + for (i = 0; dirname_libs && dirname_libs[i]; i++) { + if ((filename = nm_utils_file_is_in_path (full_filename, dirname_libs[i]))) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_LIB (i); + dirname = dirname_libs[i]; + break; + } + } + if (!dirname) + return FALSE; + } + + if (_ignore_filename (storage_type, filename)) { - _LOGI ("removed " NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection)); + /* we accept nmmeta files, but only in /etc and /run directories. */ - /* Removing from the hash table should drop the last reference */ - g_object_ref (connection); - g_signal_handlers_disconnect_by_func (connection, connection_removed_cb, self); - removed = g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))); - nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection)); - g_object_unref (connection); + if ( !NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC) + || !nms_keyfile_nmmeta_check_filename (filename, NULL)) { + NM_SET_OUT (out_failed_due_to_invalid_filename, TRUE); + return FALSE; + } + + is_nmmeta_file = TRUE; + } - g_return_if_fail (removed); + NM_SET_OUT (out_storage_type, storage_type); + NM_SET_OUT (out_dirname, dirname); + NM_SET_OUT (out_filename, filename); + NM_SET_OUT (out_is_nmmeta_file, is_nmmeta_file); + return TRUE; } -static NMSKeyfileConnection * -find_by_path (NMSKeyfilePlugin *self, const char *path) +/*****************************************************************************/ + +static NMConnection * +_read_from_file (const char *full_filename, + const char *plugin_dir, + struct stat *out_stat, + NMTernary *out_is_nm_generated, + NMTernary *out_is_volatile, + char **out_shadowed_storage, + NMTernary *out_shadowed_owned, + GError **error) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - GHashTableIter iter; - NMSettingsConnection *candidate = NULL; + NMConnection *connection; - g_return_val_if_fail (path != NULL, NULL); + nm_assert (full_filename && full_filename[0] == '/'); - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) { - if (g_strcmp0 (path, nm_settings_connection_get_filename (candidate)) == 0) - return NMS_KEYFILE_CONNECTION (candidate); - } - return NULL; + connection = nms_keyfile_reader_from_file (full_filename, + plugin_dir, + out_stat, + out_is_nm_generated, + out_is_volatile, + out_shadowed_storage, + out_shadowed_owned, + error); + + nm_assert (!connection || (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS)); + nm_assert (!connection || nm_utils_is_uuid (nm_connection_get_uuid (connection))); + + return connection; } -/* update_connection: - * @self: the plugin instance - * @source: if %NULL, this re-reads the connection from @full_path - * and updates it. When passing @source, this adds a connection from - * memory. - * @full_path: the filename of the keyfile to be loaded - * @connection: an existing connection that might be updated. - * If given, @connection must be an existing connection that is currently - * owned by the plugin. - * @protect_existing_connection: if %TRUE, and !@connection, we don't allow updating - * an existing connection with the same UUID. - * If %TRUE and @connection, allow updating only if the reload would modify - * @connection (without changing its UUID) or if we would create a new connection. - * In other words, if this parameter is %TRUE, we only allow creating a - * new connection (with an unseen UUID) or updating the passed in @connection - * (whereas the UUID cannot change). - * Note, that this allows for @connection to be replaced by a new connection. - * @protected_connections: (allow-none): if given, we only update an - * existing connection if it is not contained in this hash. - * @error: error in case of failure - * - * Loads a connection from file @full_path. This can both be used to - * load a connection initially or to update an existing connection. - * - * If you pass in an existing connection and the reloaded file happens - * to have a different UUID, the connection is deleted. - * Beware, that means that after the function, you have a dangling pointer - * if the returned connection is different from @connection. - * - * Returns: the updated connection. - * */ -static NMSKeyfileConnection * -update_connection (NMSKeyfilePlugin *self, - NMConnection *source, - const char *full_path, - NMSKeyfileConnection *connection, - gboolean protect_existing_connection, - GHashTable *protected_connections, - GError **error) +/*****************************************************************************/ + +static void +_nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */, + gpointer storage /* NMSKeyfileStorage */, + gboolean tracked) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - NMSKeyfileConnection *connection_new; - NMSKeyfileConnection *connection_by_uuid; - GError *local = NULL; +#if NM_MORE_ASSERTS + NMSettUtilStorageByUuidHead *sbuh; const char *uuid; - g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL); - g_return_val_if_fail (full_path || source, NULL); + nm_assert (!plugin || NMS_IS_KEYFILE_PLUGIN (plugin)); + nm_assert (NMS_IS_KEYFILE_STORAGE (storage)); + nm_assert (!plugin || plugin == nm_settings_storage_get_plugin (storage)); - if (full_path) - _LOGD ("loading from file \"%s\"...", full_path); + nm_assert (({ + const char *f = nms_keyfile_storage_get_filename (storage); + f && f[0] == '/'; + })); - if ( !nm_utils_file_is_in_path (full_path, nms_keyfile_utils_get_path ()) - && !nm_utils_file_is_in_path (full_path, NM_KEYFILE_PATH_NAME_RUN)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "File not in recognized system-connections directory"); - return FALSE; - } + uuid = nms_keyfile_storage_get_uuid (storage); - connection_new = nms_keyfile_connection_new (source, full_path, nms_keyfile_utils_get_path (), &local); - if (!connection_new) { - /* Error; remove the connection */ - if (source) - _LOGW ("error creating connection %s: %s", nm_connection_get_uuid (source), local->message); - else - _LOGW ("error loading connection from file %s: %s", full_path, local->message); - if ( connection - && !protect_existing_connection - && (!protected_connections || !g_hash_table_contains (protected_connections, connection))) - remove_connection (self, connection); - g_propagate_error (error, local); - return NULL; + nm_assert (nm_utils_is_uuid (uuid)); + + nm_assert ( ((NMSKeyfileStorage *) storage)->is_meta_data + || !(((NMSKeyfileStorage *) storage)->u.conn_data.connection) + || ( NM_IS_CONNECTION ((((NMSKeyfileStorage *) storage)->u.conn_data.connection)) + && nm_streq0 (uuid, nm_connection_get_uuid ((((NMSKeyfileStorage *) storage)->u.conn_data.connection))))); + + nm_assert ( !tracked + || !plugin + || c_list_contains (&NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages._storage_lst_head, + &NMS_KEYFILE_STORAGE (storage)->parent._storage_lst)); + + nm_assert ( !tracked + || !plugin + || storage == g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx_by_filename, + nms_keyfile_storage_get_filename (storage))); + + if ( tracked + && plugin) { + sbuh = g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx_by_uuid, &uuid); + nm_assert (sbuh); + nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &((NMSKeyfileStorage *) storage)->parent._storage_by_uuid_lst)); } +#endif +} - uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new)); - connection_by_uuid = g_hash_table_lookup (priv->connections, uuid); +/*****************************************************************************/ - if ( connection - && connection != connection_by_uuid) { +static NMSKeyfileStorage * +_load_file (NMSKeyfilePlugin *self, + const char *dirname, + const char *filename, + NMSKeyfileStorageType storage_type, + GError **error) +{ + NMSKeyfilePluginPrivate *priv; + gs_unref_object NMConnection *connection = NULL; + NMTernary is_volatile_opt; + NMTernary is_nm_generated_opt; + NMTernary shadowed_owned_opt; + gs_free char *shadowed_storage = NULL; + gs_free_error GError *local = NULL; + gs_free char *full_filename = NULL; + struct stat st; - if ( (protect_existing_connection && connection_by_uuid != NULL) - || (protected_connections && g_hash_table_contains (protected_connections, connection))) { - NMSKeyfileConnection *conflicting = (protect_existing_connection && connection_by_uuid != NULL) ? connection_by_uuid : connection; + if (_ignore_filename (storage_type, filename)) { + gs_free char *nmmeta = NULL; + gs_free char *loaded_path = NULL; + gs_free char *shadowed_storage_filename = NULL; - if (source) - _LOGW ("cannot update protected "NMS_KEYFILE_CONNECTION_LOG_FMT" connection due to conflicting UUID %s", NMS_KEYFILE_CONNECTION_LOG_ARG (conflicting), uuid); + if (!nms_keyfile_nmmeta_check_filename (filename, NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip due to invalid filename"); + else + _LOGT ("load: \"%s/%s\": skip file due to invalid filename", dirname, filename); + return NULL; + } + if (!nms_keyfile_nmmeta_read (dirname, + filename, + &full_filename, + &nmmeta, + &loaded_path, + &shadowed_storage_filename, + NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip unreadable nmmeta file"); else - _LOGW ("cannot load %s due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, full_path, NMS_KEYFILE_CONNECTION_LOG_ARG (conflicting)); - g_object_unref (connection_new); - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Cannot update protected connection due to conflicting UUID"); + _LOGT ("load: \"%s/%s\": skip unreadable nmmeta file", dirname, filename); + return NULL; + } + nm_assert (loaded_path); + if (!NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip nmmeta file from read-only directory"); + else + _LOGT ("load: \"%s/%s\": skip nmmeta file from read-only directory", dirname, filename); + return NULL; + } + if (!nm_streq (loaded_path, NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip nmmeta file not symlinking %s", NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL); + else + _LOGT ("load: \"%s/%s\": skip nmmeta file not symlinking to %s", dirname, filename, NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL); return NULL; } - /* The new connection has a different UUID then the original one. - * Remove @connection. */ - remove_connection (self, connection); + return nms_keyfile_storage_new_tombstone (self, + nmmeta, + full_filename, + storage_type, + shadowed_storage_filename); } - if ( connection_by_uuid - && ( (!connection && protect_existing_connection) - || (protected_connections && g_hash_table_contains (protected_connections, connection_by_uuid)))) { - if (source) - _LOGW ("cannot update connection due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_by_uuid)); + full_filename = g_build_filename (dirname, filename, NULL); + + priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + + connection = _read_from_file (full_filename, + _get_plugin_dir (priv), + &st, + &is_nm_generated_opt, + &is_volatile_opt, + &shadowed_storage, + &shadowed_owned_opt, + &local); + if (!connection) { + if (error) + g_propagate_error (error, local); else - _LOGW ("cannot load %s due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, full_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_by_uuid)); - g_object_unref (connection_new); - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Skip updating protected connection during reload"); + _LOGW ("load: \"%s\": failed to load connection: %s", full_filename, local->message); return NULL; } - if (connection_by_uuid) { - const char *old_path; - - old_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)); - - if (nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)), - nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)), - NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | - NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) { - /* Nothing to do... except updating the path. */ - if (old_path && g_strcmp0 (old_path, full_path) != 0) - _LOGI ("rename \"%s\" to "NMS_KEYFILE_CONNECTION_LOG_FMT" without other changes", old_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - } else { - /* An existing connection changed. */ - if (source) - _LOGI ("update "NMS_KEYFILE_CONNECTION_LOG_FMT" from %s", NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new), NMS_KEYFILE_CONNECTION_LOG_PATH (old_path)); - else if (!g_strcmp0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new)))) - _LOGI ("update "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - else if (old_path) - _LOGI ("rename \"%s\" to "NMS_KEYFILE_CONNECTION_LOG_FMT, old_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - else - _LOGI ("update and persist "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid), - nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)), - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - "keyfile-update", - &local)) { - /* Shouldn't ever get here as 'connection_new' was verified by the reader already - * and the UUID did not change. */ - g_assert_not_reached (); - } - g_assert_no_error (local); - } - nm_settings_connection_set_filename (NM_SETTINGS_CONNECTION (connection_by_uuid), full_path); - g_object_unref (connection_new); - return connection_by_uuid; - } else { - if (source) - _LOGI ("add connection "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - else - _LOGI ("new connection "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - g_hash_table_insert (priv->connections, g_strdup (uuid), connection_new); - - g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED, - G_CALLBACK (connection_removed_cb), - self); - - if (!source) { - /* Only raise the signal if we were called without source, i.e. if we read the connection from file. - * Otherwise, we were called by add_connection() which does not expect the signal. */ - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (connection_new)); - } + return nms_keyfile_storage_new_connection (self, + g_steal_pointer (&connection), + full_filename, + storage_type, + is_nm_generated_opt, + is_volatile_opt, + shadowed_storage, + shadowed_owned_opt, + &st.st_mtim); +} - return connection_new; - } +static NMSKeyfileStorage * +_load_file_from_path (NMSKeyfilePlugin *self, + const char *full_filename, + NMSKeyfileStorageType storage_type, + GError **error) +{ + gs_free char *f_dirname_free = NULL; + const char *f_filename; + const char *f_dirname; + + nm_assert (full_filename && full_filename[0] == '/'); + + f_filename = strrchr (full_filename, '/'); + f_dirname = nm_strndup_a (300, full_filename, f_filename - full_filename, &f_dirname_free); + f_filename++; + return _load_file (self, + f_dirname, + f_filename, + storage_type, + error); } static void -dir_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) +_load_dir (NMSKeyfilePlugin *self, + NMSKeyfileStorageType storage_type, + const char *dirname, + NMSettUtilStorages *storages) { - NMSettingsPlugin *config = NM_SETTINGS_PLUGIN (user_data); - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config); - NMSKeyfileConnection *connection; - char *full_path; - gboolean exists; - - full_path = g_file_get_path (file); - if (nm_keyfile_utils_ignore_filename (full_path, FALSE)) { - g_free (full_path); + const char *filename; + GDir *dir; + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + + dir = g_dir_open (dirname, 0, NULL); + if (!dir) return; - } - exists = g_file_test (full_path, G_FILE_TEST_EXISTS); - - _LOGD ("dir_changed(%s) = %d; file %s", full_path, event_type, exists ? "exists" : "does not exist"); - - connection = find_by_path (self, full_path); - - switch (event_type) { - case G_FILE_MONITOR_EVENT_DELETED: - if (!exists && connection) - remove_connection (NMS_KEYFILE_PLUGIN (config), connection); - break; - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - if (exists) - update_connection (NMS_KEYFILE_PLUGIN (config), NULL, full_path, connection, TRUE, NULL, NULL); - break; - default: - break; + + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); + + while ((filename = g_dir_read_name (dir))) { + gs_unref_object NMSKeyfileStorage *storage = NULL; + + filename = g_strdup (filename); + if (!g_hash_table_add (dupl_filenames, (char *) filename)) + continue; + + storage = _load_file (self, + dirname, + filename, + storage_type, + NULL); + if (!storage) + continue; + + nm_sett_util_storages_add_take (storages, g_steal_pointer (&storage)); } - g_free (full_path); + g_dir_close (dir); + +#if NM_MORE_ASSERTS + { + NMSKeyfileStorage *storage; + + c_list_for_each_entry (storage, &storages->_storage_lst_head, parent._storage_lst) + nm_assert (NMS_IS_KEYFILE_STORAGE (storage)); + } +#endif } +/*****************************************************************************/ + static void -config_changed_cb (NMConfig *config, - NMConfigData *config_data, - NMConfigChangeFlags changes, - NMConfigData *old_data, - NMSKeyfilePlugin *self) +_storages_consolidate (NMSKeyfilePlugin *self, + NMSettUtilStorages *storages_new, + gboolean replace_all, + GHashTable *storages_replaced, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - gs_free char *old_value = NULL; - gs_free char *new_value = NULL; + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted); + gs_unref_ptrarray GPtrArray *storages_modified = NULL; + CList storages_deleted; + NMSKeyfileStorage *storage_safe; + NMSKeyfileStorage *storage_new; + NMSKeyfileStorage *storage_old; + NMSKeyfileStorage *storage; + guint i; - old_value = nm_config_data_get_value (old_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC); - new_value = nm_config_data_get_value (config_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC); + storages_modified = g_ptr_array_new_with_free_func (g_object_unref); + c_list_init (&storages_deleted); - if (!nm_streq0 (old_value, new_value)) - _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); -} + c_list_for_each_entry (storage_old, &priv->storages._storage_lst_head, parent._storage_lst) + storage_old->is_dirty = TRUE; -static void -setup_monitoring (NMSettingsPlugin *config) -{ - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config); - GFile *file; - GFileMonitor *monitor; - - if (nm_config_get_monitor_connection_files (priv->config)) { - file = g_file_new_for_path (nms_keyfile_utils_get_path ()); - monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - - if (monitor) { - priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), config); - priv->monitor = monitor; + c_list_for_each_entry_safe (storage_new, storage_safe, &storages_new->_storage_lst_head, parent._storage_lst) { + storage_old = nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage_new)); + + nm_sett_util_storages_steal (storages_new, storage_new); + + if ( !storage_old + || !nm_streq (nms_keyfile_storage_get_uuid (storage_new), nms_keyfile_storage_get_uuid (storage_old))) { + if (storage_old) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_by_uuid_lst); + } + storage_new->is_dirty = FALSE; + nm_sett_util_storages_add_take (&priv->storages, storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_new)); + continue; } - } - g_signal_connect (G_OBJECT (priv->config), - NM_CONFIG_SIGNAL_CONFIG_CHANGED, - G_CALLBACK (config_changed_cb), - config); -} + storage_old->is_dirty = FALSE; + nms_keyfile_storage_copy_content (storage_old, storage_new); + nms_keyfile_storage_destroy (storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_old)); + } -static GHashTable * -_paths_from_connections (GHashTable *connections) -{ - GHashTableIter iter; - NMSKeyfileConnection *connection; - GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); + c_list_for_each_entry_safe (storage_old, storage_safe, &priv->storages._storage_lst_head, parent._storage_lst) { + if (!storage_old->is_dirty) + continue; + if ( replace_all + || ( storages_replaced + && g_hash_table_contains (storages_replaced, storage_old))) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_by_uuid_lst); + } + } - g_hash_table_iter_init (&iter, connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { - const char *path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection)); + /* raise events. */ - if (path) - g_hash_table_add (paths, (void *) path); + for (i = 0; i < storages_modified->len; i++) { + storage = storages_modified->pdata[i]; + storage->is_dirty = TRUE; } - return paths; -} -static int -_sort_paths (const char **f1, const char **f2, GHashTable *paths) -{ - struct stat st; - gboolean c1, c2; - gint64 m1, m2; + for (i = 0; i < storages_modified->len; i++) { + gs_unref_object NMConnection *connection = NULL; - c1 = !!g_hash_table_contains (paths, *f1); - c2 = !!g_hash_table_contains (paths, *f2); - if (c1 != c2) - return c1 ? -1 : 1; + storage = storages_modified->pdata[i]; - m1 = stat (*f1, &st) == 0 ? (gint64) st.st_mtime : G_MININT64; - m2 = stat (*f2, &st) == 0 ? (gint64) st.st_mtime : G_MININT64; - if (m1 != m2) - return m1 > m2 ? -1 : 1; + if (!storage->is_dirty) { + /* the entry is no longer is_dirty. In the meantime we already emited + * another signal for it. */ + continue; + } + storage->is_dirty = FALSE; - return strcmp (*f1, *f2); -} + if (c_list_is_empty (&storage->parent._storage_lst)) { + /* hm? The profile was deleted in the meantime? That is only possible + * if the signal handler called again into the plugin. In any case, the event + * was already emitted. Skip. */ + continue; + } -static void -_read_dir (GPtrArray *filenames, - const char *path, - gboolean require_extension) -{ - GDir *dir; - const char *item; - GError *error = NULL; + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage))); - dir = g_dir_open (path, 0, &error); - if (!dir) { - _LOGD ("cannot read directory '%s': %s", path, error->message); - g_clear_error (&error); - return; + connection = nms_keyfile_storage_steal_connection (storage); + + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + connection, + user_data); } - while ((item = g_dir_read_name (dir))) { - if (nm_keyfile_utils_ignore_filename (item, require_extension)) - continue; - g_ptr_array_add (filenames, g_build_filename (path, item, NULL)); + while ((storage = c_list_first_entry (&storages_deleted, NMSKeyfileStorage, parent._storage_by_uuid_lst))) { + c_list_unlink (&storage->parent._storage_by_uuid_lst); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + NULL, + user_data); + nms_keyfile_storage_destroy (storage); } - g_dir_close (dir); } +static void +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_keyfile_storage_destroy); + int i; + + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_RUN, priv->dirname_run, &storages_new); + if (priv->dirname_etc) + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_ETC, priv->dirname_etc, &storages_new); + for (i = 0; priv->dirname_libs[i]; i++) + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_LIB (i), priv->dirname_libs[i], &storages_new); + + _storages_consolidate (self, + &storages_new, + TRUE, + NULL, + callback, + user_data); +} static void -read_connections (NMSettingsPlugin *config) +load_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config); + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - GHashTable *alive_connections; - GHashTableIter iter; - NMSKeyfileConnection *connection; - GPtrArray *dead_connections = NULL; - guint i; - GPtrArray *filenames; - GHashTable *paths; + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_keyfile_storage_destroy); + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_unref_hashtable GHashTable *storages_replaced = NULL; + gs_unref_hashtable GHashTable *loaded_uuids = NULL; + const char *loaded_uuid; + GHashTableIter h_iter; + gsize i; + + if (n_entries == 0) + return; - filenames = g_ptr_array_new_with_free_func (g_free); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - _read_dir (filenames, NM_KEYFILE_PATH_NAME_RUN, TRUE); - _read_dir (filenames, nms_keyfile_utils_get_path (), FALSE); + loaded_uuids = g_hash_table_new (nm_str_hash, g_str_equal); - alive_connections = g_hash_table_new (nm_direct_hash, NULL); + storages_replaced = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL); - /* While reloading, we don't replace connections that we already loaded while - * iterating over the files. - * - * To have sensible, reproducible behavior, sort the paths by last modification - * time preferring older files. - */ - paths = _paths_from_connections (priv->connections); - g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths); - g_hash_table_destroy (paths); - - for (i = 0; i < filenames->len; i++) { - connection = update_connection (self, NULL, filenames->pdata[i], NULL, FALSE, alive_connections, NULL); - if (connection) - g_hash_table_add (alive_connections, connection); - } - g_ptr_array_free (filenames, TRUE); - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { - if ( !g_hash_table_contains (alive_connections, connection) - && nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection))) { - if (!dead_connections) - dead_connections = g_ptr_array_new (); - g_ptr_array_add (dead_connections, connection); + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *const entry = &entries[i]; + NMSKeyfileStorageType storage_type; + gs_free_error GError *local = NULL; + const char *f_filename; + const char *f_dirname; + const char *full_filename; + gs_free char *full_filename_keep = NULL; + gboolean is_nmmeta_file; + NMSettingsPluginConnectionLoadEntry *dupl_content_entry; + gboolean failed_due_to_invalid_filename; + gs_unref_object NMSKeyfileStorage *storage = NULL; + + if (entry->handled) + continue; + + if (!_path_detect_storage_type (entry->filename, + (const char *const*) priv->dirname_libs, + priv->dirname_etc, + priv->dirname_run, + &storage_type, + &f_dirname, + &f_filename, + &is_nmmeta_file, + &failed_due_to_invalid_filename)) { + if (failed_due_to_invalid_filename) { + entry->handled = TRUE; + nm_utils_error_set (&entry->error, NM_UTILS_ERROR_UNKNOWN, "filename is not valid for a keyfile"); + } + continue; } - } - g_hash_table_destroy (alive_connections); - if (dead_connections) { - for (i = 0; i < dead_connections->len; i++) - remove_connection (self, dead_connections->pdata[i]); - g_ptr_array_free (dead_connections, TRUE); + full_filename_keep = g_build_filename (f_dirname, f_filename, NULL); + + if ((dupl_content_entry = g_hash_table_lookup (dupl_filenames, full_filename_keep))) { + /* we already visited this file. */ + entry->handled = dupl_content_entry->handled; + if (dupl_content_entry->error) { + g_set_error_literal (&entry->error, + dupl_content_entry->error->domain, + dupl_content_entry->error->code, + dupl_content_entry->error->message); + } + continue; + } + + entry->handled = TRUE; + + full_filename = full_filename_keep; + if (!g_hash_table_insert (dupl_filenames, g_steal_pointer (&full_filename_keep), entry)) + nm_assert_not_reached (); + + storage = _load_file (self, + f_dirname, + f_filename, + storage_type, + &local); + if (!storage) { + if (nm_utils_file_stat (full_filename, NULL) == -ENOENT) { + NMSKeyfileStorage *storage2; + + /* the file does not exist. We take that as indication to unload the file + * that was previously loaded... */ + storage2 = nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename); + if (storage2) + g_hash_table_add (storages_replaced, g_object_ref (storage2)); + continue; + } + g_propagate_error (&entry->error, g_steal_pointer (&local)); + continue; + } + + g_hash_table_add (loaded_uuids, (char *) nms_keyfile_storage_get_uuid (storage)); + + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage)); } -} -/*****************************************************************************/ + /* now we visit all UUIDs that are about to change... */ + g_hash_table_iter_init (&h_iter, loaded_uuids); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &loaded_uuid, NULL)) { + NMSKeyfileStorage *storage; + NMSettUtilStorageByUuidHead *sbuh; -static GSList * -get_connections (NMSettingsPlugin *config) -{ - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config); + sbuh = nm_sett_util_storages_lookup_by_uuid (&priv->storages, loaded_uuid); + if (!sbuh) + continue; + + c_list_for_each_entry (storage, &sbuh->_storage_by_uuid_lst_head, parent._storage_by_uuid_lst) { + const char *full_filename = nms_keyfile_storage_get_filename (storage); + gs_unref_object NMSKeyfileStorage *storage_new = NULL; + gs_free_error GError *local = NULL; + + if (g_hash_table_contains (dupl_filenames, full_filename)) { + /* already re-loaded. */ + continue; + } + + /* @storage has a UUID that was just loaded from disk, but we have an entry in cache. + * Reload that file too despite not being told to do so. The reason is to get + * the latest file timestamp so that we get the priorities right. */ + + storage_new = _load_file_from_path (self, + full_filename, + storage->storage_type, + &local); + if ( storage_new + && !nm_streq (loaded_uuid, nms_keyfile_storage_get_uuid (storage_new))) { + /* the file now references a different UUID. We are not told to reload + * that file, so this means the existing storage (with the previous + * filename and UUID tuple) is no longer valid. */ + g_clear_object (&storage_new); + } - if (!priv->initialized) { - setup_monitoring (config); - read_connections (config); - priv->initialized = TRUE; + g_hash_table_add (storages_replaced, g_object_ref (storage)); + if (storage_new) + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage_new)); + } } - return _nm_utils_hash_values_to_slist (priv->connections); + + nm_clear_pointer (&loaded_uuids, g_hash_table_destroy); + nm_clear_pointer (&dupl_filenames, g_hash_table_destroy); + + _storages_consolidate (self, + &storages_new, + FALSE, + storages_replaced, + callback, + user_data); } -static gboolean -load_connection (NMSettingsPlugin *config, - const char *filename) +gboolean +nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, + NMConnection *connection, + gboolean in_memory, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) { - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN ((NMSKeyfilePlugin *) config); - NMSKeyfileConnection *connection; - gboolean require_extension; - - if (nm_utils_file_is_in_path (filename, nms_keyfile_utils_get_path ())) - require_extension = FALSE; - else if (nm_utils_file_is_in_path (filename, NM_KEYFILE_PATH_NAME_RUN)) - require_extension = TRUE; - else - return FALSE; + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + gs_unref_object NMConnection *reread = NULL; + gs_free char *full_filename = NULL; + NMSKeyfileStorageType storage_type; + gs_unref_object NMSKeyfileStorage *storage = NULL; + GError *local = NULL; + const char *uuid; + gboolean reread_same; + struct timespec mtime; + char strbuf[100]; + + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (out_storage && !*out_storage); + nm_assert (out_connection && !*out_connection); + + nm_assert ( in_memory + || ( !is_nm_generated + && !is_volatile + && !shadowed_storage + && !shadowed_owned)); - if (nm_keyfile_utils_ignore_filename (filename, require_extension)) + uuid = nm_connection_get_uuid (connection); + + /* Note that even if the caller requests persistent storage, we may switch to in-memory, if + * no /etc directory is configured. */ + storage_type = !in_memory && priv->dirname_etc + ? NMS_KEYFILE_STORAGE_TYPE_ETC + : NMS_KEYFILE_STORAGE_TYPE_RUN; + + if (!nms_keyfile_writer_connection (connection, + is_nm_generated, + is_volatile, + shadowed_storage, + shadowed_owned, + storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC + ? priv->dirname_etc + : priv->dirname_run, + _get_plugin_dir (priv), + NULL, + FALSE, + FALSE, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, NULL), + &full_filename, + &reread, + &reread_same, + &local)) { + _LOGT ("commit: %s (%s) failed to add: %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + local->message); + g_propagate_error (error, local); return FALSE; + } - connection = update_connection (self, NULL, filename, find_by_path (self, filename), TRUE, NULL, NULL); + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - return (connection != NULL); -} + nm_assert (_nm_connection_verify (reread, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (nm_streq0 (nm_connection_get_uuid (connection), nm_connection_get_uuid (reread))); -static void -reload_connections (NMSettingsPlugin *config) -{ - read_connections (config); + nm_assert (full_filename && full_filename[0] == '/'); + nm_assert (!nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); + + _LOGT ("commit: %s (%s) added as \"%s\"%s%s%s%s", + uuid, + nm_connection_get_id (connection), + full_filename, + _extra_flags_to_string (strbuf, sizeof (strbuf), is_nm_generated, is_volatile), + NM_PRINT_FMT_QUOTED (shadowed_storage, " (shadows \"", shadowed_storage, shadowed_owned ? "\", owned)" : "\")", "")); + + storage = nms_keyfile_storage_new_connection (self, + g_steal_pointer (&reread), + full_filename, + storage_type, + is_nm_generated ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + is_volatile ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + shadowed_storage, + shadowed_owned ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + nm_sett_util_stat_mtime (full_filename, FALSE, &mtime)); + + nm_sett_util_storages_add_take (&priv->storages, g_object_ref (storage)); + + *out_connection = nms_keyfile_storage_steal_connection (storage); + *out_storage = NM_SETTINGS_STORAGE (g_steal_pointer (&storage)); + + return TRUE; } -static NMSettingsConnection * -add_connection (NMSettingsPlugin *config, +static gboolean +add_connection (NMSettingsPlugin *plugin, NMConnection *connection, - gboolean save_to_disk, + NMSettingsStorage **out_storage, + NMConnection **out_connection, GError **error) { - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config); - gs_free char *path = NULL; + return nms_keyfile_plugin_add_connection (NMS_KEYFILE_PLUGIN (plugin), + connection, + FALSE, + FALSE, + FALSE, + NULL, + FALSE, + out_storage, + out_connection, + error); +} + +gboolean +nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self, + NMSettingsStorage *storage_x, + NMConnection *connection, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + gboolean force_rename, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) +{ + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + NMSKeyfileStorage *storage = NMS_KEYFILE_STORAGE (storage_x); + gs_unref_object NMConnection *connection_clone = NULL; gs_unref_object NMConnection *reread = NULL; + gs_free char *full_filename = NULL; + gs_free_error GError *local = NULL; + struct timespec mtime; + const char *previous_filename; + gboolean reread_same; + const char *uuid; + char strbuf[100]; + + _nm_assert_storage (self, storage, TRUE); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (nm_streq (nms_keyfile_storage_get_uuid (storage), nm_connection_get_uuid (connection))); + nm_assert (!error || !*error); + nm_assert (NM_IN_SET (storage->storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)); + nm_assert (!storage->is_meta_data); + nm_assert ( storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN + || ( !is_nm_generated + && !is_volatile + && !shadowed_storage + && !shadowed_owned)); + nm_assert (!shadowed_owned || shadowed_storage); + nm_assert ( priv->dirname_etc + || storage->storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC); + + previous_filename = nms_keyfile_storage_get_filename (storage); + uuid = nms_keyfile_storage_get_uuid (storage); if (!nms_keyfile_writer_connection (connection, - save_to_disk, - NULL, + is_nm_generated, + is_volatile, + shadowed_storage, + shadowed_owned, + storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC + ? priv->dirname_etc + : priv->dirname_run, + _get_plugin_dir (priv), + previous_filename, + FALSE, FALSE, - &path, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, previous_filename), + &full_filename, &reread, - NULL, - error)) - return NULL; + &reread_same, + &local)) { + _LOGW ("commit: failure to write %s (%s) to \"%s\": %s", + uuid, + nm_connection_get_id (connection_clone), + previous_filename, + local->message); + g_propagate_error (error, g_steal_pointer (&local)); + return FALSE; + } + + nm_assert ( full_filename + && nm_streq (full_filename, previous_filename)); + + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error)); + nm_assert (_nm_connection_verify (reread, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (nm_streq (nm_connection_get_uuid (reread), uuid)); + + _LOGT ("commit: \"%s\": profile %s (%s) written%s%s%s%s", + full_filename, + uuid, + nm_connection_get_id (connection), + _extra_flags_to_string (strbuf, sizeof (strbuf), is_nm_generated, is_volatile), + NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", "")); + + storage->u.conn_data.is_nm_generated = is_nm_generated; + storage->u.conn_data.is_volatile = is_volatile; + storage->u.conn_data.stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime); + storage->u.conn_data.shadowed_owned = shadowed_owned; + + *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage)); + *out_connection = g_steal_pointer (&reread); + return TRUE; +} + +static gboolean +update_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) +{ + return nms_keyfile_plugin_update_connection (NMS_KEYFILE_PLUGIN (plugin), + storage, + connection, + FALSE, + FALSE, + NULL, + FALSE, + FALSE, + out_storage, + out_connection, + error); +} + +static gboolean +delete_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage_x, + GError **error) +{ + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + gs_unref_object NMSKeyfileStorage *storage = g_object_ref (NMS_KEYFILE_STORAGE (storage_x)); + const char *remove_from_disk_errmsg = NULL; + const char *operation_message; + const char *previous_filename; + const char *uuid; + gboolean success = TRUE; + + _nm_assert_storage (self, storage, TRUE); + nm_assert (!error || !*error); + + previous_filename = nms_keyfile_storage_get_filename (storage); + uuid = nms_keyfile_storage_get_uuid (storage); + + if (!NM_IN_SET (storage->storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)) { + nm_utils_error_set (error, + NM_UTILS_ERROR_UNKNOWN, + "profile in read-only storage cannot be deleted"); + success = FALSE; + operation_message = "dropped readonly file from memory"; + } else if (unlink (previous_filename) != 0) { + int errsv; + + errsv = errno; + if (errsv != ENOENT) { + remove_from_disk_errmsg = nm_strerror_native (errsv); + operation_message = "failed to delete from disk"; + success = FALSE; + nm_utils_error_set_errno (error, + errsv, + "failure to delete \"%s\": %s", + previous_filename); + } else + operation_message = "does not exist on disk"; + } else + operation_message = "deleted from disk"; + + _LOGT ("commit: deleted \"%s\", %s %s (%s%s%s%s)", + previous_filename, + storage->is_meta_data ? "meta-data" : "profile", + uuid, + operation_message, + NM_PRINT_FMT_QUOTED (remove_from_disk_errmsg, ": ", remove_from_disk_errmsg, "", "")); + + if (success) { + nm_sett_util_storages_steal (&priv->storages, storage); + nms_keyfile_storage_destroy (storage); + } + + return success; +} + +/** + * nms_keyfile_plugin_set_nmmeta_tombstone: + * @self: the #NMSKeyfilePlugin instance + * @simulate: if %TRUE, don't do anything on the filename but just pretend + * that the loaded UUID file gets tracked/untracked. In this mode, the function + * cannot fail (except on hard-failure, see below). + * The idea is that you first try without simulate to write to disk. + * If that fails, you might still want to forcefully pretend (in-memory + * only) that this uuid is marked as tombstone (or not), as desired. + * So you repeate the call with @simulate %TRUE. + * @uuid: the UUID for which to write/delete the nmmeta file + * @in_memory: the storage type, either /etc or /run. Note that if @self + * has no /etc directory configured, this results in a hard failure. + * @set: if %TRUE, write the symlink to point to /dev/null. If %FALSE, + * delete the nmmeta file (if it exists). + * @shadowed_storage: a tombstone can also shadow an existing storage. + * In combination with @set and @in_memory, this is allowed to store + * the shadowed storage filename. + * @out_storage: (transfer full) (allow-none): the storage element that changes, or + * NULL if nothing changed. Note that the file on disk is already as + * we want to write it, then this still counts as a change. No change only + * means if we try to delete a storage (@set %FALSE) that did not + * exist previously. + * @out_hard_failure: (allow-none): on failure, indicate that this is a hard failure. + * + * The function writes or deletes nmmeta files to/from filesystem. In this case, + * the nmmeta files can only be symlinks to /dev/null (to indicate tombstones). + * + * A hard failure can only happen if @self has no /etc directory configured + * and @in_memory is FALSE. In such case even @simulate call fails (which + * otherwise would always succeed). + * Also, if you get a hard-failure (with @simulate %FALSE) there is no point + * in retrying with @simulate %TRUE (contrary to all other cases!). + * + * Returns: %TRUE on success. + */ +gboolean +nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, + gboolean simulate, + const char *uuid, + gboolean in_memory, + gboolean set, + const char *shadowed_storage, + NMSettingsStorage **out_storage, + gboolean *out_hard_failure) +{ + NMSKeyfilePluginPrivate *priv; + gboolean hard_failure = FALSE; + NMSKeyfileStorage *storage; + gs_unref_object NMSKeyfileStorage *storage_result = NULL; + gboolean nmmeta_success = FALSE; + gs_free char *nmmeta_filename = NULL; + NMSKeyfileStorageType storage_type; + const char *loaded_path; + const char *dirname; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (self)); + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (!out_storage || !*out_storage); + nm_assert (!shadowed_storage || (set && in_memory)); + + priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + + loaded_path = set + ? NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL + : NULL; + + if (in_memory) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN; + dirname = priv->dirname_run; + } else { + if (!priv->dirname_etc) { + _LOGT ("commit: cannot %s%s nmmeta file for %s as there is no /etc directory", + simulate ? "simulate " : "", + loaded_path ? "write" : "delete", + uuid); + hard_failure = TRUE; + goto out; + } + storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC; + dirname = priv->dirname_etc; + } + + if (simulate) { + nmmeta_success = TRUE; + nmmeta_filename = nms_keyfile_nmmeta_filename (dirname, uuid, FALSE); + } else { + nmmeta_success = nms_keyfile_nmmeta_write (dirname, + uuid, + loaded_path, + FALSE, + shadowed_storage, + &nmmeta_filename); + } + + _LOGT ("commit: %s nmmeta file \"%s\"%s%s%s%s%s%s %s", + loaded_path ? "writing" : "deleting", + nmmeta_filename, + NM_PRINT_FMT_QUOTED (loaded_path, " (pointing to \"", loaded_path, "\")", ""), + NM_PRINT_FMT_QUOTED (shadowed_storage, " (shadows \"", shadowed_storage, "\")", ""), + simulate + ? "simulated" + : ( nmmeta_success + ? "succeeded" + : "failed")); + + if (!nmmeta_success) + goto out; + + storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, nmmeta_filename); + + nm_assert ( !storage + || ( storage->is_meta_data + && storage->storage_type == storage_type + && nm_streq (nms_keyfile_storage_get_uuid (storage), uuid))); + + if (loaded_path) { + + if (!storage) { + storage = nms_keyfile_storage_new_tombstone (self, + uuid, + nmmeta_filename, + storage_type, + shadowed_storage); + nm_sett_util_storages_add_take (&priv->storages, storage); + } else { + g_free (storage->u.meta_data.shadowed_storage); + storage->u.meta_data.shadowed_storage = g_strdup (shadowed_storage); + } + + storage_result = g_object_ref (storage); + } else { + if (storage) + storage_result = nm_sett_util_storages_steal (&priv->storages, storage); + } + +out: + nm_assert (!nmmeta_success || !hard_failure); + nm_assert (nmmeta_success || !storage_result); + + NM_SET_OUT (out_hard_failure, hard_failure); + NM_SET_OUT (out_storage, (NMSettingsStorage *) g_steal_pointer (&storage_result)); + return nmmeta_success; +} + +/*****************************************************************************/ + +static void +config_changed_cb (NMConfig *config, + NMConfigData *config_data, + NMConfigChangeFlags changes, + NMConfigData *old_data, + NMSKeyfilePlugin *self) +{ + gs_free char *old_value = NULL; + gs_free char *new_value = NULL; + + old_value = nm_config_data_get_value (old_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC); + new_value = nm_config_data_get_value (config_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC); + + if (!nm_streq0 (old_value, new_value)) + _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); } static GSList * get_unmanaged_specs (NMSettingsPlugin *config) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (config); gs_free char *value = NULL; value = nm_config_data_get_value (nm_config_get_data (priv->config), @@ -584,13 +1225,48 @@ nms_keyfile_plugin_init (NMSKeyfilePlugin *plugin) NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin); priv->config = g_object_ref (nm_config_get ()); - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); + + priv->storages = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT (priv->storages, nms_keyfile_storage_destroy); + + /* dirname_libs are a set of read-only directories with lower priority than /etc or /run. + * There is nothing complicated about having multiple of such directories, so dirname_libs + * is a list (which currently only has at most one directory). */ + priv->dirname_libs[0] = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_LIB), FALSE); + priv->dirname_libs[1] = NULL; + priv->dirname_run = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_RUN), FALSE); + priv->dirname_etc = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_KEYFILE, + NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, + NM_CONFIG_GET_VALUE_STRIP); + if (priv->dirname_etc && priv->dirname_etc[0] == '\0') { + /* special case: configure an empty keyfile path so that NM has no writable keyfile + * directory. In this case, NM will only honor dirname_libs and dirname_run, meaning + * it cannot persist profile to non-volatile memory. */ + nm_clear_g_free (&priv->dirname_etc); + } else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') { + /* either invalid path or unspecified. Use the default. */ + g_free (priv->dirname_etc); + priv->dirname_etc = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_ETC_DEFAULT), FALSE); + } else + nm_sd_utils_path_simplify (priv->dirname_etc, FALSE); + + /* no duplicates */ + if (NM_IN_STRSET (priv->dirname_libs[0], priv->dirname_etc, + priv->dirname_run)) + nm_clear_g_free (&priv->dirname_libs[0]); + if (NM_IN_STRSET (priv->dirname_etc, priv->dirname_run)) + nm_clear_g_free (&priv->dirname_etc); + + nm_assert (!priv->dirname_libs[0] || priv->dirname_libs[0][0] == '/'); + nm_assert (!priv->dirname_etc || priv->dirname_etc[0] == '/'); + nm_assert ( priv->dirname_run && priv->dirname_run[0] == '/'); } static void constructed (GObject *object) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) object); + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (object); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); G_OBJECT_CLASS (nms_keyfile_plugin_parent_class)->constructed (object); @@ -599,6 +1275,17 @@ constructed (GObject *object) NM_CONFIG_KEYFILE_KEY_KEYFILE_HOSTNAME, NM_CONFIG_GET_VALUE_RAW)) _LOGW ("'hostname' option is deprecated and has no effect"); + + if (nm_config_data_has_value (nm_config_get_data_orig (priv->config), + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES, + NM_CONFIG_GET_VALUE_RAW)) + _LOGW ("'monitor-connection-files' option is deprecated and has no effect"); + + g_signal_connect (G_OBJECT (priv->config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (config_changed_cb), + self); } NMSKeyfilePlugin * @@ -610,24 +1297,19 @@ nms_keyfile_plugin_new (void) static void dispose (GObject *object) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) object); + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (object); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - if (priv->monitor) { - nm_clear_g_signal_handler (priv->monitor, &priv->monitor_id); + if (priv->config) + g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, object); - g_file_monitor_cancel (priv->monitor); - g_clear_object (&priv->monitor); - } + nm_sett_util_storages_clear (&priv->storages); - if (priv->connections) { - g_hash_table_destroy (priv->connections); - priv->connections = NULL; - } + nm_clear_g_free (&priv->dirname_libs[0]); + nm_clear_g_free (&priv->dirname_etc); + nm_clear_g_free (&priv->dirname_run); - if (priv->config) { - g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, object); - g_clear_object (&priv->config); - } + g_clear_object (&priv->config); G_OBJECT_CLASS (nms_keyfile_plugin_parent_class)->dispose (object); } @@ -641,9 +1323,11 @@ nms_keyfile_plugin_class_init (NMSKeyfilePluginClass *klass) object_class->constructed = constructed; object_class->dispose = dispose; - plugin_class->get_connections = get_connections; - plugin_class->load_connection = load_connection; + plugin_class->plugin_name = "keyfile"; + plugin_class->get_unmanaged_specs = get_unmanaged_specs; plugin_class->reload_connections = reload_connections; + plugin_class->load_connections = load_connections; plugin_class->add_connection = add_connection; - plugin_class->get_unmanaged_specs = get_unmanaged_specs; + plugin_class->update_connection = update_connection; + plugin_class->delete_connection = delete_connection; } diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.h b/src/settings/plugins/keyfile/nms-keyfile-plugin.h index 41f47aac..48440964 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.h +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -22,6 +21,11 @@ #ifndef __NMS_KEYFILE_PLUGIN_H__ #define __NMS_KEYFILE_PLUGIN_H__ +#include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-storage.h" + +#include "nms-keyfile-utils.h" + #define NMS_TYPE_KEYFILE_PLUGIN (nms_keyfile_plugin_get_type ()) #define NMS_KEYFILE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePlugin)) #define NMS_KEYFILE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePluginClass)) @@ -36,4 +40,36 @@ GType nms_keyfile_plugin_get_type (void); NMSKeyfilePlugin *nms_keyfile_plugin_new (void); +gboolean nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, + NMConnection *connection, + gboolean in_memory, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error); + +gboolean nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self, + NMSettingsStorage *storage, + NMConnection *connection, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + gboolean force_rename, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error); + +gboolean nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, + gboolean simulate, + const char *uuid, + gboolean in_memory, + gboolean set, + const char *shadowed_storage, + NMSettingsStorage **out_storage, + gboolean *out_hard_failure); + #endif /* __NMS_KEYFILE_PLUGIN_H__ */ diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c index 5778f13c..8d1f5599 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-reader.c +++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -162,6 +161,11 @@ nms_keyfile_reader_from_keyfile (GKeyFile *key_file, NMConnection * nms_keyfile_reader_from_file (const char *full_filename, const char *profile_dir, + struct stat *out_stat, + NMTernary *out_is_nm_generated, + NMTernary *out_is_volatile, + char **out_shadowed_storage, + NMTernary *out_shadowed_owned, GError **error) { gs_unref_keyfile GKeyFile *key_file = NULL; @@ -171,9 +175,12 @@ nms_keyfile_reader_from_file (const char *full_filename, nm_assert (full_filename && full_filename[0] == '/'); nm_assert (!profile_dir || profile_dir[0] == '/'); + NM_SET_OUT (out_is_nm_generated, NM_TERNARY_DEFAULT); + NM_SET_OUT (out_is_volatile, NM_TERNARY_DEFAULT); + if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_KEYFILE, full_filename, - NULL, + out_stat, error)) return NULL; @@ -195,6 +202,26 @@ nms_keyfile_reader_from_file (const char *full_filename, connection = NULL; } + NM_SET_OUT (out_is_nm_generated, nm_key_file_get_boolean (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_NM_GENERATED, + NM_TERNARY_DEFAULT)); + + NM_SET_OUT (out_is_volatile, nm_key_file_get_boolean (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_VOLATILE, + NM_TERNARY_DEFAULT)); + + NM_SET_OUT (out_shadowed_storage, g_key_file_get_string (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE, + NULL)); + + NM_SET_OUT (out_shadowed_owned, nm_key_file_get_boolean (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED, + NM_TERNARY_DEFAULT)); + return connection; } diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.h b/src/settings/plugins/keyfile/nms-keyfile-reader.h index c0fb06d1..f20e6d93 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-reader.h +++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -31,8 +30,15 @@ NMConnection *nms_keyfile_reader_from_keyfile (GKeyFile *key_file, gboolean verbose, GError **error); +struct stat; + NMConnection *nms_keyfile_reader_from_file (const char *full_filename, const char *profile_dir, + struct stat *out_stat, + NMTernary *out_is_nm_generated, + NMTernary *out_is_volatile, + char **out_shadowed_storage, + NMTernary *out_shadowed_owned, GError **error); #endif /* __NMS_KEYFILE_READER_H__ */ diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.c b/src/settings/plugins/keyfile/nms-keyfile-storage.c new file mode 100644 index 00000000..d68d60c8 --- /dev/null +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.c @@ -0,0 +1,277 @@ +/* NetworkManager system settings service - keyfile plugin + * + * 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 (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nms-keyfile-storage.h" + +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "nms-keyfile-plugin.h" + +/*****************************************************************************/ + +struct _NMSKeyfileStorageClass { + NMSettingsStorageClass parent; +}; + +G_DEFINE_TYPE (NMSKeyfileStorage, nms_keyfile_storage, NM_TYPE_SETTINGS_STORAGE) + +/*****************************************************************************/ + +void +nms_keyfile_storage_copy_content (NMSKeyfileStorage *dst, + const NMSKeyfileStorage *src) +{ + nm_assert (src != dst); + nm_assert (nm_streq (nms_keyfile_storage_get_uuid (dst), nms_keyfile_storage_get_uuid (src))); + nm_assert ( nms_keyfile_storage_get_filename (dst) + && nm_streq (nms_keyfile_storage_get_filename (dst), nms_keyfile_storage_get_filename (src))); + nm_assert (dst->storage_type == src->storage_type); + nm_assert (dst->is_meta_data == src->is_meta_data); + + if (dst->is_meta_data) { + gs_free char *shadowed_storage_to_free = NULL; + + shadowed_storage_to_free = g_steal_pointer (&dst->u.meta_data.shadowed_storage); + dst->u.meta_data = src->u.meta_data; + dst->u.meta_data.shadowed_storage = g_strdup (dst->u.meta_data.shadowed_storage); + } else { + gs_unref_object NMConnection *connection_to_free = NULL; + gs_free char *shadowed_storage_to_free = NULL; + + connection_to_free = g_steal_pointer (&dst->u.conn_data.connection); + shadowed_storage_to_free = g_steal_pointer (&dst->u.conn_data.shadowed_storage); + dst->u.conn_data = src->u.conn_data; + nm_g_object_ref (dst->u.conn_data.connection); + dst->u.conn_data.shadowed_storage = g_strdup (dst->u.conn_data.shadowed_storage); + } +} + +NMConnection * +nms_keyfile_storage_steal_connection (NMSKeyfileStorage *self) +{ + nm_assert (NMS_IS_KEYFILE_STORAGE (self)); + nm_assert ( self->is_meta_data + || NM_IS_CONNECTION (self->u.conn_data.connection)); + + return self->is_meta_data + ? NULL + : g_steal_pointer (&self->u.conn_data.connection); +} + +/*****************************************************************************/ + +static int +cmp_fcn (const NMSKeyfileStorage *a, + const NMSKeyfileStorage *b) +{ + nm_assert (NMS_IS_KEYFILE_STORAGE (a)); + nm_assert (NMS_IS_KEYFILE_STORAGE (b)); + nm_assert (a != b); + + /* sort by storage-type, which also has a numeric value according to their + * (inverse) priority. */ + NM_CMP_FIELD_UNSAFE (b, a, storage_type); + + /* meta-data is more important. */ + NM_CMP_FIELD_UNSAFE (a, b, is_meta_data); + + if (a->is_meta_data) { + nm_assert (nm_streq (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b))); + NM_CMP_FIELD_UNSAFE (a, b, u.meta_data.is_tombstone); + } else { + /* newer files are more important. */ + NM_CMP_FIELD (a, b, u.conn_data.stat_mtime.tv_sec); + NM_CMP_FIELD (a, b, u.conn_data.stat_mtime.tv_nsec); + + NM_CMP_DIRECT_STRCMP (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b)); + } + + return 0; +} + +/*****************************************************************************/ + +static void +nms_keyfile_storage_init (NMSKeyfileStorage *self) +{ +} + +static NMSKeyfileStorage * +_storage_new (NMSKeyfilePlugin *plugin, + const char *uuid, + const char *filename, + gboolean is_meta_data, + NMSKeyfileStorageType storage_type) + +{ + NMSKeyfileStorage *self; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin)); + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + + self = g_object_new (NMS_TYPE_KEYFILE_STORAGE, + NM_SETTINGS_STORAGE_PLUGIN, plugin, + NM_SETTINGS_STORAGE_UUID, uuid, + NM_SETTINGS_STORAGE_FILENAME, filename, + NULL); + + *((bool *) &self->is_meta_data) = is_meta_data; + *((NMSKeyfileStorageType *) &self->storage_type) = storage_type; + + return self; +} + +NMSKeyfileStorage * +nms_keyfile_storage_new_tombstone (NMSKeyfilePlugin *plugin, + const char *uuid, + const char *filename, + NMSKeyfileStorageType storage_type, + const char *shadowed_storage) +{ + NMSKeyfileStorage *self; + + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + nm_assert (nms_keyfile_nmmeta_check_filename (filename, NULL)); + nm_assert (NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)); + + self = _storage_new (plugin, uuid, filename, TRUE, storage_type); + self->u.meta_data.is_tombstone = TRUE; + if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) + self->u.meta_data.shadowed_storage = g_strdup (shadowed_storage); + return self; +} + +NMSKeyfileStorage * +nms_keyfile_storage_new_connection (NMSKeyfilePlugin *plugin, + NMConnection *connection_take /* pass reference */, + const char *filename, + NMSKeyfileStorageType storage_type, + NMTernary is_nm_generated_opt, + NMTernary is_volatile_opt, + const char *shadowed_storage, + NMTernary shadowed_owned_opt, + const struct timespec *stat_mtime) +{ + NMSKeyfileStorage *self; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin)); + nm_assert (NM_IS_CONNECTION (connection_take)); + nm_assert (_nm_connection_verify (connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (filename && filename[0] == '/'); + nm_assert ( storage_type >= NMS_KEYFILE_STORAGE_TYPE_RUN + && storage_type <= _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST); + nmtst_connection_assert_unchanging (connection_take); + + self = _storage_new (plugin, nm_connection_get_uuid (connection_take), filename, FALSE, storage_type); + + self->u.conn_data.connection = connection_take; /* take reference. */ + + self->u.conn_data.shadowed_storage = g_strdup (shadowed_storage); + + if (stat_mtime) + self->u.conn_data.stat_mtime = *stat_mtime; + + if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) { + self->u.conn_data.is_nm_generated = (is_nm_generated_opt == NM_TERNARY_TRUE); + self->u.conn_data.is_volatile = (is_volatile_opt == NM_TERNARY_TRUE); + self->u.conn_data.shadowed_owned = shadowed_storage + && (shadowed_owned_opt == NM_TERNARY_TRUE); + } + + return self; +} + +static void +_storage_clear (NMSKeyfileStorage *self) +{ + c_list_unlink (&self->parent._storage_lst); + c_list_unlink (&self->parent._storage_by_uuid_lst); + if (self->is_meta_data) + nm_clear_g_free (&self->u.meta_data.shadowed_storage); + else { + g_clear_object (&self->u.conn_data.connection); + nm_clear_g_free (&self->u.conn_data.shadowed_storage); + self->u.conn_data.shadowed_owned = FALSE; + } +} + +static void +dispose (GObject *object) +{ + NMSKeyfileStorage *self = NMS_KEYFILE_STORAGE (object); + + _storage_clear (self); + + G_OBJECT_CLASS (nms_keyfile_storage_parent_class)->dispose (object); +} + +void +nms_keyfile_storage_destroy (NMSKeyfileStorage *self) +{ + _storage_clear (self); + g_object_unref (self); +} + +static void +nms_keyfile_storage_class_init (NMSKeyfileStorageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingsStorageClass *storage_class = NM_SETTINGS_STORAGE_CLASS (klass); + + object_class->dispose = dispose; + + storage_class->cmp_fcn = (int (*) (NMSettingsStorage *, NMSettingsStorage *)) cmp_fcn; +} + +/*****************************************************************************/ + +#include "settings/nm-settings-connection.h" + +void +nm_settings_storage_load_sett_flags (NMSettingsStorage *self, + NMSettingsConnectionIntFlags *sett_flags, + NMSettingsConnectionIntFlags *sett_mask) +{ + NMSKeyfileStorage *s; + + *sett_flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; + *sett_mask = NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE; + + if (!NMS_IS_KEYFILE_STORAGE (self)) + return; + + s = NMS_KEYFILE_STORAGE (self); + + if (s->is_meta_data) + return; + if (s->storage_type != NMS_KEYFILE_STORAGE_TYPE_RUN) + return; + + if (s->u.conn_data.is_nm_generated) + *sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED; + + if (s->u.conn_data.is_volatile) + *sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE; +} diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.h b/src/settings/plugins/keyfile/nms-keyfile-storage.h new file mode 100644 index 00000000..2252b47b --- /dev/null +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.h @@ -0,0 +1,264 @@ +/* NetworkManager system settings service - keyfile plugin + * + * 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 (C) 2018 Red Hat, Inc. + */ + +#ifndef __NMS_KEYFILE_STORAGE_H__ +#define __NMS_KEYFILE_STORAGE_H__ + +#include "c-list/src/c-list.h" +#include "settings/nm-settings-storage.h" +#include "nms-keyfile-utils.h" + +/*****************************************************************************/ + +#define NMS_TYPE_KEYFILE_STORAGE (nms_keyfile_storage_get_type ()) +#define NMS_KEYFILE_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorage)) +#define NMS_KEYFILE_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass)) +#define NMS_IS_KEYFILE_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_KEYFILE_STORAGE)) +#define NMS_IS_KEYFILE_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_KEYFILE_STORAGE)) +#define NMS_KEYFILE_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass)) + +typedef struct { + /* whether this is a tombstone to hide a UUID (via symlink to /dev/null). */ + char *shadowed_storage; + bool is_tombstone:1; +} NMSettingsMetaData; + +typedef struct { + NMSettingsStorage parent; + + /* The connection. Note that there are tombstones (loaded-uuid files to /dev/null) + * that don't have a connection. + * + * Also, we don't actually remember the loaded connection after returning it + * to NMSettings. So, also for regular storages (non-tombstones) this field + * is often cleared. */ + union { + struct { + NMConnection *connection; + + /* when we move a profile from permanent storage to unsaved (/run), then + * we may leave the profile on disk (depending on options for Update2()). + * + * Later, when we save the profile again to disk, we want to re-use that filename. + * Likewise, we delete the (now in-memory) profile, we may want to also delete + * the original filename. + * + * This is the original filename, and we store it inside [.nmmeta] in the + * keyfile in /run. Note that we don't store this in the .nmmeta file, because + * the information is tied to the particular keyfile in /run, not to all UUIDs + * in general. */ + char *shadowed_storage; + + /* the timestamp (stat's mtime) of the keyfile. For meta-data this + * is irrelevant. The purpose is that if the same storage type (directory) has + * multiple files with the same UUID, then the newer file gets preferred. */ + struct timespec stat_mtime; + + /* these flags are only relevant for storages with %NMS_KEYFILE_STORAGE_TYPE_RUN + * (and non-metadata). This is to persist and reload these settings flags to + * /run. + * + * Note that these flags are not stored in as meta-data. The reason is that meta-data + * is per UUID. But these flags are only relevant for a particular keyfile on disk. + * That is, it must be tied to the actual keyfile, and not to the UUID. */ + bool is_nm_generated:1; + bool is_volatile:1; + + /* if shadowed_storage is set, then this flag indicates whether the file + * is owned. The difference comes into play when deleting the in-memory, + * shadowing profile: a owned profile will also be deleted. */ + bool shadowed_owned:1; + + } conn_data; + + /* the content from the .nmmeta file. Note that the nmmeta file has the UUID + * in the filename, that means there can be only two variants of this file: + * in /etc and in /run. As such, this is really meta-data about the entire profile + * (the UUID), and not about the individual keyfile. */ + NMSettingsMetaData meta_data; + + } u; + + /* The storage type. This is directly related to the filename. Since + * the filename cannot change, this value is unchanging. */ + const NMSKeyfileStorageType storage_type; + + /* whether union "u" has meta_data or conn_data. Since the type of the storage + * depends on the (immutable) filename, this is also const. */ + const bool is_meta_data; + + /* this flag is only used during reload to mark and prune old entries. */ + bool is_dirty:1; + +} NMSKeyfileStorage; + +typedef struct _NMSKeyfileStorageClass NMSKeyfileStorageClass; + +GType nms_keyfile_storage_get_type (void); + +struct _NMSKeyfilePlugin; + +NMSKeyfileStorage *nms_keyfile_storage_new_tombstone (struct _NMSKeyfilePlugin *self, + const char *uuid, + const char *filename, + NMSKeyfileStorageType storage_type, + const char *shadowed_storage); + +NMSKeyfileStorage *nms_keyfile_storage_new_connection (struct _NMSKeyfilePlugin *self, + NMConnection *connection_take /* pass reference */, + const char *filename, + NMSKeyfileStorageType storage_type, + NMTernary is_nm_generated_opt, + NMTernary is_volatile_opt, + const char *shadowed_storage, + NMTernary shadowed_owned_opt, + const struct timespec *stat_mtime); + +void nms_keyfile_storage_destroy (NMSKeyfileStorage *storage); + +/*****************************************************************************/ + +void nms_keyfile_storage_copy_content (NMSKeyfileStorage *dst, + const NMSKeyfileStorage *src); + +NMConnection *nms_keyfile_storage_steal_connection (NMSKeyfileStorage *storage); + +/*****************************************************************************/ + +static inline const char * +nms_keyfile_storage_get_uuid (const NMSKeyfileStorage *self) +{ + return nm_settings_storage_get_uuid ((const NMSettingsStorage *) self); +} + +static inline const char * +nms_keyfile_storage_get_filename (const NMSKeyfileStorage *self) +{ + return nm_settings_storage_get_filename ((const NMSettingsStorage *) self); +} + +/*****************************************************************************/ + +static inline gboolean +nm_settings_storage_is_keyfile_run (const NMSettingsStorage *self) +{ + return NMS_IS_KEYFILE_STORAGE (self) + && (((NMSKeyfileStorage *) self)->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN); +} + +static inline gboolean +nm_settings_storage_is_keyfile_lib (const NMSettingsStorage *self) +{ + return NMS_IS_KEYFILE_STORAGE (self) + && (((NMSKeyfileStorage *) self)->storage_type >= NMS_KEYFILE_STORAGE_TYPE_LIB_BASE); +} + +static inline const NMSettingsMetaData * +nm_settings_storage_is_meta_data (const NMSettingsStorage *storage) +{ + const NMSKeyfileStorage *self; + + if (!NMS_IS_KEYFILE_STORAGE (storage)) + return NULL; + + self = (NMSKeyfileStorage *) storage; + + if (!self->is_meta_data) + return NULL; + + return &self->u.meta_data; +} + +static inline const NMSettingsMetaData * +nm_settings_storage_is_meta_data_alive (const NMSettingsStorage *storage) +{ + const NMSettingsMetaData *meta_data; + + meta_data = nm_settings_storage_is_meta_data (storage); + + if (!meta_data) + return NULL; + + /* Regular (all other) storages are alive as long as they report a NMConnection, and + * they will be dropped, once they have no more connection. + * + * Meta-data storages are special: they never report a NMConnection. + * So, a meta-data storage is alive as long as it is tracked by the + * settings plugin. + * + * This function is used to ckeck for that. */ + + if (c_list_is_empty (&storage->_storage_lst)) + return NULL; + + return meta_data; +} + +static inline const char * +nm_settings_storage_get_shadowed_storage (const NMSettingsStorage *storage, + gboolean *out_shadowed_owned) +{ + if (NMS_IS_KEYFILE_STORAGE (storage)) { + const NMSKeyfileStorage *self = (const NMSKeyfileStorage *) storage; + + if (self->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) { + if (!self->is_meta_data) { + if (self->u.conn_data.shadowed_storage) { + NM_SET_OUT (out_shadowed_owned, self->u.conn_data.shadowed_owned); + return self->u.conn_data.shadowed_storage; + } + } else { + NM_SET_OUT (out_shadowed_owned, FALSE); + return self->u.meta_data.shadowed_storage; + } + } + } + + NM_SET_OUT (out_shadowed_owned, FALSE); + return NULL; +} + +static inline const char * +nm_settings_storage_get_filename_for_shadowed_storage (const NMSettingsStorage *storage) +{ + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), NULL); + + if (!storage->_filename) + return NULL; + + if (NMS_IS_KEYFILE_STORAGE (storage)) { + const NMSKeyfileStorage *self = (const NMSKeyfileStorage *) storage; + + if ( self->is_meta_data + || self->storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC) + return NULL; + } + + return storage->_filename; +} + +/*****************************************************************************/ + +enum _NMSettingsConnectionIntFlags; + +void nm_settings_storage_load_sett_flags (NMSettingsStorage *self, + enum _NMSettingsConnectionIntFlags *sett_flags, + enum _NMSettingsConnectionIntFlags *sett_mask); + +#endif /* __NMS_KEYFILE_STORAGE_H__ */ diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c index 3c4b0288..ea03e1b6 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.c +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -15,7 +14,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2010 Red Hat, Inc. + * (C) Copyright 2010 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -25,6 +24,7 @@ #include #include +#include "nm-glib-aux/nm-io-utils.h" #include "nm-keyfile-internal.h" #include "nm-utils.h" #include "nm-setting-wired.h" @@ -34,108 +34,155 @@ /*****************************************************************************/ +#define NMMETA_KF_GROUP_NAME_NMMETA "nmmeta" +#define NMMETA_KF_KEY_NAME_NMMETA_UUID "uuid" +#define NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH "loaded-path" +#define NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE "shadowed-storage" + +/*****************************************************************************/ + +const char * +nms_keyfile_nmmeta_check_filename (const char *filename, + guint *out_uuid_len) +{ + const char *uuid; + const char *s; + gsize len; + + s = strrchr (filename, '/'); + if (s) + filename = &s[1]; + + len = strlen (filename); + if ( len <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA) + || memcmp (&filename[len - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA)], + NM_KEYFILE_PATH_SUFFIX_NMMETA, + NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA)) != 0) { + /* the filename does not have the right suffix. */ + return NULL; + } + + len -= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA); + + if (!NM_IN_SET (len, 36, 40)) { + /* the remaining part of the filename has not the right length to + * contain a UUID (according to nm_utils_is_uuid()). */ + return NULL; + } + + uuid = nm_strndup_a (100, filename, len, NULL); + if (!nm_utils_is_uuid (uuid)) + return NULL; + + NM_SET_OUT (out_uuid_len, len); + return filename; +} + char * -nms_keyfile_loaded_uuid_filename (const char *dirname, - const char *uuid, - gboolean temporary) +nms_keyfile_nmmeta_filename (const char *dirname, + const char *uuid, + gboolean temporary) { char filename[250]; + char *s; nm_assert (dirname && dirname[0] == '/'); - nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/')); + nm_assert ( nm_utils_is_uuid (uuid) + && !strchr (uuid, '/')); if (g_snprintf (filename, sizeof (filename), - "%s%s%s%s", - NM_KEYFILE_PATH_PREFIX_NMLOADED, + "%s%s%s", uuid, - NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, + NM_KEYFILE_PATH_SUFFIX_NMMETA, temporary ? "~" : "") >= sizeof (filename)) { - /* valid uuids are limited in length. The buffer should always be large - * enough. */ + /* valid uuids are limited in length (nm_utils_is_uuid). The buffer should always + * be large enough. */ nm_assert_not_reached (); - return NULL; } - return g_build_filename (dirname, filename, NULL); + s = g_build_filename (dirname, filename, NULL); + + nm_assert (nm_keyfile_utils_ignore_filename (s, FALSE)); + + return s; } gboolean -nms_keyfile_loaded_uuid_read (const char *dirname, - const char *filename, - char **out_full_filename, - char **out_uuid, - char **out_loaded_path) +nms_keyfile_nmmeta_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage, + struct stat *out_st) { const char *uuid; - const char *tmp; - gsize len; + guint uuid_len; gs_free char *full_filename = NULL; - gs_free char *ln = NULL; + gs_free char *loaded_path = NULL; + gs_free char *shadowed_storage = NULL; + struct stat st_stack; + struct stat *st = out_st ?: &st_stack; nm_assert (dirname && dirname[0] == '/'); nm_assert (filename && filename[0] && !strchr (filename, '/')); - if (filename[0] != '.') { - /* the hidden-uuid filename must start with '.'. That is, - * so that it does not conflict with regular keyfiles according - * to nm_keyfile_utils_ignore_filename(). */ + uuid = nms_keyfile_nmmeta_check_filename (filename, &uuid_len); + if (!uuid) return FALSE; - } - len = strlen (filename); - if ( len <= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED) - || memcmp (filename, NM_KEYFILE_PATH_PREFIX_NMLOADED, NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)) != 0) { - /* the filename does not have the right prefix. */ + full_filename = g_build_filename (dirname, filename, NULL); + + if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMMETA, + full_filename, + st, + NULL)) return FALSE; - } - tmp = &filename[NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)]; - len -= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED); + if (S_ISREG (st->st_mode)) { + gs_unref_keyfile GKeyFile *kf = NULL; + gs_free char *v_uuid = NULL; - if ( len <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION) - || memcmp (&tmp[len - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)], - NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) != 0) { - /* the file does not have the right suffix. */ - return FALSE; - } - len -= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); + kf = g_key_file_new (); - if (!NM_IN_SET (len, 36, 40)) { - /* the remaining part of the filename has not the right length to - * contain a UUID (according to nm_utils_is_uuid()). */ - return FALSE; - } + if (!g_key_file_load_from_file (kf, full_filename, G_KEY_FILE_NONE, NULL)) + return FALSE; - uuid = nm_strndup_a (100, tmp, len, NULL); - if (!nm_utils_is_uuid (uuid)) - return FALSE; + v_uuid = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_UUID, NULL); + if (!nm_streq0 (v_uuid, uuid)) + return FALSE; - full_filename = g_build_filename (dirname, filename, NULL); + loaded_path = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH, NULL); + shadowed_storage = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE, NULL); - if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMLOADED, - full_filename, - NULL, - NULL)) - return FALSE; + if ( !loaded_path + && !shadowed_storage) { + /* if there is no useful information in the file, it is the same as if + * the file is not present. Signal failure. */ + return FALSE; + } - ln = nm_utils_read_link_absolute (full_filename, NULL); - if (!ln) - return FALSE; + } else { + loaded_path = nm_utils_read_link_absolute (full_filename, NULL); + if (!loaded_path) + return FALSE; + } - NM_SET_OUT (out_uuid, g_strdup (uuid)); + NM_SET_OUT (out_uuid, g_strndup (uuid, uuid_len)); NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); - NM_SET_OUT (out_loaded_path, g_steal_pointer (&ln)); + NM_SET_OUT (out_loaded_path, g_steal_pointer (&loaded_path)); + NM_SET_OUT (out_shadowed_storage, g_steal_pointer (&shadowed_storage)); return TRUE; } gboolean -nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, - char **out_dirname, - char **out_filename, - char **out_uuid, - char **out_loaded_path) +nms_keyfile_nmmeta_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage) { gs_free char *dirname = NULL; gs_free char *filename = NULL; @@ -145,11 +192,13 @@ nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, filename = g_path_get_basename (full_filename); dirname = g_path_get_dirname (full_filename); - if (!nms_keyfile_loaded_uuid_read (dirname, - filename, - NULL, - out_uuid, - out_loaded_path)) + if (!nms_keyfile_nmmeta_read (dirname, + filename, + NULL, + out_uuid, + out_loaded_path, + out_shadowed_storage, + NULL)) return FALSE; NM_SET_OUT (out_dirname, g_steal_pointer (&dirname)); @@ -158,20 +207,23 @@ nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, } gboolean -nms_keyfile_loaded_uuid_write (const char *dirname, - const char *uuid, - const char *loaded_path, - gboolean allow_relative, - char **out_full_filename) +nms_keyfile_nmmeta_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean loaded_path_allow_relative, + const char *shadowed_storage, + char **out_full_filename) { gs_free char *full_filename_tmp = NULL; gs_free char *full_filename = NULL; nm_assert (dirname && dirname[0] == '/'); - nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/')); + nm_assert ( nm_utils_is_uuid (uuid) + && !strchr (uuid, '/')); nm_assert (!loaded_path || loaded_path[0] == '/'); + nm_assert (!shadowed_storage || loaded_path); - full_filename_tmp = nms_keyfile_loaded_uuid_filename (dirname, uuid, TRUE); + full_filename_tmp = nms_keyfile_nmmeta_filename (dirname, uuid, TRUE); nm_assert (g_str_has_suffix (full_filename_tmp, "~")); nm_assert (nm_utils_file_is_in_path (full_filename_tmp, dirname)); @@ -188,7 +240,7 @@ nms_keyfile_loaded_uuid_write (const char *dirname, return success; } - if (allow_relative) { + if (loaded_path_allow_relative) { const char *f; f = nm_utils_file_is_in_path (loaded_path, dirname); @@ -199,18 +251,40 @@ nms_keyfile_loaded_uuid_write (const char *dirname, } } - if (symlink (loaded_path, full_filename_tmp) != 0) { - full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; - NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); - return FALSE; - } + full_filename = g_strndup (full_filename_tmp, strlen (full_filename_tmp) - 1); - full_filename = g_strdup (full_filename_tmp); - full_filename[strlen (full_filename) - 1] = '\0'; - if (rename (full_filename_tmp, full_filename) != 0) { - (void) unlink (full_filename_tmp); - NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); - return FALSE; + if (shadowed_storage) { + gs_unref_keyfile GKeyFile *kf = NULL; + gs_free char *contents = NULL; + gsize length; + + kf = g_key_file_new (); + + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_UUID, uuid); + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH, loaded_path); + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE, shadowed_storage); + + contents = g_key_file_to_data (kf, &length, NULL); + + if (!nm_utils_file_set_contents (full_filename, contents, length, 0600, NULL)) { + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return FALSE; + } + } else { + /* we only have the "loaded_path" to store. That is commonly used for the tombstones to + * link to /dev/null. A symlink is sufficient to store that ammount of information. + * No need to bother with a keyfile. */ + if (symlink (loaded_path, full_filename_tmp) != 0) { + full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return FALSE; + } + + if (rename (full_filename_tmp, full_filename) != 0) { + (void) unlink (full_filename_tmp); + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); + return FALSE; + } } NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); @@ -232,10 +306,11 @@ nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, "file is not a regular file"); return FALSE; } - } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { - if (!S_ISLNK (st->st_mode)) { + } else if (filetype == NMS_KEYFILE_FILETYPE_NMMETA) { + if ( !S_ISLNK (st->st_mode) + && !S_ISREG (st->st_mode)) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "file is not a slink"); + "file is neither a symlink nor a regular file"); return FALSE; } } else @@ -249,7 +324,7 @@ nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, return FALSE; } - if ( filetype == NMS_KEYFILE_FILETYPE_KEYFILE + if ( S_ISREG (st->st_mode) && (st->st_mode & 0077)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "File permissions (%03o) are insecure", @@ -279,7 +354,7 @@ nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype, "cannot access file: %s", nm_strerror_native (errsv)); return FALSE; } - } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { + } else if (filetype == NMS_KEYFILE_FILETYPE_NMMETA) { if (lstat (filename, &st) != 0) { errsv = errno; g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -295,22 +370,3 @@ nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype, NM_SET_OUT (out_st, st); return TRUE; } - -/*****************************************************************************/ - -const char * -nms_keyfile_utils_get_path (void) -{ - static char *path = NULL; - - if (G_UNLIKELY (!path)) { - path = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, - NM_CONFIG_KEYFILE_GROUP_KEYFILE, - NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, - NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); - if (!path) - path = g_strdup (""NM_KEYFILE_PATH_NAME_ETC_DEFAULT""); - } - return path; -} - diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h index bc601dad..723c4436 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.h +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -15,7 +14,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2010-2016 Red Hat, Inc. + * (C) Copyright 2010 - 2018 Red Hat, Inc. */ #ifndef __NMS_KEYFILE_UTILS_H__ @@ -23,42 +22,56 @@ #include "NetworkManagerUtils.h" -#define NMS_KEYFILE_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory") -#define NMS_KEYFILE_CONNECTION_LOG_FMT "%s (%s,\"%s\")" -#define NMS_KEYFILE_CONNECTION_LOG_ARG(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)) -#define NMS_KEYFILE_CONNECTION_LOG_FMTD "%s (%s,\"%s\",%p)" -#define NMS_KEYFILE_CONNECTION_LOG_ARGD(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)), (con) - typedef enum { NMS_KEYFILE_FILETYPE_KEYFILE, - NMS_KEYFILE_FILETYPE_NMLOADED, + NMS_KEYFILE_FILETYPE_NMMETA, } NMSKeyfileFiletype; -const char *nms_keyfile_utils_get_path (void); +typedef enum { + NMS_KEYFILE_STORAGE_TYPE_RUN = 1, /* read-write, runtime only, e.g. /run */ + NMS_KEYFILE_STORAGE_TYPE_ETC = 2, /* read-write, persistent, e.g. /etc */ + NMS_KEYFILE_STORAGE_TYPE_LIB_BASE = 3, /* read-only, e.g. /usr/lib */ + + _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST = 1000, +} NMSKeyfileStorageType; + +static inline NMSKeyfileStorageType +NMS_KEYFILE_STORAGE_TYPE_LIB (guint run_idx) +{ + nm_assert (run_idx <= (_NMS_KEYFILE_STORAGE_TYPE_LIB_LAST - NMS_KEYFILE_STORAGE_TYPE_LIB_BASE)); + return NMS_KEYFILE_STORAGE_TYPE_LIB_BASE + run_idx; +} /*****************************************************************************/ -char *nms_keyfile_loaded_uuid_filename (const char *dirname, - const char *uuid, - gboolean temporary); - -gboolean nms_keyfile_loaded_uuid_read (const char *dirname, - const char *filename, - char **out_full_filename, - char **out_uuid, - char **out_loaded_path); - -gboolean nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, - char **out_dirname, - char **out_filename, - char **out_uuid, - char **out_loaded_path); - -gboolean nms_keyfile_loaded_uuid_write (const char *dirname, - const char *uuid, - const char *loaded_path, - gboolean allow_relative, - char **out_full_filename); +const char *nms_keyfile_nmmeta_check_filename (const char *filename, + guint *out_uuid_len); + +char *nms_keyfile_nmmeta_filename (const char *dirname, + const char *uuid, + gboolean temporary); + +gboolean nms_keyfile_nmmeta_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage, + struct stat *out_st); + +gboolean nms_keyfile_nmmeta_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage); + +gboolean nms_keyfile_nmmeta_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean loaded_path_allow_relative, + const char *shadowed_storage, + char **out_full_filename); /*****************************************************************************/ diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c index 2e06aeb9..5fbbb7a1 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.c +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -169,6 +168,10 @@ _handler_write (NMConnection *connection, static gboolean _internal_write_connection (NMConnection *connection, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, const char *keyfile_dir, const char *profile_dir, gboolean with_extension, @@ -177,6 +180,8 @@ _internal_write_connection (NMConnection *connection, const char *existing_path, gboolean existing_path_read_only, gboolean force_rename, + NMSKeyfileWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_path, NMConnection **out_reread, gboolean *out_reread_same, @@ -191,33 +196,57 @@ _internal_write_connection (NMConnection *connection, GError *local_err = NULL; int errsv; gboolean rename; + int i_path; g_return_val_if_fail (!out_path || !*out_path, FALSE); g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + + nm_assert (!shadowed_owned || shadowed_storage); + rename = force_rename || existing_path_read_only || ( existing_path && !nm_utils_file_is_in_path (existing_path, keyfile_dir)); - switch (_nm_connection_verify (connection, error)) { - case NM_SETTING_VERIFY_NORMALIZABLE: - nm_assert_not_reached (); - /* fall-through */ - case NM_SETTING_VERIFY_SUCCESS: - break; - default: - g_return_val_if_reached (FALSE); - } - id = nm_connection_get_id (connection); - g_assert (id && *id); + nm_assert (id && *id); info.keyfile_dir = keyfile_dir; kf_file = nm_keyfile_write (connection, _handler_write, &info, error); if (!kf_file) return FALSE; + + if (is_nm_generated) { + g_key_file_set_boolean (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_NM_GENERATED, + TRUE); + } + + if (is_volatile) { + g_key_file_set_boolean (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_VOLATILE, + TRUE); + } + + if (shadowed_storage) { + g_key_file_set_string (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE, + shadowed_storage); + } + + if (shadowed_owned) { + g_key_file_set_boolean (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED, + TRUE); + } + kf_content_buf = g_key_file_to_data (kf_file, &kf_content_len, error); if (!kf_content_buf) return FALSE; @@ -225,64 +254,59 @@ _internal_write_connection (NMConnection *connection, if (!g_file_test (keyfile_dir, G_FILE_TEST_IS_DIR)) (void) g_mkdir_with_parents (keyfile_dir, 0755); - /* If we have existing file path, use it. Else generate one from - * connection's ID. - */ - if ( existing_path - && !rename) - path = g_strdup (existing_path); - else { - gs_free char *filename_escaped = NULL; + for (i_path = -2; i_path < 10000; i_path++) { + gs_free char *path_candidate = NULL; + gboolean is_existing_path; - filename_escaped = nm_keyfile_utils_create_filename (id, with_extension); - path = g_build_filename (keyfile_dir, filename_escaped, NULL); - } - - /* If a file with this path already exists (but isn't the existing path - * of the connection) then we need another name. Multiple connections - * can have the same ID (ie if two connections with the same ID are visible - * to different users) but of course can't have the same path. Yeah, - * there's a race here, but there's not a lot we can do about it, and - * we shouldn't get more than one connection with the same UUID either. - */ - if ( !nm_streq0 (path, existing_path) - && g_file_test (path, G_FILE_TEST_EXISTS)) { - guint i; - gboolean name_found = FALSE; + if (i_path == -2) { + if ( !existing_path + || rename) + continue; + path_candidate = g_strdup (existing_path); + } else if (i_path == -1) { + gs_free char *filename_escaped = NULL; - /* A keyfile with this connection's ID already exists. Pick another name. */ - for (i = 0; i < 100; i++) { + filename_escaped = nm_keyfile_utils_create_filename (id, with_extension); + path_candidate = g_build_filename (keyfile_dir, filename_escaped, NULL); + } else { gs_free char *filename_escaped = NULL; gs_free char *filename = NULL; - if (i == 0) + if (i_path == 0) filename = g_strdup_printf ("%s-%s", id, nm_connection_get_uuid (connection)); else - filename = g_strdup_printf ("%s-%s-%u", id, nm_connection_get_uuid (connection), i); + filename = g_strdup_printf ("%s-%s-%d", id, nm_connection_get_uuid (connection), i_path); filename_escaped = nm_keyfile_utils_create_filename (filename, with_extension); - g_free (path); - path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); - - if ( nm_streq0 (path, existing_path) - || !g_file_test (path, G_FILE_TEST_EXISTS)) { - name_found = TRUE; - break; - } + path_candidate = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); } - if (!name_found) { - if (existing_path_read_only || !existing_path) { - /* this really should not happen, we tried hard to find an unused name... bail out. */ - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "could not find suitable keyfile file name (%s already used)", path); - return FALSE; - } - /* Both our preferred path based on connection id and id-uuid are taken. - * Fallback to @existing_path */ - g_free (path); - path = g_strdup (existing_path); + + is_existing_path = existing_path + && nm_streq (existing_path, path_candidate); + + if ( is_existing_path + && rename) + continue; + + if ( allow_filename_cb + && !allow_filename_cb (path_candidate, allow_filename_user_data)) + continue; + + if (!is_existing_path) { + if (g_file_test (path_candidate, G_FILE_TEST_EXISTS)) + continue; } + + path = g_steal_pointer (&path_candidate); + break; + } + + if (!path) { + /* this really should not happen, we tried hard to find an unused name... bail out. */ + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "could not find suitable keyfile file name (%s already used)", path); + return FALSE; } nm_utils_file_set_contents (path, kf_content_buf, kf_content_len, 0600, &local_err); @@ -348,30 +372,37 @@ _internal_write_connection (NMConnection *connection, gboolean nms_keyfile_writer_connection (NMConnection *connection, - gboolean save_to_disk, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + const char *keyfile_dir, + const char *profile_dir, const char *existing_path, + gboolean existing_path_read_only, gboolean force_rename, + NMSKeyfileWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_path, NMConnection **out_reread, gboolean *out_reread_same, GError **error) { - const char *keyfile_dir; - - if (save_to_disk) - keyfile_dir = nms_keyfile_utils_get_path (); - else - keyfile_dir = NM_KEYFILE_PATH_NAME_RUN; - return _internal_write_connection (connection, + is_nm_generated, + is_volatile, + shadowed_storage, + shadowed_owned, keyfile_dir, - nms_keyfile_utils_get_path (), + profile_dir, TRUE, 0, 0, existing_path, - FALSE, + existing_path_read_only, force_rename, + allow_filename_cb, + allow_filename_user_data, out_path, out_reread, out_reread_same, @@ -389,6 +420,10 @@ nms_keyfile_writer_test_connection (NMConnection *connection, GError **error) { return _internal_write_connection (connection, + FALSE, + FALSE, + NULL, + FALSE, keyfile_dir, keyfile_dir, FALSE, @@ -397,6 +432,8 @@ nms_keyfile_writer_test_connection (NMConnection *connection, NULL, FALSE, FALSE, + NULL, + NULL, out_path, out_reread, out_reread_same, diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h index 030fb7cc..99e86025 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.h +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -24,10 +23,21 @@ #include "nm-connection.h" +typedef gboolean (*NMSKeyfileWriterAllowFilenameCb) (const char *check_filename, + gpointer allow_filename_user_data); + gboolean nms_keyfile_writer_connection (NMConnection *connection, - gboolean save_to_disk, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + const char *keyfile_dir, + const char *profile_dir, const char *existing_path, + gboolean existing_path_read_only, gboolean force_rename, + NMSKeyfileWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_path, NMConnection **out_reread, gboolean *out_reread_same, diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build index 752b6d7b..f1e96bdf 100644 --- a/src/settings/plugins/keyfile/tests/meson.build +++ b/src/settings/plugins/keyfile/tests/meson.build @@ -1,6 +1,4 @@ -test_unit = 'test-keyfile' - -test_keyfiles_dir = join_paths(meson.current_source_dir(), 'keyfiles') +test_unit = 'test-keyfile-settings' exe = executable( test_unit, @@ -9,7 +7,7 @@ exe = executable( ) test( - 'keyfile/' + test_unit, + test_unit, test_script, args: test_args + [exe.full_path()], timeout: default_test_timeout, diff --git a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c new file mode 100644 index 00000000..f96111a2 --- /dev/null +++ b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c @@ -0,0 +1,2706 @@ +/* NetworkManager system settings service - keyfile plugin + * + * 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 (C) 2008 - 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "nm-core-internal.h" + +#include "settings/plugins/keyfile/nms-keyfile-reader.h" +#include "settings/plugins/keyfile/nms-keyfile-writer.h" +#include "settings/plugins/keyfile/nms-keyfile-utils.h" + +#include "nm-test-utils-core.h" + +#define TEST_KEYFILES_DIR NM_BUILD_SRCDIR"/src/settings/plugins/keyfile/tests/keyfiles" +#define TEST_SCRATCH_DIR NM_BUILD_BUILDDIR"/src/settings/plugins/keyfile/tests/keyfiles" + +/*****************************************************************************/ + +static void +check_ip_address (NMSettingIPConfig *config, int idx, const char *address, int plen) +{ + NMIPAddress *ip4 = nm_setting_ip_config_get_address (config, idx); + + g_assert (ip4); + g_assert_cmpstr (nm_ip_address_get_address (ip4), ==, address); + g_assert_cmpint (nm_ip_address_get_prefix (ip4), ==, plen); +} + +static void +check_ip_route (NMSettingIPConfig *config, int idx, const char *destination, int plen, + const char *next_hop, gint64 metric) +{ + NMIPRoute *route = nm_setting_ip_config_get_route (config, idx); + + g_assert (route); + g_assert_cmpstr (nm_ip_route_get_dest (route), ==, destination); + g_assert_cmpint (nm_ip_route_get_prefix (route), ==, plen); + g_assert_cmpstr (nm_ip_route_get_next_hop (route), ==, next_hop); + g_assert_cmpint (nm_ip_route_get_metric (route), ==, metric); +} + +#define keyfile_read_connection_from_file(full_filename) \ +({ \ + gs_free_error GError *_error = NULL; \ + NMConnection *_connection; \ + \ + g_assert (full_filename && full_filename[0] == '/'); \ + \ + _connection = nms_keyfile_reader_from_file (full_filename, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + (nmtst_get_rand_uint32 () % 2) ? &_error : NULL); \ + nmtst_assert_success (_connection, _error); \ + nmtst_assert_connection_verifies_without_normalization (_connection); \ + \ + _connection; \ +}) + +static void +assert_reread (NMConnection *connection, gboolean normalize_connection, const char *testfile) +{ + gs_unref_object NMConnection *reread = NULL; + gs_unref_object NMConnection *connection_clone = NULL; + NMSettingConnection *s_con; + + g_assert (NM_IS_CONNECTION (connection)); + + reread = keyfile_read_connection_from_file (testfile); + + if ( !normalize_connection + && (s_con = nm_connection_get_setting_connection (connection)) + && !nm_setting_connection_get_master (s_con) + && !nm_connection_get_setting_proxy (connection)) { + connection_clone = nmtst_clone_connection (connection); + connection = connection_clone; + nm_connection_add_setting (connection, nm_setting_proxy_new ()); + } + + nmtst_assert_connection_equals (connection, normalize_connection, reread, FALSE); +} + +static void +assert_reread_and_unlink (NMConnection *connection, gboolean normalize_connection, const char *testfile) +{ + assert_reread (connection, normalize_connection, testfile); + unlink (testfile); +} + +static void +assert_reread_same (NMConnection *connection, + NMConnection *reread) +{ + nmtst_assert_connection_verifies_without_normalization (reread); + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + +static void +write_test_connection_reread (NMConnection *connection, + char **testfile, + NMConnection **out_reread, + gboolean *out_reread_same) +{ + uid_t owner_uid; + gid_t owner_grp; + gboolean success; + GError *error = NULL; + GError **p_error = (nmtst_get_rand_uint32 () % 2) ? &error : NULL; + gs_unref_object NMConnection *connection_normalized = NULL; + + g_assert (NM_IS_CONNECTION (connection)); + g_assert (testfile && !*testfile); + + owner_uid = geteuid (); + owner_grp = getegid (); + + connection_normalized = nmtst_connection_duplicate_and_normalize (connection); + + success = nms_keyfile_writer_test_connection (connection_normalized, + TEST_SCRATCH_DIR, + owner_uid, + owner_grp, + testfile, + out_reread, + out_reread_same, + p_error); + g_assert_no_error (error); + g_assert (success); + g_assert (*testfile && (*testfile)[0]); +} + +static void +write_test_connection (NMConnection *connection, char **testfile) +{ + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = FALSE; + + write_test_connection_reread (connection, testfile, &reread, &reread_same); + assert_reread_same (connection, reread); + g_assert (reread_same); +} + +static void +write_test_connection_and_reread (NMConnection *connection, gboolean normalize_connection) +{ + gs_free char *testfile = NULL; + + g_assert (NM_IS_CONNECTION (connection)); + + write_test_connection (connection, &testfile); + assert_reread_and_unlink (connection, normalize_connection, testfile); +} + +static GKeyFile * +keyfile_load_from_file (const char *testfile) +{ + GKeyFile *keyfile; + GError *error = NULL; + gboolean success; + + g_assert (testfile && *testfile); + + keyfile = g_key_file_new (); + success = g_key_file_load_from_file (keyfile, testfile, G_KEY_FILE_NONE, &error); + g_assert_no_error (error); + g_assert(success); + + return keyfile; +} + +static void +_setting_copy_property_gbytes (NMConnection *src, NMConnection *dst, const char *setting_name, const char *property_name) +{ + gs_unref_bytes GBytes *blob = NULL; + NMSetting *s_src; + NMSetting *s_dst; + + g_assert (NM_IS_CONNECTION (src)); + g_assert (NM_IS_CONNECTION (dst)); + g_assert (setting_name); + g_assert (property_name); + + s_src = nm_connection_get_setting_by_name (src, setting_name); + g_assert (NM_IS_SETTING (s_src)); + s_dst = nm_connection_get_setting_by_name (dst, setting_name); + g_assert (NM_IS_SETTING (s_dst)); + + g_object_get (s_src, property_name, &blob, NULL); + g_object_set (s_dst, property_name, blob, NULL); +} + +/*****************************************************************************/ + +static void +test_read_valid_wired_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + NMIPRoute *route; + const char *mac; + char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; + + NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses1*"); + NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses2*"); + NMTST_EXPECT_NM_WARN ("*missing prefix length*address4*"); + NMTST_EXPECT_NM_WARN ("*missing prefix length*address5*"); + NMTST_EXPECT_NM_WARN ("*ipv4.dns: ignoring invalid DNS server IPv4 address 'bogus'*"); + NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes2*"); + NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes3*"); + NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes5*"); + NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes8*"); + NMTST_EXPECT_NM_WARN ("*missing prefix length*address4*"); + NMTST_EXPECT_NM_INFO ("*ipv6.address*semicolon at the end*address5*"); + NMTST_EXPECT_NM_WARN ("*missing prefix length*address5*"); + NMTST_EXPECT_NM_INFO ("*ipv6.address*semicolon at the end*address7*"); + NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*"); + NMTST_EXPECT_NM_INFO ("*ipv6.route*semicolon at the end*route6*"); + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection"); + g_test_assert_expected_messages (); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wired Connection"); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "4e80a56d-c99f-4aad-a6dd-b449bc398c57"); + g_assert_cmpuint (nm_setting_connection_get_timestamp (s_con), ==, 6654332); + g_assert (nm_setting_connection_get_autoconnect (s_con)); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired); + + mac = nm_setting_wired_get_mac_address (s_wired); + g_assert (mac); + g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac_address, sizeof (expected_mac_address))); + g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 1400); + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + g_assert (s_ip4); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 2); + g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "4.2.2.1"); + g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 1), ==, "4.2.2.2"); + + /* IPv4 addresses */ + g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 10); + check_ip_address (s_ip4, 0, "2.3.4.5", 24); + check_ip_address (s_ip4, 1, "192.168.0.5", 24); + check_ip_address (s_ip4, 2, "1.2.3.4", 16); + check_ip_address (s_ip4, 3, "3.4.5.6", 16); + check_ip_address (s_ip4, 4, "4.5.6.7", 24); + check_ip_address (s_ip4, 5, "5.6.7.8", 24); + check_ip_address (s_ip4, 6, "1.2.3.30", 24); + check_ip_address (s_ip4, 7, "1.2.3.30", 25); + check_ip_address (s_ip4, 8, "1.2.3.31", 24); + check_ip_address (s_ip4, 9, "1.2.3.31", 25); + + /* IPv4 gateway */ + g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "2.3.4.6"); + + /* IPv4 routes */ + g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 13); + check_ip_route (s_ip4, 0, "5.6.7.8", 32, NULL, -1); + check_ip_route (s_ip4, 1, "1.2.3.0", 24, "2.3.4.8", 99); + check_ip_route (s_ip4, 2, "1.1.1.2", 12, NULL, -1); + check_ip_route (s_ip4, 3, "1.1.1.3", 13, NULL, -1); + check_ip_route (s_ip4, 4, "1.1.1.4", 14, "2.2.2.4", -1); + check_ip_route (s_ip4, 5, "1.1.1.5", 15, "2.2.2.5", -1); + check_ip_route (s_ip4, 6, "1.1.1.6", 16, "2.2.2.6", 0); + check_ip_route (s_ip4, 7, "1.1.1.7", 17, NULL, -1); + check_ip_route (s_ip4, 8, "1.1.1.8", 18, NULL, -1); + check_ip_route (s_ip4, 9, "1.1.1.9", 19, NULL, 0); + check_ip_route (s_ip4, 10, "1.1.1.10", 21, NULL, 0); + check_ip_route (s_ip4, 11, "1.1.1.10", 20, NULL, 0); + check_ip_route (s_ip4, 12, "1.1.1.11", 21, NULL, 21); + + /* Route attributes */ + route = nm_setting_ip_config_get_route (s_ip4, 12); + g_assert (route); + + nmtst_assert_route_attribute_uint32 (route, NM_IP_ROUTE_ATTRIBUTE_CWND, 10); + nmtst_assert_route_attribute_uint32 (route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1430); + nmtst_assert_route_attribute_boolean (route, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, TRUE); + nmtst_assert_route_attribute_string (route, NM_IP_ROUTE_ATTRIBUTE_SRC, "7.7.7.7"); + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + g_assert (s_ip6); + + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_MANUAL); + + g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 2); + g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip6, 0), ==, "1111:dddd::aaaa"); + g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip6, 1), ==, "1::cafe"); + g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip6), ==, 3); + g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 0), ==, "super-domain.com"); + g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 1), ==, "redhat.com"); + g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 2), ==, "gnu.org"); + + /* IPv6 addresses */ + g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip6), ==, 10); + check_ip_address (s_ip6, 0, "2:3:4:5:6:7:8:9", 64); + check_ip_address (s_ip6, 1, "abcd:1234:ffff::cdde", 64); + check_ip_address (s_ip6, 2, "1:2:3:4:5:6:7:8", 96); + check_ip_address (s_ip6, 3, "3:4:5:6:7:8:9:0", 128); + check_ip_address (s_ip6, 4, "3:4:5:6:7:8:9:14", 64); + check_ip_address (s_ip6, 5, "3:4:5:6:7:8:9:15", 64); + check_ip_address (s_ip6, 6, "3:4:5:6:7:8:9:16", 66); + check_ip_address (s_ip6, 7, "3:4:5:6:7:8:9:17", 67); + check_ip_address (s_ip6, 8, "3:4:5:6:7:8:9:18", 68); + check_ip_address (s_ip6, 9, "3:4:5:6:7:8:9:19", 69); + + /* IPv6 gateway */ + g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip6), ==, "2:3:4:5:1:2:3:4"); + + /* Routes */ + g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 7); + check_ip_route (s_ip6, 0, "d:e:f:0:1:2:3:4", 64, "f:e:d:c:1:2:3:4", -1); + check_ip_route (s_ip6, 1, "a:b:c:d::", 64, "f:e:d:c:1:2:3:4", 99); + check_ip_route (s_ip6, 2, "8:7:6:5:4:3:2:1", 128, NULL, -1); + check_ip_route (s_ip6, 3, "6:7:8:9:0:1:2:3", 126, NULL, 1); + check_ip_route (s_ip6, 4, "7:8:9:0:1:2:3:4", 125, NULL, 5); + check_ip_route (s_ip6, 5, "8:9:0:1:2:3:4:5", 124, NULL, 6); + check_ip_route (s_ip6, 6, "8:9:0:1:2:3:4:6", 123, NULL, -1); + + /* Route attributes */ + route = nm_setting_ip_config_get_route (s_ip6, 6); + g_assert (route); + nmtst_assert_route_attribute_string (route, NM_IP_ROUTE_ATTRIBUTE_FROM, "abce::/63"); +} + +static void +add_one_ip_address (NMSettingIPConfig *s_ip, + const char *addr, + guint32 prefix) +{ + NMIPAddress *ip_addr; + gs_free_error GError *error = NULL; + + ip_addr = nm_ip_address_new (NM_IS_SETTING_IP4_CONFIG (s_ip) ? AF_INET : AF_INET6, + addr, prefix, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip, ip_addr); + nm_ip_address_unref (ip_addr); +} + +static void +add_one_ip_route (NMSettingIPConfig *s_ip, + const char *dest, + const char *nh, + guint32 prefix, + gint64 metric) +{ + NMIPRoute *route; + gs_free_error GError *error = NULL; + + g_assert (prefix > 0); + route = nm_ip_route_new (NM_IS_SETTING_IP4_CONFIG (s_ip) ? AF_INET : AF_INET6, + dest, prefix, nh, metric, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_route (s_ip, route); + nm_ip_route_unref (route); +} + +static void +test_write_wired_connection (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + NMIPRoute *rt; + const char *mac = "99:88:77:66:55:44"; + const char *dns1 = "4.2.2.1"; + const char *dns2 = "4.2.2.2"; + const char *address1 = "192.168.0.5"; + const char *address2 = "1.2.3.4"; + const char *gw = "192.168.0.1"; + const char *route1 = "10.10.10.2"; + const char *route1_nh = "10.10.10.1"; + const char *route2 = "1.1.1.1"; + const char *route2_nh = "1.2.1.1"; + const char *route3 = "2.2.2.2"; + const char *route3_nh = NULL; + const char *route4 = "3.3.3.3"; + const char *route4_nh = NULL; + const char *dns6_1 = "1::cafe"; + const char *dns6_2 = "2::cafe"; + const char *address6_1 = "abcd::beef"; + const char *address6_2 = "dcba::beef"; + const char *route6_1 = "1:2:3:4:5:6:7:8"; + const char *route6_1_nh = "8:7:6:5:4:3:2:1"; + const char *route6_2 = "2001::1000"; + const char *route6_2_nh = "2001::1111"; + const char *route6_3 = "4:5:6:7:8:9:0:1"; + const char *route6_3_nh = "::"; + const char *route6_4 = "5:6:7:8:9:0:1:2"; + const char *route6_4_nh = "::"; + guint64 timestamp = 0x12345678L; + GError *error = NULL; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Work Wired", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_TIMESTAMP, timestamp, + NULL); + + /* Wired setting */ + + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + g_object_set (s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, mac, + NM_SETTING_WIRED_MTU, 900, + NULL); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, gw, + NULL); + + /* Addresses */ + add_one_ip_address (s_ip4, address1, 24); + add_one_ip_address (s_ip4, address2, 8); + + /* Routes */ + add_one_ip_route (s_ip4, route1, route1_nh, 24, 3); + add_one_ip_route (s_ip4, route2, route2_nh, 8, 1); + add_one_ip_route (s_ip4, route3, route3_nh, 7, -1); + + rt = nm_ip_route_new (AF_INET, route4, 6, route4_nh, 4, &error); + g_assert_no_error (error); + nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (10)); + nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1492)); + nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("1.2.3.4")); + g_assert (nm_setting_ip_config_add_route (s_ip4, rt)); + nm_ip_route_unref (rt); + + /* DNS servers */ + nm_setting_ip_config_add_dns (s_ip4, dns1); + nm_setting_ip_config_add_dns (s_ip4, dns2); + + /* IP6 setting */ + + s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NULL); + + /* Addresses */ + add_one_ip_address (s_ip6, address6_1, 64); + add_one_ip_address (s_ip6, address6_2, 56); + + /* Routes */ + add_one_ip_route (s_ip6, route6_1, route6_1_nh, 64, 3); + add_one_ip_route (s_ip6, route6_2, route6_2_nh, 56, 1); + add_one_ip_route (s_ip6, route6_3, route6_3_nh, 63, 5); + add_one_ip_route (s_ip6, route6_4, route6_4_nh, 62, -1); + + /* DNS servers */ + nm_setting_ip_config_add_dns (s_ip6, dns6_1); + nm_setting_ip_config_add_dns (s_ip6, dns6_2); + + /* DNS searches */ + nm_setting_ip_config_add_dns_search (s_ip6, "wallaceandgromit.com"); + + write_test_connection_and_reread (connection, FALSE); +} + +static void +test_read_ip6_wired_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_IP6"); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wired Connection IP6"); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "4e80a56d-c99f-4aad-a6dd-b449bc398c57"); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired); + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + g_assert (s_ip4); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_DISABLED); + g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 0); + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + g_assert (s_ip6); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_MANUAL); + g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip6), ==, 1); + check_ip_address (s_ip6, 0, "abcd:1234:ffff::cdde", 64); + g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip6), ==, "abcd:1234:ffff::cdd1"); +} + +static void +test_write_ip6_wired_connection (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + const char *dns = "1::cafe"; + const char *address = "abcd::beef"; + const char *gw = "dcba::beef"; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Work Wired IP6", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NULL); + + /* IP6 setting */ + + s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, gw, + NULL); + + /* Addresses */ + add_one_ip_address (s_ip6, address, 64); + + /* DNS servers */ + nm_setting_ip_config_add_dns (s_ip6, dns); + + /* DNS searches */ + nm_setting_ip_config_add_dns_search (s_ip6, "wallaceandgromit.com"); + + write_test_connection_and_reread (connection, FALSE); +} + +static void +test_read_wired_mac_case (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + const char *mac; + char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 }; + + NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses1*"); + NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses2*"); + NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*"); + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_MAC_Case"); + g_test_assert_expected_messages (); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wired Connection MAC Case"); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "4e80a56d-c99f-4aad-a6dd-b449bc398c57"); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired); + mac = nm_setting_wired_get_mac_address (s_wired); + g_assert (mac); + g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac_address, sizeof (expected_mac_address))); +} + +static void +test_read_mac_old_format (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWired *s_wired; + const char *mac; + char expected_mac[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 }; + char expected_cloned_mac[ETH_ALEN] = { 0x00, 0x16, 0xaa, 0xbb, 0xcc, 0xfe }; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_MAC_Old_Format"); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired); + + mac = nm_setting_wired_get_mac_address (s_wired); + g_assert (mac); + g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, ETH_ALEN)); + + mac = nm_setting_wired_get_cloned_mac_address (s_wired); + g_assert (mac); + g_assert (nm_utils_hwaddr_matches (mac, -1, expected_cloned_mac, ETH_ALEN)); +} + +static void +test_read_mac_ib_old_format (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingInfiniband *s_ib; + const char *mac; + guint8 expected_mac[INFINIBAND_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, + 0x90 }; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_MAC_IB_Old_Format"); + + s_ib = nm_connection_get_setting_infiniband (connection); + g_assert (s_ib); + + /* MAC address */ + mac = nm_setting_infiniband_get_mac_address (s_ib); + g_assert (mac); + g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, sizeof (expected_mac))); +} + +static void +test_read_valid_wireless_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + NMSettingIPConfig *s_ip4; + const char *bssid; + const guint8 expected_bssid[ETH_ALEN] = { 0x00, 0x1a, 0x33, 0x44, 0x99, 0x82 }; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wireless_Connection"); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wireless Connection"); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "2f962388-e5f3-45af-a62c-ac220b8f7baa"); + g_assert_cmpuint (nm_setting_connection_get_timestamp (s_con), ==, 1226604314); + g_assert (nm_setting_connection_get_autoconnect (s_con) == FALSE); + + s_wireless = nm_connection_get_setting_wireless (connection); + g_assert (s_wireless); + bssid = nm_setting_wireless_get_bssid (s_wireless); + g_assert (bssid); + g_assert (nm_utils_hwaddr_matches (bssid, -1, expected_bssid, sizeof (expected_bssid))); + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + g_assert (s_ip4); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); +} + +static void +test_write_wireless_connection (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + const char *bssid = "aa:b9:a1:74:55:44"; + GBytes *ssid; + unsigned char tmpssid[] = { 0x31, 0x33, 0x33, 0x37 }; + guint64 timestamp = 0x12344433L; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Work Wireless", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NM_SETTING_CONNECTION_TIMESTAMP, timestamp, + NULL); + + /* Wireless setting */ + + s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_wireless)); + + ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); + + g_object_set (s_wireless, + NM_SETTING_WIRELESS_BSSID, bssid, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRED_MTU, 1000, + NULL); + + g_bytes_unref (ssid); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + /* IP6 setting */ + + s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + + write_test_connection_and_reread (connection, FALSE); +} + +static void +test_read_string_ssid (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWireless *s_wireless; + GBytes *ssid; + const guint8 *ssid_data; + gsize ssid_len; + const char *expected_ssid = "blah blah ssid 1234"; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_String_SSID"); + + s_wireless = nm_connection_get_setting_wireless (connection); + g_assert (s_wireless); + + ssid = nm_setting_wireless_get_ssid (s_wireless); + g_assert (ssid); + + ssid_data = g_bytes_get_data (ssid, &ssid_len); + g_assert_cmpmem (ssid_data, ssid_len, expected_ssid, strlen (expected_ssid)); +} + +static void +test_write_string_ssid (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + NMSettingIPConfig *s_ip4; + char *tmp; + gs_free char *testfile = NULL; + GBytes *ssid; + unsigned char tmpssid[] = { 65, 49, 50, 51, 32, 46, 92, 46, 36, 37, 126, 93 }; + gs_unref_keyfile GKeyFile *keyfile = NULL; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "String SSID Test", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wireless setting */ + + s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_wireless)); + + ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); + g_object_set (s_wireless, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_bytes_unref (ssid); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + write_test_connection (connection, &testfile); + + /* Ensure the SSID was written out as a string */ + keyfile = keyfile_load_from_file (testfile); + tmp = g_key_file_get_string (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, NULL); + g_assert (tmp); + g_assert_cmpmem (tmp, strlen (tmp), tmpssid, sizeof (tmpssid)); + g_free (tmp); + + assert_reread_and_unlink (connection, TRUE, testfile); +} + +static void +test_read_intlist_ssid (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWireless *s_wifi; + GBytes *ssid; + const guint8 *ssid_data; + gsize ssid_len; + const char *expected_ssid = "blah1234"; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Intlist_SSID"); + + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + + ssid = nm_setting_wireless_get_ssid (s_wifi); + g_assert (ssid != NULL); + ssid_data = g_bytes_get_data (ssid, &ssid_len); + g_assert_cmpmem (ssid_data, ssid_len, expected_ssid, strlen (expected_ssid)); +} + +static void +test_write_intlist_ssid (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingIPConfig *s_ip4; + gs_free char *testfile = NULL; + GBytes *ssid; + unsigned char tmpssid[] = { 65, 49, 50, 51, 0, 50, 50 }; + gs_free_error GError *error = NULL; + gs_unref_keyfile GKeyFile *keyfile = NULL; + int *intlist; + gsize len = 0, i; + + connection = nm_simple_connection_new (); + g_assert (connection); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Intlist SSID Test", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wireless setting */ + s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); + g_assert (s_wifi); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); + g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_bytes_unref (ssid); + + /* IP4 setting */ + s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + g_assert (s_ip4); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + write_test_connection (connection, &testfile); + + /* Ensure the SSID was written out as an int list */ + keyfile = keyfile_load_from_file (testfile); + + intlist = g_key_file_get_integer_list (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, &len, &error); + g_assert_no_error (error); + g_assert (intlist); + g_assert_cmpint (len, ==, sizeof (tmpssid)); + + for (i = 0; i < len; i++) + g_assert_cmpint (intlist[i], ==, tmpssid[i]); + g_free (intlist); + + assert_reread_and_unlink (connection, TRUE, testfile); +} + +static void +test_read_intlike_ssid (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWireless *s_wifi; + GBytes *ssid; + const char *expected_ssid = "101"; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID"); + + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + + ssid = nm_setting_wireless_get_ssid (s_wifi); + g_assert (ssid); + g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid))); +} + +static void +test_read_intlike_ssid_2 (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWireless *s_wifi; + GBytes *ssid; + const char *expected_ssid = "11;12;13;"; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID_2"); + + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + + ssid = nm_setting_wireless_get_ssid (s_wifi); + g_assert (ssid); + g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid))); +} + +static void +test_write_intlike_ssid (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingIPConfig *s_ip4; + gs_free char *testfile = NULL; + GBytes *ssid; + unsigned char tmpssid[] = { 49, 48, 49 }; + gs_free_error GError *error = NULL; + gs_unref_keyfile GKeyFile *keyfile = NULL; + char *tmp; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Intlike SSID Test", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wireless setting */ + s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); + g_assert (s_wifi); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); + g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_bytes_unref (ssid); + + /* IP4 setting */ + s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + g_assert (s_ip4); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + write_test_connection (connection, &testfile); + + /* Ensure the SSID was written out as a plain "101" */ + keyfile = keyfile_load_from_file (testfile); + + tmp = g_key_file_get_string (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, &error); + g_assert_no_error (error); + g_assert (tmp); + g_assert_cmpstr (tmp, ==, "101"); + g_free (tmp); + + assert_reread_and_unlink (connection, TRUE, testfile); +} + +static void +test_write_intlike_ssid_2 (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingIPConfig *s_ip4; + gs_free char *testfile = NULL; + GBytes *ssid; + unsigned char tmpssid[] = { 49, 49, 59, 49, 50, 59, 49, 51, 59}; + gs_free_error GError *error = NULL; + gs_unref_keyfile GKeyFile *keyfile = NULL; + char *tmp; + + connection = nm_simple_connection_new (); + g_assert (connection); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Intlike SSID Test 2", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wireless setting */ + s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); + g_assert (s_wifi); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); + g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL); + g_bytes_unref (ssid); + + /* IP4 setting */ + s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + g_assert (s_ip4); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + write_test_connection (connection, &testfile); + + /* Ensure the SSID was written out as a plain "11;12;13;" */ + keyfile = keyfile_load_from_file (testfile); + + tmp = g_key_file_get_string (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, &error); + g_assert_no_error (error); + g_assert (tmp); + g_assert_cmpstr (tmp, ==, "11\\;12\\;13\\;"); + g_free (tmp); + + assert_reread_and_unlink (connection, TRUE, testfile); +} + +static void +test_read_bt_dun_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingBluetooth *s_bluetooth; + NMSettingSerial *s_serial; + NMSettingGsm *s_gsm; + const char *bdaddr; + const guint8 expected_bdaddr[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/ATT_Data_Connect_BT"); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "AT&T Data Connect BT"); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "089130ab-ce28-46e4-ad77-d44869b03d19"); + + s_bluetooth = nm_connection_get_setting_bluetooth (connection); + g_assert (s_bluetooth); + bdaddr = nm_setting_bluetooth_get_bdaddr (s_bluetooth); + g_assert (bdaddr); + g_assert (nm_utils_hwaddr_matches (bdaddr, -1, expected_bdaddr, sizeof (expected_bdaddr))); + g_assert_cmpstr (nm_setting_bluetooth_get_connection_type (s_bluetooth), ==, NM_SETTING_BLUETOOTH_TYPE_DUN); + + s_gsm = nm_connection_get_setting_gsm (connection); + g_assert (s_gsm); + g_assert_cmpstr (nm_setting_gsm_get_apn (s_gsm), ==, "ISP.CINGULAR"); + g_assert_cmpstr (nm_setting_gsm_get_username (s_gsm), ==, "ISP@CINGULARGPRS.COM"); + g_assert_cmpstr (nm_setting_gsm_get_password (s_gsm), ==, "CINGULAR1"); + + s_serial = nm_connection_get_setting_serial (connection); + g_assert (s_serial); + g_assert (nm_setting_serial_get_parity (s_serial) == NM_SETTING_SERIAL_PARITY_ODD); +} + +static void +test_write_bt_dun_connection (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingBluetooth *s_bt; + NMSettingIPConfig *s_ip4; + NMSettingGsm *s_gsm; + const char *bdaddr = "aa:b9:a1:74:55:44"; + guint64 timestamp = 0x12344433L; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "T-Mobile Funkadelic", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME, + NM_SETTING_CONNECTION_TIMESTAMP, timestamp, + NULL); + + /* Bluetooth setting */ + + s_bt = NM_SETTING_BLUETOOTH (nm_setting_bluetooth_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_bt)); + + g_object_set (s_bt, + NM_SETTING_BLUETOOTH_BDADDR, bdaddr, + NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_DUN, + NULL); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + /* GSM setting */ + s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + + g_object_set (s_gsm, + NM_SETTING_GSM_APN, "internet2.voicestream.com", + NM_SETTING_GSM_USERNAME, "george.clinton", + NM_SETTING_GSM_PASSWORD, "parliament", + NULL); + + write_test_connection_and_reread (connection, TRUE); +} + +static void +test_read_gsm_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingSerial *s_serial; + NMSettingGsm *s_gsm; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/ATT_Data_Connect_Plain"); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "AT&T Data Connect"); + g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_GSM_SETTING_NAME); + + /* Plain GSM, so no BT setting expected */ + g_assert (nm_connection_get_setting_bluetooth (connection) == NULL); + + s_gsm = nm_connection_get_setting_gsm (connection); + g_assert (s_gsm); + g_assert_cmpstr (nm_setting_gsm_get_apn (s_gsm), ==, "ISP.CINGULAR"); + g_assert_cmpstr (nm_setting_gsm_get_username (s_gsm), ==, "ISP@CINGULARGPRS.COM"); + g_assert_cmpstr (nm_setting_gsm_get_password (s_gsm), ==, "CINGULAR1"); + g_assert_cmpstr (nm_setting_gsm_get_network_id (s_gsm), ==, "24005"); + g_assert_cmpstr (nm_setting_gsm_get_pin (s_gsm), ==, "2345"); + g_assert_cmpstr (nm_setting_gsm_get_device_id (s_gsm), ==, "da812de91eec16620b06cd0ca5cbc7ea25245222"); + g_assert_cmpstr (nm_setting_gsm_get_sim_id (s_gsm), ==, "89148000000060671234"); + g_assert_cmpstr (nm_setting_gsm_get_sim_operator_id (s_gsm), ==, "310260"); + + s_serial = nm_connection_get_setting_serial (connection); + g_assert (s_serial); + g_assert_cmpint (nm_setting_serial_get_parity (s_serial), ==, NM_SETTING_SERIAL_PARITY_ODD); +} + +static void +test_write_gsm_connection (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingIPConfig *s_ip4; + NMSettingGsm *s_gsm; + guint64 timestamp = 0x12344433L; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "T-Mobile Funkadelic 2", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, + NM_SETTING_CONNECTION_TIMESTAMP, timestamp, + NULL); + + /* IP4 setting */ + + s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + /* GSM setting */ + s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + + g_object_set (s_gsm, + NM_SETTING_GSM_APN, "internet2.voicestream.com", + NM_SETTING_GSM_USERNAME, "george.clinton.again", + NM_SETTING_GSM_PASSWORD, "parliament2", + NM_SETTING_GSM_PIN, "123456", + NM_SETTING_GSM_NETWORK_ID, "254098", + NM_SETTING_GSM_HOME_ONLY, TRUE, + NM_SETTING_GSM_DEVICE_ID, "da812de91eec16620b06cd0ca5cbc7ea25245222", + NM_SETTING_GSM_SIM_ID, "89148000000060671234", + NM_SETTING_GSM_SIM_OPERATOR_ID, "310260", + NULL); + + write_test_connection_and_reread (connection, TRUE); +} + +static void +test_read_wired_8021x_tls_blob_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWired *s_wired; + NMSetting8021x *s_8021x; + const char *tmp; + GBytes *blob; + + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Blob"); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired != NULL); + + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x != NULL); + + g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); + tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); + g_assert (g_strcmp0 (tmp, "tls") == 0); + + tmp = nm_setting_802_1x_get_identity (s_8021x); + g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); + + tmp = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (g_strcmp0 (tmp, "12345testing") == 0); + + g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_BLOB); + + /* Make sure it's not a path, since it's a blob */ + NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)); + tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); + g_test_assert_expected_messages (); + g_assert (tmp == NULL); + + /* Validate the path */ + blob = nm_setting_802_1x_get_ca_cert_blob (s_8021x); + g_assert (blob != NULL); + g_assert_cmpint (g_bytes_get_size (blob), ==, 568); + + tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); + g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem"); + + tmp = nm_setting_802_1x_get_private_key_path (s_8021x); + g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem"); +} + +static void +test_read_wired_8021x_tls_bad_path_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWired *s_wired; + NMSetting8021x *s_8021x; + const char *tmp; + char *tmp2; + + NMTST_EXPECT_NM_WARN ("*does not exist*"); + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Path_Missing"); + g_test_assert_expected_messages (); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired != NULL); + + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x != NULL); + + g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); + tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); + g_assert (g_strcmp0 (tmp, "tls") == 0); + + tmp = nm_setting_802_1x_get_identity (s_8021x); + g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); + + tmp = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (g_strcmp0 (tmp, "12345testing") == 0); + + g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH); + + tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); + g_assert_cmpstr (tmp, ==, "/some/random/cert/path.pem"); + + tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-key-and-cert.pem"); + + tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); + g_assert_cmpstr (tmp, ==, tmp2); + + tmp = nm_setting_802_1x_get_private_key_path (s_8021x); + g_assert_cmpstr (tmp, ==, tmp2); + + g_free (tmp2); +} + +static void +test_read_wired_8021x_tls_old_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWired *s_wired; + NMSetting8021x *s_8021x; + const char *tmp; + + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*"); + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Old"); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired != NULL); + + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x != NULL); + + g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); + tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); + g_assert (g_strcmp0 (tmp, "tls") == 0); + + tmp = nm_setting_802_1x_get_identity (s_8021x); + g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); + + tmp = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (g_strcmp0 (tmp, "12345testing") == 0); + + tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); + g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem") == 0); + + tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); + g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0); + + tmp = nm_setting_802_1x_get_private_key_path (s_8021x); + g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0); +} + +static void +test_read_wired_8021x_tls_new_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWired *s_wired; + NMSetting8021x *s_8021x; + const char *tmp; + char *tmp2; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_New"); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired != NULL); + + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x != NULL); + + g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); + tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); + g_assert (g_strcmp0 (tmp, "tls") == 0); + + tmp = nm_setting_802_1x_get_identity (s_8021x); + g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); + + tmp = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (g_strcmp0 (tmp, "12345testing") == 0); + + tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-ca-cert.pem"); + tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); + g_assert_cmpstr (tmp, ==, tmp2); + g_free (tmp2); + + tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-key-and-cert.pem"); + + tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); + g_assert_cmpstr (tmp, ==, tmp2); + + tmp = nm_setting_802_1x_get_private_key_path (s_8021x); + g_assert_cmpstr (tmp, ==, tmp2); + + g_free (tmp2); +} + +#define TEST_WIRED_TLS_CA_CERT TEST_KEYFILES_DIR"/test-ca-cert.pem" +#define TEST_WIRED_TLS_CLIENT_CERT TEST_KEYFILES_DIR"/test-key-and-cert.pem" +#define TEST_WIRED_TLS_PRIVKEY TEST_KEYFILES_DIR"/test-key-and-cert.pem" + +static NMConnection * +create_wired_tls_connection (NMSetting8021xCKScheme scheme) +{ + NMTST_UUID_INIT (uuid); + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingIPConfig *s_ip4; + NMSetting *s_wired; + NMSetting8021x *s_8021x; + gboolean success; + gs_free_error GError *error = NULL; + + connection = nm_simple_connection_new (); + g_assert (connection != NULL); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Wired Really Secure TLS", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + g_assert (s_ip4); + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + /* Wired setting */ + s_wired = nm_setting_wired_new (); + g_assert (s_wired); + nm_connection_add_setting (connection, s_wired); + + /* 802.1x setting */ + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + nm_connection_add_setting (connection, NM_SETTING (s_8021x)); + + nm_setting_802_1x_add_eap_method (s_8021x, "tls"); + g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL); + + success = nm_setting_802_1x_set_ca_cert (s_8021x, + TEST_WIRED_TLS_CA_CERT, + scheme, + NULL, + &error); + g_assert_no_error (error); + g_assert (success); + + success = nm_setting_802_1x_set_client_cert (s_8021x, + TEST_WIRED_TLS_CLIENT_CERT, + scheme, + NULL, + &error); + g_assert_no_error (error); + g_assert (success); + + success = nm_setting_802_1x_set_private_key (s_8021x, + TEST_WIRED_TLS_PRIVKEY, + "test1", + scheme, + NULL, + &error); + g_assert_no_error (error); + g_assert (success); + + return connection; +} + +static char * +get_path (const char *file, gboolean relative) +{ + return relative ? g_path_get_basename (file) : g_strdup (file); +} + +static void +test_write_wired_8021x_tls_connection_path (void) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + char *tmp, *tmp2; + gboolean success; + gs_free char *testfile = NULL; + gs_unref_keyfile GKeyFile *keyfile = NULL; + gboolean relative = FALSE; + gboolean reread_same = FALSE; + + connection = create_wired_tls_connection (NM_SETTING_802_1X_CK_SCHEME_PATH); + g_assert (connection != NULL); + + write_test_connection_reread (connection, &testfile, &reread, &reread_same); + nmtst_assert_connection_verifies_without_normalization (reread); + _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); + _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); + _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY); + assert_reread_same (connection, reread); + g_clear_object (&reread); + + /* Read the connection back in and compare it to the one we just wrote out */ + reread = keyfile_read_connection_from_file (testfile); + + success = nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT); + if (!reread) { + g_warning ("Written and re-read connection weren't the same"); + g_assert (success); + } + + /* Ensure the cert and key values are properly written out */ + keyfile = keyfile_load_from_file (testfile); + + /* Depending on whether this test is being run from 'make check' or + * 'make distcheck' we might be using relative paths (check) or + * absolute ones (distcheck). + */ + tmp2 = g_path_get_dirname (testfile); + if (g_strcmp0 (tmp2, TEST_KEYFILES_DIR) == 0) + relative = TRUE; + g_free (tmp2); + + /* CA cert */ + tmp = g_key_file_get_string (keyfile, + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_CA_CERT, + NULL); + tmp2 = get_path (TEST_WIRED_TLS_CA_CERT, relative); + g_assert_cmpstr (tmp, ==, tmp2); + g_free (tmp2); + g_free (tmp); + + /* Client cert */ + tmp = g_key_file_get_string (keyfile, + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_CLIENT_CERT, + NULL); + tmp2 = get_path (TEST_WIRED_TLS_CLIENT_CERT, relative); + g_assert_cmpstr (tmp, ==, tmp2); + g_free (tmp2); + g_free (tmp); + + /* Private key */ + tmp = g_key_file_get_string (keyfile, + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_PRIVATE_KEY, + NULL); + tmp2 = get_path (TEST_WIRED_TLS_PRIVKEY, relative); + g_assert_cmpstr (tmp, ==, tmp2); + g_free (tmp2); + g_free (tmp); + + unlink (testfile); +} + +static void +test_write_wired_8021x_tls_connection_blob (void) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSetting8021x *s_8021x; + gs_free char *testfile = NULL; + char *new_ca_cert; + char *new_client_cert; + char *new_priv_key; + const char *uuid; + gboolean reread_same = FALSE; + GBytes *password_raw; + +#define PASSWORD_RAW "password-raw\0test" + + connection = create_wired_tls_connection (NM_SETTING_802_1X_CK_SCHEME_BLOB); + g_assert (connection != NULL); + + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x); + + password_raw = g_bytes_new (PASSWORD_RAW, NM_STRLEN (PASSWORD_RAW)); + g_object_set (s_8021x, + NM_SETTING_802_1X_PASSWORD_RAW, + password_raw, + NULL); + g_bytes_unref (password_raw); + + write_test_connection_reread (connection, &testfile, &reread, &reread_same); + nmtst_assert_connection_verifies_without_normalization (reread); + _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); + _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); + _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY); + assert_reread_same (connection, reread); + g_clear_object (&reread); + + /* Check that the new certs got written out */ + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + uuid = nm_setting_connection_get_uuid (s_con); + g_assert (uuid); + + new_ca_cert = g_strdup_printf ("%s/%s-ca-cert.pem", TEST_SCRATCH_DIR, uuid); + g_assert (new_ca_cert); + g_assert (g_file_test (new_ca_cert, G_FILE_TEST_EXISTS)); + + new_client_cert = g_strdup_printf ("%s/%s-client-cert.pem", TEST_SCRATCH_DIR, uuid); + g_assert (new_client_cert); + g_assert (g_file_test (new_client_cert, G_FILE_TEST_EXISTS)); + + new_priv_key = g_strdup_printf ("%s/%s-private-key.pem", TEST_SCRATCH_DIR, uuid); + g_assert (new_priv_key); + g_assert (g_file_test (new_priv_key, G_FILE_TEST_EXISTS)); + + /* Read the connection back in and compare it to the one we just wrote out */ + reread = keyfile_read_connection_from_file (testfile); + + /* Ensure the re-read connection's certificates use the path scheme */ + s_8021x = nm_connection_get_setting_802_1x (reread); + g_assert (s_8021x); + g_assert (nm_setting_802_1x_get_ca_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH); + g_assert (nm_setting_802_1x_get_client_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH); + g_assert (nm_setting_802_1x_get_private_key_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH); + + password_raw = nm_setting_802_1x_get_password_raw (s_8021x); + g_assert (password_raw); + g_assert (nm_utils_gbytes_equal_mem (password_raw, PASSWORD_RAW, NM_STRLEN (PASSWORD_RAW))); + + unlink (testfile); + + /* Clean up written certs */ + unlink (new_ca_cert); + g_free (new_ca_cert); + + unlink (new_client_cert); + g_free (new_client_cert); + + unlink (new_priv_key); + g_free (new_priv_key); +} + +static void +test_read_dcb_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_dcb_connection"); +} + +static void +test_read_infiniband_connection (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingInfiniband *s_ib; + const char *mac; + guint8 expected_mac[INFINIBAND_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, + 0x90 }; + const char *expected_id = "Test InfiniBand Connection"; + const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57"; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_InfiniBand_Connection"); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, expected_uuid); + + s_ib = nm_connection_get_setting_infiniband (connection); + g_assert (s_ib); + + mac = nm_setting_infiniband_get_mac_address (s_ib); + g_assert (mac); + g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, sizeof (expected_mac))); +} + +static void +test_write_infiniband_connection (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingInfiniband *s_ib; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + const char *mac = "99:88:77:66:55:44:ab:bc:cd:de:ef:f0:0a:1b:2c:3d:4e:5f:6f:ba"; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = (NMSettingConnection *) nm_setting_connection_new (); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Work InfiniBand", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_INFINIBAND_SETTING_NAME, + NULL); + + /* InfiniBand setting */ + s_ib = (NMSettingInfiniband *) nm_setting_infiniband_new (); + g_assert (s_ib); + nm_connection_add_setting (connection, NM_SETTING (s_ib)); + + g_object_set (s_ib, + NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, + NM_SETTING_INFINIBAND_MTU, 900, + NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + g_assert (s_ip4); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + g_assert (s_ip6); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + + write_test_connection_and_reread (connection, FALSE); +} + +static void +test_read_bridge_main (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingIPConfig *s_ip4; + NMSettingBridge *s_bridge; + const char *expected_id = "Test Bridge Main"; + const char *expected_uuid = "8f061643-fe41-4d4c-a8d9-097d26e2ad3a"; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Bridge_Main"); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, expected_uuid); + g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "br0"); + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + g_assert (s_ip4); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + + s_bridge = nm_connection_get_setting_bridge (connection); + g_assert (s_bridge); + g_assert_cmpuint (nm_setting_bridge_get_forward_delay (s_bridge), ==, 2); + g_assert_cmpuint (nm_setting_bridge_get_stp (s_bridge), ==, TRUE); + g_assert_cmpuint (nm_setting_bridge_get_priority (s_bridge), ==, 32744); + g_assert_cmpuint (nm_setting_bridge_get_hello_time (s_bridge), ==, 7); + g_assert_cmpuint (nm_setting_bridge_get_max_age (s_bridge), ==, 39); + g_assert_cmpuint (nm_setting_bridge_get_ageing_time (s_bridge), ==, 235352); + g_assert_cmpuint (nm_setting_bridge_get_multicast_snooping (s_bridge), ==, FALSE); +} + +static void +test_write_bridge_main (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingBridge *s_bridge; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + + connection = nm_simple_connection_new (); + g_assert (connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Bridge Main", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_CONNECTION_INTERFACE_NAME, "br0", + NULL); + + /* Bridge setting */ + s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); + g_assert (s_bridge); + nm_connection_add_setting (connection, NM_SETTING (s_bridge)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + g_assert (s_ip4); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, + NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1", + NULL); + + add_one_ip_address (s_ip4, "1.2.3.4", 24); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + g_assert (s_ip6); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + + write_test_connection_and_reread (connection, FALSE); +} + +static void +test_read_bridge_component (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingBridgePort *s_port; + NMSettingWired *s_wired; + const char *mac; + guint8 expected_mac[ETH_ALEN] = { 0x00, 0x22, 0x15, 0x59, 0x62, 0x97 }; + const char *expected_id = "Test Bridge Component"; + const char *expected_uuid = "d7b4f96c-c45e-4298-bef8-f48574f8c1c0"; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Bridge_Component"); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id); + g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, expected_uuid); + g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, "br0"); + g_assert (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BRIDGE_SETTING_NAME)); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired); + mac = nm_setting_wired_get_mac_address (s_wired); + g_assert (mac); + g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, sizeof (expected_mac))); + + s_port = nm_connection_get_setting_bridge_port (connection); + g_assert (s_port); + g_assert (nm_setting_bridge_port_get_hairpin_mode (s_port)); + g_assert_cmpuint (nm_setting_bridge_port_get_priority (s_port), ==, 28); + g_assert_cmpuint (nm_setting_bridge_port_get_path_cost (s_port), ==, 100); +} + +static void +test_write_bridge_component (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingBridgePort *s_port; + NMSettingWired *s_wired; + const char *mac = "99:88:77:66:55:44"; + + connection = nm_simple_connection_new (); + g_assert (connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Bridge Component", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_MASTER, "br0", + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + g_assert (s_wired); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + g_object_set (s_wired, + NM_SETTING_WIRED_MAC_ADDRESS, mac, + NM_SETTING_WIRED_MTU, 1300, + NULL); + + /* BridgePort setting */ + s_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); + g_assert (s_port); + nm_connection_add_setting (connection, NM_SETTING (s_port)); + + g_object_set (s_port, + NM_SETTING_BRIDGE_PORT_PRIORITY, 3, + NM_SETTING_BRIDGE_PORT_PATH_COST, 99, + NULL); + + write_test_connection_and_reread (connection, FALSE); +} + +static void +test_read_new_wired_group_name (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWired *s_wired; + const char *mac; + guint8 expected_mac[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_New_Wired_Group_Name"); + + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired); + g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 1400); + + mac = nm_setting_wired_get_mac_address (s_wired); + g_assert (mac); + g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, sizeof (expected_mac))); +} + +static void +test_write_new_wired_group_name (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + gs_unref_keyfile GKeyFile *kf = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + gs_free char *testfile = NULL; + gs_free_error GError *error = NULL; + char *s; + int mtu; + + connection = nm_simple_connection_new (); + g_assert (connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Wired New Group Name", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + g_assert (s_wired); + g_object_set (s_wired, NM_SETTING_WIRED_MTU, 1400, NULL); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + write_test_connection (connection, &testfile); + + assert_reread (connection, TRUE, testfile); + + /* Look at the keyfile itself to ensure we wrote out the new group names and type */ + kf = keyfile_load_from_file (testfile); + + s = g_key_file_get_string (kf, NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE, &error); + g_assert_no_error (error); + g_assert_cmpstr (s, ==, "ethernet"); + g_free (s); + + mtu = g_key_file_get_integer (kf, "ethernet", NM_SETTING_WIRED_MTU, &error); + g_assert_no_error (error); + g_assert_cmpint (mtu, ==, 1400); + + unlink (testfile); +} + +static void +test_read_new_wireless_group_names (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + GBytes *ssid; + const char *expected_ssid = "foobar"; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_New_Wireless_Group_Names"); + + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + + ssid = nm_setting_wireless_get_ssid (s_wifi); + g_assert (ssid); + g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid))); + + g_assert_cmpstr (nm_setting_wireless_get_mode (s_wifi), ==, NM_SETTING_WIRELESS_MODE_INFRA); + + s_wsec = nm_connection_get_setting_wireless_security (connection); + g_assert (s_wsec); + g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "wpa-psk"); + g_assert_cmpstr (nm_setting_wireless_security_get_psk (s_wsec), ==, "s3cu4e passphrase"); +} + +static void +test_write_new_wireless_group_names (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + gs_unref_keyfile GKeyFile *kf = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + GBytes *ssid; + unsigned char tmpssid[] = { 0x31, 0x33, 0x33, 0x37 }; + const char *expected_psk = "asdfasdfasdfa12315"; + gs_free char *testfile = NULL; + gs_free_error GError *error = NULL; + char *s; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write New Wireless Group Names", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, + NULL); + + /* Wi-Fi setting */ + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); + g_object_set (s_wifi, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + NULL); + g_bytes_unref (ssid); + + /* Wi-Fi security setting */ + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_PSK, expected_psk, + NULL); + + write_test_connection (connection, &testfile); + + assert_reread (connection, TRUE, testfile); + + /* Look at the keyfile itself to ensure we wrote out the new group names and type */ + kf = keyfile_load_from_file (testfile); + + s = g_key_file_get_string (kf, NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE, &error); + g_assert_no_error (error); + g_assert_cmpstr (s, ==, "wifi"); + g_free (s); + + s = g_key_file_get_string (kf, "wifi", NM_SETTING_WIRELESS_MODE, &error); + g_assert_no_error (error); + g_assert_cmpstr (s, ==, NM_SETTING_WIRELESS_MODE_INFRA); + g_free (s); + + s = g_key_file_get_string (kf, "wifi-security", NM_SETTING_WIRELESS_SECURITY_PSK, &error); + g_assert_no_error (error); + g_assert_cmpstr (s, ==, expected_psk); + g_free (s); + + unlink (testfile); +} + +static void +test_read_missing_vlan_setting (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingVlan *s_vlan; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Missing_Vlan_Setting"); + + s_vlan = nm_connection_get_setting_vlan (connection); + g_assert (s_vlan); + g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 0); + g_assert_cmpint (nm_setting_vlan_get_flags (s_vlan), ==, NM_VLAN_FLAG_REORDER_HEADERS); +} + +static void +test_read_missing_vlan_flags (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingVlan *s_vlan; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Missing_Vlan_Flags"); + + s_vlan = nm_connection_get_setting_vlan (connection); + g_assert (s_vlan); + + g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 444); + g_assert_cmpstr (nm_setting_vlan_get_parent (s_vlan), ==, "em1"); + g_assert_cmpint (nm_setting_vlan_get_flags (s_vlan), ==, NM_VLAN_FLAG_REORDER_HEADERS); +} + +static void +test_read_missing_id_uuid (void) +{ + gs_unref_object NMConnection *connection = NULL; + gs_free char *expected_uuid = NULL; + const char *FILENAME = TEST_KEYFILES_DIR"/Test_Missing_ID_UUID"; + + expected_uuid = _nm_utils_uuid_generate_from_strings ("keyfile", FILENAME, NULL); + + connection = keyfile_read_connection_from_file (FILENAME); + + g_assert_cmpstr (nm_connection_get_id (connection), ==, "Test_Missing_ID_UUID"); + g_assert_cmpstr (nm_connection_get_uuid (connection), ==, expected_uuid); +} + +static void +test_read_minimal (void) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *con_archetype = NULL; + NMSettingConnection *s_con; + + con_archetype = nmtst_create_minimal_connection ("Test_minimal_x", + "a15bd68f-c32b-40b8-8d27-49e472a85919", + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + nmtst_connection_normalize (con_archetype); + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_1"); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), + NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), + NULL); + nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); + g_clear_object (&connection); + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_2"); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), + NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), + NULL); + nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); + g_clear_object (&connection); +} + +static void +test_read_minimal_slave (void) +{ + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *con_archetype = NULL; + NMSettingConnection *s_con; + + con_archetype = nmtst_create_minimal_connection ("Test_minimal_slave_x", + "a56b4ca5-7075-43d4-82c7-5d0cb15f7654", + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, "br0", + NM_SETTING_CONNECTION_SLAVE_TYPE, "bridge", + NULL); + nmtst_connection_normalize (con_archetype); + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_1"); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), + NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), + NULL); + nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); + g_clear_object (&connection); + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_2"); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), + NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), + NULL); + nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); + g_clear_object (&connection); + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_3"); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), + NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), + NULL); + nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); + g_clear_object (&connection); + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_4"); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), + NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), + NULL); + nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); + g_clear_object (&connection); +} + +static void +test_read_enum_property (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingIPConfig *s_ip6; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Enum_Property"); + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + g_assert (s_ip6); + g_assert_cmpint (nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)), ==, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR); +} + +static void +test_write_enum_property (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip6; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Enum Property", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + /* IP6 setting */ + s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_IP6_PRIVACY, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + NULL); + + nmtst_connection_normalize (connection); + + write_test_connection_and_reread (connection, FALSE); +} + +static void +test_read_flags_property (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingGsm *s_gsm; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Flags_Property"); + + s_gsm = nm_connection_get_setting_gsm (connection); + g_assert (s_gsm); + g_assert_cmpint (nm_setting_gsm_get_password_flags (s_gsm), ==, + NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_REQUIRED); +} + +static void +test_write_flags_property (void) +{ + NMTST_UUID_INIT (uuid); + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSetting *s_gsm; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Flags Property", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, + NULL); + + /* GSM setting */ + s_gsm = nm_setting_gsm_new (); + nm_connection_add_setting (connection, s_gsm); + g_object_set (s_gsm, + NM_SETTING_GSM_APN, "myapn", + NM_SETTING_GSM_USERNAME, "adfasdfasdf", + NM_SETTING_GSM_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_NOT_REQUIRED, + NULL); + + nmtst_connection_normalize (connection); + + write_test_connection_and_reread (connection, FALSE); +} + +/*****************************************************************************/ + +static void +test_read_tc_config (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingTCConfig *s_tc; + NMTCQdisc *qdisc1, *qdisc2; + NMTCAction *action1, *action2; + NMTCTfilter *tfilter1, *tfilter2; + + connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_TC_Config"); + + s_tc = nm_connection_get_setting_tc_config (connection); + g_assert (s_tc); + + g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 2); + + qdisc1 = nm_setting_tc_config_get_qdisc (s_tc, 0); + g_assert (qdisc1); + g_assert (g_strcmp0 (nm_tc_qdisc_get_kind (qdisc1), "fq_codel") == 0); + g_assert (nm_tc_qdisc_get_handle (qdisc1) == TC_H_MAKE (0x1234 << 16, 0x0000)); + g_assert (nm_tc_qdisc_get_parent (qdisc1) == TC_H_ROOT); + + qdisc2 = nm_setting_tc_config_get_qdisc (s_tc, 1); + g_assert (qdisc2); + g_assert (g_strcmp0 (nm_tc_qdisc_get_kind (qdisc2), "ingress") == 0); + g_assert (nm_tc_qdisc_get_handle (qdisc2) == TC_H_MAKE (TC_H_INGRESS, 0)); + g_assert (nm_tc_qdisc_get_parent (qdisc2) == TC_H_INGRESS); + + g_assert (nm_setting_tc_config_get_num_tfilters (s_tc) == 2); + + tfilter1 = nm_setting_tc_config_get_tfilter (s_tc, 0); + g_assert (tfilter1); + g_assert (g_strcmp0 (nm_tc_tfilter_get_kind (tfilter1), "matchall") == 0); + g_assert (nm_tc_tfilter_get_handle (tfilter1) == TC_H_UNSPEC); + g_assert (nm_tc_tfilter_get_parent (tfilter1) == TC_H_MAKE (0x1234 << 16, 0x0000)); + + action1 = nm_tc_tfilter_get_action (tfilter1); + g_assert (action1); + g_assert (g_strcmp0 (nm_tc_action_get_kind (action1), "drop") == 0); + + tfilter2 = nm_setting_tc_config_get_tfilter (s_tc, 1); + g_assert (tfilter2); + g_assert (g_strcmp0 (nm_tc_tfilter_get_kind (tfilter2), "matchall") == 0); + g_assert (nm_tc_tfilter_get_handle (tfilter2) == TC_H_UNSPEC); + g_assert (nm_tc_tfilter_get_parent (tfilter2) == TC_H_MAKE (TC_H_INGRESS, 0)); + + action2 = nm_tc_tfilter_get_action (tfilter2); + g_assert (action2); + g_assert (g_strcmp0 (nm_tc_action_get_kind (action2), "simple") == 0); + g_assert (g_strcmp0 (g_variant_get_bytestring (nm_tc_action_get_attribute (action2, "sdata")), + "Hello") == 0); +} + +static void +test_write_tc_config (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSetting *s_tc; + NMTCQdisc *qdisc1, *qdisc2; + NMTCTfilter *tfilter1, *tfilter2; + NMTCAction *action; + GError *error = NULL; + + connection = nmtst_create_minimal_connection ("Test TC", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + s_tc = nm_setting_tc_config_new (); + + qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error); + nmtst_assert_success (qdisc1, error); + nm_tc_qdisc_set_handle (qdisc1, TC_H_MAKE (0x1234 << 16, 0x0000)); + nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc1); + + qdisc2 = nm_tc_qdisc_new ("ingress", TC_H_INGRESS, &error); + nmtst_assert_success (qdisc2, error); + nm_tc_qdisc_set_handle (qdisc2, TC_H_MAKE (TC_H_INGRESS, 0)); + nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc2); + + tfilter1 = nm_tc_tfilter_new ("matchall", + TC_H_MAKE (0x1234 << 16, 0x0000), + &error); + nmtst_assert_success (tfilter1, error); + action = nm_tc_action_new ("drop", &error); + nmtst_assert_success (action, error); + nm_tc_tfilter_set_action (tfilter1, action); + nm_tc_action_unref (action); + nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter1); + nm_tc_tfilter_unref (tfilter1); + + tfilter2 = nm_tc_tfilter_new ("matchall", + TC_H_MAKE (TC_H_INGRESS, 0), + &error); + nmtst_assert_success (tfilter2, error); + action = nm_tc_action_new ("simple", &error); + nmtst_assert_success (action, error); + nm_tc_action_set_attribute (action, "sdata", g_variant_new_bytestring ("Hello")); + nm_tc_tfilter_set_action (tfilter2, action); + nm_tc_action_unref (action); + nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter2); + nm_tc_tfilter_unref (tfilter2); + + nm_connection_add_setting (connection, s_tc); + + nmtst_connection_normalize (connection); + write_test_connection_and_reread (connection, FALSE); + + nm_tc_qdisc_unref (qdisc1); + nm_tc_qdisc_unref (qdisc2); +} + +/*****************************************************************************/ + +static void +_escape_filename (gboolean with_extension, const char *filename, gboolean would_be_ignored) +{ + gs_free char *esc = NULL; + + g_assert (filename && filename[0]); + + if (!!would_be_ignored != !!nm_keyfile_utils_ignore_filename (filename, with_extension)) { + if (would_be_ignored) + g_error ("We expect filename \"%s\" to be ignored, but it isn't", filename); + else + g_error ("We expect filename \"%s\" not to be ignored, but it is", filename); + } + + esc = nm_keyfile_utils_create_filename (filename, with_extension); + g_assert (esc && esc[0]); + g_assert (!strchr (esc, '/')); + + if (nm_keyfile_utils_ignore_filename (esc, with_extension)) + g_error ("Escaping filename \"%s\" yielded \"%s\", but this is ignored", filename, esc); +} + +static void +test_nm_keyfile_plugin_utils_escape_filename (void) +{ + _escape_filename (FALSE, "ab", FALSE); + _escape_filename (FALSE, ".vim-file.swp", TRUE); + _escape_filename (FALSE, ".vim-file.Swp", TRUE); + _escape_filename (FALSE, ".vim-file.SWP", TRUE); + _escape_filename (FALSE, ".vim-file.swpx", TRUE); + _escape_filename (FALSE, ".vim-file.Swpx", TRUE); + _escape_filename (FALSE, ".vim-file.SWPX", TRUE); + _escape_filename (FALSE, ".pem-file.pem", TRUE); + _escape_filename (FALSE, ".pem-file.Pem", TRUE); + _escape_filename (FALSE, ".pem-file.PEM", TRUE); + _escape_filename (FALSE, ".pem-file.der", TRUE); + _escape_filename (FALSE, ".pem-file.Der", TRUE); + _escape_filename (FALSE, ".mkstemp.ABCEDF", TRUE); + _escape_filename (FALSE, ".mkstemp.abcdef", TRUE); + _escape_filename (FALSE, ".mkstemp.123456", TRUE); + _escape_filename (FALSE, ".mkstemp.A23456", TRUE); + _escape_filename (FALSE, ".#emacs-locking", TRUE); + _escape_filename (FALSE, "file-with-tilde~", TRUE); + _escape_filename (FALSE, ".file-with-dot", TRUE); + _escape_filename (FALSE, "/some/path/with/trailing/slash/", TRUE); + _escape_filename (FALSE, "/some/path/without/trailing/slash", FALSE); + + _escape_filename (TRUE, "lala", TRUE); +} + +/*****************************************************************************/ + +static void +_assert_keyfile_nmmeta (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean allow_relative, + const char *exp_full_filename, + const char *exp_uuid, + const char *exp_symlink_target, + const char *exp_loaded_path) +{ + gs_free char *full_filename = NULL; + gs_free char *symlink_target = NULL; + gs_free char *uuid2 = NULL; + gs_free char *loaded_path2 = NULL; + gs_free char *dirname3 = NULL; + gs_free char *filename3 = NULL; + gs_free char *uuid3 = NULL; + gs_free char *loaded_path3 = NULL; + gboolean success; + gs_free char *filename = NULL; + + g_assert (dirname && dirname[0] == '/'); + g_assert (exp_full_filename && exp_full_filename[0]); + g_assert (!exp_loaded_path || exp_loaded_path[0] == '/'); + + filename = g_path_get_basename (exp_full_filename); + + full_filename = nms_keyfile_nmmeta_filename (dirname, uuid, FALSE); + g_assert_cmpstr (full_filename, ==, full_filename); + nm_clear_g_free (&full_filename); + + + g_assert (nms_keyfile_nmmeta_write (dirname, uuid, loaded_path, allow_relative, NULL, &full_filename)); + g_assert_cmpstr (full_filename, ==, exp_full_filename); + nm_clear_g_free (&full_filename); + + if (exp_symlink_target) + g_assert (g_file_test (exp_full_filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_SYMLINK)); + else + g_assert (!g_file_test (exp_full_filename, G_FILE_TEST_EXISTS)); + symlink_target = g_file_read_link (exp_full_filename, NULL); + g_assert_cmpstr (symlink_target, ==, exp_symlink_target); + + + success = nms_keyfile_nmmeta_read (dirname, filename, &full_filename, &uuid2, &loaded_path2, NULL, NULL); + g_assert_cmpint (!!exp_uuid, ==, success); + if (success) + g_assert_cmpstr (full_filename, ==, exp_full_filename); + else + g_assert_cmpstr (full_filename, ==, NULL); + nm_clear_g_free (&full_filename); + g_assert_cmpstr (uuid2, ==, exp_uuid); + g_assert_cmpstr (loaded_path2, ==, exp_loaded_path); + + + success = nms_keyfile_nmmeta_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3, NULL); + g_assert_cmpint (!!exp_uuid, ==, success); + if (success) { + g_assert_cmpstr (dirname3, ==, dirname); + g_assert_cmpstr (filename3, ==, filename); + } else { + g_assert_cmpstr (dirname3, ==, NULL); + g_assert_cmpstr (filename3, ==, NULL); + } + g_assert_cmpstr (uuid3, ==, exp_uuid); + g_assert_cmpstr (loaded_path3, ==, exp_loaded_path); +} + +static void +test_nmmeta (void) +{ + const char *uuid = "3c03fd17-ddc3-4100-a954-88b6fafff959"; + gs_free char *filename = g_strdup_printf ("%s%s", + uuid, + NM_KEYFILE_PATH_SUFFIX_NMMETA); + gs_free char *full_filename = g_strdup_printf ("%s/%s", + TEST_SCRATCH_DIR, + filename); + const char *loaded_path0 = NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL; + const char *loaded_path1 = "/some/where/but/not/scratch/dir"; + const char *filename2 = "foo1"; + gs_free char *loaded_path2 = g_strdup_printf ("%s/%s", + TEST_SCRATCH_DIR, + filename2); + + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, NULL, FALSE, full_filename, NULL, NULL, NULL); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, NULL, TRUE, full_filename, NULL, NULL, NULL); + + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path0, FALSE, full_filename, uuid, loaded_path0, loaded_path0); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path0, TRUE, full_filename, uuid, loaded_path0, loaded_path0); + + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path1, FALSE, full_filename, uuid, loaded_path1, loaded_path1); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path1, TRUE, full_filename, uuid, loaded_path1, loaded_path1); + + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path2, FALSE, full_filename, uuid, loaded_path2, loaded_path2); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path2, TRUE, full_filename, uuid, filename2, loaded_path2); + + (void) unlink (full_filename); +} + +/*****************************************************************************/ + +NMTST_DEFINE (); + +int main (int argc, char **argv) +{ + int errsv; + + _nm_utils_set_testing (NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK); + + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + + if (g_mkdir_with_parents (TEST_SCRATCH_DIR, 0755) != 0) { + errsv = errno; + g_error ("failure to create test directory \"%s\": %s", TEST_SCRATCH_DIR, nm_strerror_native (errsv)); + } + + /* The tests */ + g_test_add_func ("/keyfile/test_read_valid_wired_connection", test_read_valid_wired_connection); + g_test_add_func ("/keyfile/test_write_wired_connection", test_write_wired_connection); + + g_test_add_func ("/keyfile/test_read_ip6_wired_connection", test_read_ip6_wired_connection); + g_test_add_func ("/keyfile/test_write_ip6_wired_connection", test_write_ip6_wired_connection); + + g_test_add_func ("/keyfile/test_read_wired_mac_case", test_read_wired_mac_case); + g_test_add_func ("/keyfile/test_read_mac_old_format", test_read_mac_old_format); + g_test_add_func ("/keyfile/test_read_mac_ib_old_format", test_read_mac_ib_old_format); + + g_test_add_func ("/keyfile/test_read_valid_wireless_connection", test_read_valid_wireless_connection); + g_test_add_func ("/keyfile/test_write_wireless_connection", test_write_wireless_connection); + + g_test_add_func ("/keyfile/test_read_string_ssid", test_read_string_ssid); + g_test_add_func ("/keyfile/test_write_string_ssid", test_write_string_ssid); + + g_test_add_func ("/keyfile/test_read_intlist_ssid", test_read_intlist_ssid); + g_test_add_func ("/keyfile/test_write_intlist_ssid", test_write_intlist_ssid); + + g_test_add_func ("/keyfile/test_read_intlike_ssid", test_read_intlike_ssid); + g_test_add_func ("/keyfile/test_write_intlike_ssid", test_write_intlike_ssid); + + g_test_add_func ("/keyfile/test_read_intlike_ssid_2", test_read_intlike_ssid_2); + g_test_add_func ("/keyfile/test_write_intlike_ssid_2", test_write_intlike_ssid_2); + + g_test_add_func ("/keyfile/test_read_bt_dun_connection", test_read_bt_dun_connection); + g_test_add_func ("/keyfile/test_write_bt_dun_connection", test_write_bt_dun_connection); + + g_test_add_func ("/keyfile/test_read_gsm_connection", test_read_gsm_connection); + g_test_add_func ("/keyfile/test_write_gsm_connection", test_write_gsm_connection); + + g_test_add_func ("/keyfile/test_read_wired_8021x_tls_blob_connection", test_read_wired_8021x_tls_blob_connection); + g_test_add_func ("/keyfile/test_read_wired_8021x_tls_bad_path_connection", test_read_wired_8021x_tls_bad_path_connection); + + g_test_add_func ("/keyfile/test_read_wired_8021x_tls_old_connection", test_read_wired_8021x_tls_old_connection); + g_test_add_func ("/keyfile/test_read_wired_8021x_tls_new_connection", test_read_wired_8021x_tls_new_connection); + g_test_add_func ("/keyfile/test_write_wired_8021x_tls_connection_path", test_write_wired_8021x_tls_connection_path); + g_test_add_func ("/keyfile/test_write_wired_8021x_tls_connection_blob", test_write_wired_8021x_tls_connection_blob); + + g_test_add_func ("/keyfile/test_read_dcb_connection", test_read_dcb_connection); + + g_test_add_func ("/keyfile/test_read_infiniband_connection", test_read_infiniband_connection); + g_test_add_func ("/keyfile/test_write_infiniband_connection", test_write_infiniband_connection); + + g_test_add_func ("/keyfile/test_read_bridge_main", test_read_bridge_main); + g_test_add_func ("/keyfile/test_write_bridge_main", test_write_bridge_main); + g_test_add_func ("/keyfile/test_read_bridge_component", test_read_bridge_component); + g_test_add_func ("/keyfile/test_write_bridge_component", test_write_bridge_component); + + g_test_add_func ("/keyfile/test_read_new_wired_group_name", test_read_new_wired_group_name); + g_test_add_func ("/keyfile/test_write_new_wired_group_name", test_write_new_wired_group_name); + g_test_add_func ("/keyfile/test_read_new_wireless_group_names", test_read_new_wireless_group_names); + g_test_add_func ("/keyfile/test_write_new_wireless_group_names", test_write_new_wireless_group_names); + + g_test_add_func ("/keyfile/test_read_missing_vlan_setting", test_read_missing_vlan_setting); + g_test_add_func ("/keyfile/test_read_missing_vlan_flags", test_read_missing_vlan_flags); + g_test_add_func ("/keyfile/test_read_missing_id_uuid", test_read_missing_id_uuid); + + g_test_add_func ("/keyfile/test_read_minimal", test_read_minimal); + g_test_add_func ("/keyfile/test_read_minimal_slave", test_read_minimal_slave); + + g_test_add_func ("/keyfile/test_read_enum_property", test_read_enum_property); + g_test_add_func ("/keyfile/test_write_enum_property", test_write_enum_property); + g_test_add_func ("/keyfile/test_read_flags_property", test_read_flags_property); + g_test_add_func ("/keyfile/test_write_flags_property", test_write_flags_property); + + g_test_add_func ("/keyfile/test_read_tc_config", test_read_tc_config); + g_test_add_func ("/keyfile/test_write_tc_config", test_write_tc_config); + + g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename", test_nm_keyfile_plugin_utils_escape_filename); + + g_test_add_func ("/keyfile/test_nmmeta", test_nmmeta); + + return g_test_run (); +} diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c deleted file mode 100644 index baecac13..00000000 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ /dev/null @@ -1,2703 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - keyfile plugin - * - * 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 (C) 2008 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "nm-core-internal.h" - -#include "settings/plugins/keyfile/nms-keyfile-reader.h" -#include "settings/plugins/keyfile/nms-keyfile-writer.h" -#include "settings/plugins/keyfile/nms-keyfile-utils.h" - -#include "nm-test-utils-core.h" - -#define TEST_KEYFILES_DIR NM_BUILD_SRCDIR"/src/settings/plugins/keyfile/tests/keyfiles" -#define TEST_SCRATCH_DIR NM_BUILD_BUILDDIR"/src/settings/plugins/keyfile/tests/keyfiles" - -/*****************************************************************************/ - -static void -check_ip_address (NMSettingIPConfig *config, int idx, const char *address, int plen) -{ - NMIPAddress *ip4 = nm_setting_ip_config_get_address (config, idx); - - g_assert (ip4); - g_assert_cmpstr (nm_ip_address_get_address (ip4), ==, address); - g_assert_cmpint (nm_ip_address_get_prefix (ip4), ==, plen); -} - -static void -check_ip_route (NMSettingIPConfig *config, int idx, const char *destination, int plen, - const char *next_hop, gint64 metric) -{ - NMIPRoute *route = nm_setting_ip_config_get_route (config, idx); - - g_assert (route); - g_assert_cmpstr (nm_ip_route_get_dest (route), ==, destination); - g_assert_cmpint (nm_ip_route_get_prefix (route), ==, plen); - g_assert_cmpstr (nm_ip_route_get_next_hop (route), ==, next_hop); - g_assert_cmpint (nm_ip_route_get_metric (route), ==, metric); -} - -#define keyfile_read_connection_from_file(full_filename) \ -({ \ - gs_free_error GError *_error = NULL; \ - NMConnection *_connection; \ - \ - g_assert (full_filename && full_filename[0] == '/'); \ - \ - _connection = nms_keyfile_reader_from_file (full_filename, \ - NULL, \ - (nmtst_get_rand_int () % 2) ? &_error : NULL); \ - nmtst_assert_success (_connection, _error); \ - nmtst_assert_connection_verifies_without_normalization (_connection); \ - \ - _connection; \ -}) - -static void -assert_reread (NMConnection *connection, gboolean normalize_connection, const char *testfile) -{ - gs_unref_object NMConnection *reread = NULL; - gs_unref_object NMConnection *connection_clone = NULL; - NMSettingConnection *s_con; - - g_assert (NM_IS_CONNECTION (connection)); - - reread = keyfile_read_connection_from_file (testfile); - - if ( !normalize_connection - && (s_con = nm_connection_get_setting_connection (connection)) - && !nm_setting_connection_get_master (s_con) - && !nm_connection_get_setting_proxy (connection)) { - connection_clone = nmtst_clone_connection (connection); - connection = connection_clone; - nm_connection_add_setting (connection, nm_setting_proxy_new ()); - } - - nmtst_assert_connection_equals (connection, normalize_connection, reread, FALSE); -} - -static void -assert_reread_and_unlink (NMConnection *connection, gboolean normalize_connection, const char *testfile) -{ - assert_reread (connection, normalize_connection, testfile); - unlink (testfile); -} - -static void -assert_reread_same (NMConnection *connection, - NMConnection *reread) -{ - nmtst_assert_connection_verifies_without_normalization (reread); - nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); -} - -static void -write_test_connection_reread (NMConnection *connection, - char **testfile, - NMConnection **out_reread, - gboolean *out_reread_same) -{ - uid_t owner_uid; - gid_t owner_grp; - gboolean success; - GError *error = NULL; - GError **p_error = (nmtst_get_rand_int () % 2) ? &error : NULL; - gs_unref_object NMConnection *connection_normalized = NULL; - - g_assert (NM_IS_CONNECTION (connection)); - g_assert (testfile && !*testfile); - - owner_uid = geteuid (); - owner_grp = getegid (); - - connection_normalized = nmtst_connection_duplicate_and_normalize (connection); - - success = nms_keyfile_writer_test_connection (connection_normalized, - TEST_SCRATCH_DIR, - owner_uid, - owner_grp, - testfile, - out_reread, - out_reread_same, - p_error); - g_assert_no_error (error); - g_assert (success); - g_assert (*testfile && (*testfile)[0]); -} - -static void -write_test_connection (NMConnection *connection, char **testfile) -{ - gs_unref_object NMConnection *reread = NULL; - gboolean reread_same = FALSE; - - write_test_connection_reread (connection, testfile, &reread, &reread_same); - assert_reread_same (connection, reread); - g_assert (reread_same); -} - -static void -write_test_connection_and_reread (NMConnection *connection, gboolean normalize_connection) -{ - gs_free char *testfile = NULL; - - g_assert (NM_IS_CONNECTION (connection)); - - write_test_connection (connection, &testfile); - assert_reread_and_unlink (connection, normalize_connection, testfile); -} - -static GKeyFile * -keyfile_load_from_file (const char *testfile) -{ - GKeyFile *keyfile; - GError *error = NULL; - gboolean success; - - g_assert (testfile && *testfile); - - keyfile = g_key_file_new (); - success = g_key_file_load_from_file (keyfile, testfile, G_KEY_FILE_NONE, &error); - g_assert_no_error (error); - g_assert(success); - - return keyfile; -} - -static void -_setting_copy_property_gbytes (NMConnection *src, NMConnection *dst, const char *setting_name, const char *property_name) -{ - gs_unref_bytes GBytes *blob = NULL; - NMSetting *s_src; - NMSetting *s_dst; - - g_assert (NM_IS_CONNECTION (src)); - g_assert (NM_IS_CONNECTION (dst)); - g_assert (setting_name); - g_assert (property_name); - - s_src = nm_connection_get_setting_by_name (src, setting_name); - g_assert (NM_IS_SETTING (s_src)); - s_dst = nm_connection_get_setting_by_name (dst, setting_name); - g_assert (NM_IS_SETTING (s_dst)); - - g_object_get (s_src, property_name, &blob, NULL); - g_object_set (s_dst, property_name, blob, NULL); -} - -/*****************************************************************************/ - -static void -test_read_valid_wired_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - NMSettingIPConfig *s_ip6; - NMIPRoute *route; - const char *mac; - char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; - - NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses1*"); - NMTST_EXPECT_NM_INFO ("*ipv4.addresses:*semicolon at the end*addresses2*"); - NMTST_EXPECT_NM_WARN ("*missing prefix length*address4*"); - NMTST_EXPECT_NM_WARN ("*missing prefix length*address5*"); - NMTST_EXPECT_NM_WARN ("*ipv4.dns: ignoring invalid DNS server IPv4 address 'bogus'*"); - NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes2*"); - NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes3*"); - NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes5*"); - NMTST_EXPECT_NM_INFO ("*ipv4.routes*semicolon at the end*routes8*"); - NMTST_EXPECT_NM_WARN ("*missing prefix length*address4*"); - NMTST_EXPECT_NM_INFO ("*ipv6.address*semicolon at the end*address5*"); - NMTST_EXPECT_NM_WARN ("*missing prefix length*address5*"); - NMTST_EXPECT_NM_INFO ("*ipv6.address*semicolon at the end*address7*"); - NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*"); - NMTST_EXPECT_NM_INFO ("*ipv6.route*semicolon at the end*route6*"); - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection"); - g_test_assert_expected_messages (); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wired Connection"); - g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "4e80a56d-c99f-4aad-a6dd-b449bc398c57"); - g_assert_cmpuint (nm_setting_connection_get_timestamp (s_con), ==, 6654332); - g_assert (nm_setting_connection_get_autoconnect (s_con)); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - - mac = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac); - g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac_address, sizeof (expected_mac_address))); - g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 1400); - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); - g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 2); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "4.2.2.1"); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 1), ==, "4.2.2.2"); - - /* IPv4 addresses */ - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 10); - check_ip_address (s_ip4, 0, "2.3.4.5", 24); - check_ip_address (s_ip4, 1, "192.168.0.5", 24); - check_ip_address (s_ip4, 2, "1.2.3.4", 16); - check_ip_address (s_ip4, 3, "3.4.5.6", 16); - check_ip_address (s_ip4, 4, "4.5.6.7", 24); - check_ip_address (s_ip4, 5, "5.6.7.8", 24); - check_ip_address (s_ip4, 6, "1.2.3.30", 24); - check_ip_address (s_ip4, 7, "1.2.3.30", 25); - check_ip_address (s_ip4, 8, "1.2.3.31", 24); - check_ip_address (s_ip4, 9, "1.2.3.31", 25); - - /* IPv4 gateway */ - g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "2.3.4.6"); - - /* IPv4 routes */ - g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 13); - check_ip_route (s_ip4, 0, "5.6.7.8", 32, NULL, -1); - check_ip_route (s_ip4, 1, "1.2.3.0", 24, "2.3.4.8", 99); - check_ip_route (s_ip4, 2, "1.1.1.2", 12, NULL, -1); - check_ip_route (s_ip4, 3, "1.1.1.3", 13, NULL, -1); - check_ip_route (s_ip4, 4, "1.1.1.4", 14, "2.2.2.4", -1); - check_ip_route (s_ip4, 5, "1.1.1.5", 15, "2.2.2.5", -1); - check_ip_route (s_ip4, 6, "1.1.1.6", 16, "2.2.2.6", 0); - check_ip_route (s_ip4, 7, "1.1.1.7", 17, NULL, -1); - check_ip_route (s_ip4, 8, "1.1.1.8", 18, NULL, -1); - check_ip_route (s_ip4, 9, "1.1.1.9", 19, NULL, 0); - check_ip_route (s_ip4, 10, "1.1.1.10", 21, NULL, 0); - check_ip_route (s_ip4, 11, "1.1.1.10", 20, NULL, 0); - check_ip_route (s_ip4, 12, "1.1.1.11", 21, NULL, 21); - - /* Route attributes */ - route = nm_setting_ip_config_get_route (s_ip4, 12); - g_assert (route); - - nmtst_assert_route_attribute_uint32 (route, NM_IP_ROUTE_ATTRIBUTE_CWND, 10); - nmtst_assert_route_attribute_uint32 (route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1430); - nmtst_assert_route_attribute_boolean (route, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, TRUE); - nmtst_assert_route_attribute_string (route, NM_IP_ROUTE_ATTRIBUTE_SRC, "7.7.7.7"); - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - g_assert (s_ip6); - - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_MANUAL); - - g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 2); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip6, 0), ==, "1111:dddd::aaaa"); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip6, 1), ==, "1::cafe"); - g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip6), ==, 3); - g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 0), ==, "super-domain.com"); - g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 1), ==, "redhat.com"); - g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 2), ==, "gnu.org"); - - /* IPv6 addresses */ - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip6), ==, 10); - check_ip_address (s_ip6, 0, "2:3:4:5:6:7:8:9", 64); - check_ip_address (s_ip6, 1, "abcd:1234:ffff::cdde", 64); - check_ip_address (s_ip6, 2, "1:2:3:4:5:6:7:8", 96); - check_ip_address (s_ip6, 3, "3:4:5:6:7:8:9:0", 128); - check_ip_address (s_ip6, 4, "3:4:5:6:7:8:9:14", 64); - check_ip_address (s_ip6, 5, "3:4:5:6:7:8:9:15", 64); - check_ip_address (s_ip6, 6, "3:4:5:6:7:8:9:16", 66); - check_ip_address (s_ip6, 7, "3:4:5:6:7:8:9:17", 67); - check_ip_address (s_ip6, 8, "3:4:5:6:7:8:9:18", 68); - check_ip_address (s_ip6, 9, "3:4:5:6:7:8:9:19", 69); - - /* IPv6 gateway */ - g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip6), ==, "2:3:4:5:1:2:3:4"); - - /* Routes */ - g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 7); - check_ip_route (s_ip6, 0, "d:e:f:0:1:2:3:4", 64, "f:e:d:c:1:2:3:4", -1); - check_ip_route (s_ip6, 1, "a:b:c:d::", 64, "f:e:d:c:1:2:3:4", 99); - check_ip_route (s_ip6, 2, "8:7:6:5:4:3:2:1", 128, NULL, -1); - check_ip_route (s_ip6, 3, "6:7:8:9:0:1:2:3", 126, NULL, 1); - check_ip_route (s_ip6, 4, "7:8:9:0:1:2:3:4", 125, NULL, 5); - check_ip_route (s_ip6, 5, "8:9:0:1:2:3:4:5", 124, NULL, 6); - check_ip_route (s_ip6, 6, "8:9:0:1:2:3:4:6", 123, NULL, -1); - - /* Route attributes */ - route = nm_setting_ip_config_get_route (s_ip6, 6); - g_assert (route); - nmtst_assert_route_attribute_string (route, NM_IP_ROUTE_ATTRIBUTE_FROM, "abce::/63"); -} - -static void -add_one_ip_address (NMSettingIPConfig *s_ip, - const char *addr, - guint32 prefix) -{ - NMIPAddress *ip_addr; - gs_free_error GError *error = NULL; - - ip_addr = nm_ip_address_new (NM_IS_SETTING_IP4_CONFIG (s_ip) ? AF_INET : AF_INET6, - addr, prefix, &error); - g_assert_no_error (error); - nm_setting_ip_config_add_address (s_ip, ip_addr); - nm_ip_address_unref (ip_addr); -} - -static void -add_one_ip_route (NMSettingIPConfig *s_ip, - const char *dest, - const char *nh, - guint32 prefix, - gint64 metric) -{ - NMIPRoute *route; - gs_free_error GError *error = NULL; - - g_assert (prefix > 0); - route = nm_ip_route_new (NM_IS_SETTING_IP4_CONFIG (s_ip) ? AF_INET : AF_INET6, - dest, prefix, nh, metric, &error); - g_assert_no_error (error); - nm_setting_ip_config_add_route (s_ip, route); - nm_ip_route_unref (route); -} - -static void -test_write_wired_connection (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - NMSettingIPConfig *s_ip6; - NMIPRoute *rt; - const char *mac = "99:88:77:66:55:44"; - const char *dns1 = "4.2.2.1"; - const char *dns2 = "4.2.2.2"; - const char *address1 = "192.168.0.5"; - const char *address2 = "1.2.3.4"; - const char *gw = "192.168.0.1"; - const char *route1 = "10.10.10.2"; - const char *route1_nh = "10.10.10.1"; - const char *route2 = "1.1.1.1"; - const char *route2_nh = "1.2.1.1"; - const char *route3 = "2.2.2.2"; - const char *route3_nh = NULL; - const char *route4 = "3.3.3.3"; - const char *route4_nh = NULL; - const char *dns6_1 = "1::cafe"; - const char *dns6_2 = "2::cafe"; - const char *address6_1 = "abcd::beef"; - const char *address6_2 = "dcba::beef"; - const char *route6_1 = "1:2:3:4:5:6:7:8"; - const char *route6_1_nh = "8:7:6:5:4:3:2:1"; - const char *route6_2 = "2001::1000"; - const char *route6_2_nh = "2001::1111"; - const char *route6_3 = "4:5:6:7:8:9:0:1"; - const char *route6_3_nh = "::"; - const char *route6_4 = "5:6:7:8:9:0:1:2"; - const char *route6_4_nh = "::"; - guint64 timestamp = 0x12345678L; - GError *error = NULL; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Work Wired", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_CONNECTION_TIMESTAMP, timestamp, - NULL); - - /* Wired setting */ - - s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - g_object_set (s_wired, - NM_SETTING_WIRED_MAC_ADDRESS, mac, - NM_SETTING_WIRED_MTU, 900, - NULL); - - /* IP4 setting */ - - s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, - NM_SETTING_IP_CONFIG_GATEWAY, gw, - NULL); - - /* Addresses */ - add_one_ip_address (s_ip4, address1, 24); - add_one_ip_address (s_ip4, address2, 8); - - /* Routes */ - add_one_ip_route (s_ip4, route1, route1_nh, 24, 3); - add_one_ip_route (s_ip4, route2, route2_nh, 8, 1); - add_one_ip_route (s_ip4, route3, route3_nh, 7, -1); - - rt = nm_ip_route_new (AF_INET, route4, 6, route4_nh, 4, &error); - g_assert_no_error (error); - nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (10)); - nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1492)); - nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("1.2.3.4")); - g_assert (nm_setting_ip_config_add_route (s_ip4, rt)); - nm_ip_route_unref (rt); - - /* DNS servers */ - nm_setting_ip_config_add_dns (s_ip4, dns1); - nm_setting_ip_config_add_dns (s_ip4, dns2); - - /* IP6 setting */ - - s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - - g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, - NULL); - - /* Addresses */ - add_one_ip_address (s_ip6, address6_1, 64); - add_one_ip_address (s_ip6, address6_2, 56); - - /* Routes */ - add_one_ip_route (s_ip6, route6_1, route6_1_nh, 64, 3); - add_one_ip_route (s_ip6, route6_2, route6_2_nh, 56, 1); - add_one_ip_route (s_ip6, route6_3, route6_3_nh, 63, 5); - add_one_ip_route (s_ip6, route6_4, route6_4_nh, 62, -1); - - /* DNS servers */ - nm_setting_ip_config_add_dns (s_ip6, dns6_1); - nm_setting_ip_config_add_dns (s_ip6, dns6_2); - - /* DNS searches */ - nm_setting_ip_config_add_dns_search (s_ip6, "wallaceandgromit.com"); - - write_test_connection_and_reread (connection, FALSE); -} - -static void -test_read_ip6_wired_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - NMSettingIPConfig *s_ip6; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_IP6"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wired Connection IP6"); - g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "4e80a56d-c99f-4aad-a6dd-b449bc398c57"); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_DISABLED); - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 0); - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - g_assert (s_ip6); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_MANUAL); - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip6), ==, 1); - check_ip_address (s_ip6, 0, "abcd:1234:ffff::cdde", 64); - g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip6), ==, "abcd:1234:ffff::cdd1"); -} - -static void -test_write_ip6_wired_connection (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - NMSettingIPConfig *s_ip6; - const char *dns = "1::cafe"; - const char *address = "abcd::beef"; - const char *gw = "dcba::beef"; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Work Wired IP6", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NULL); - - /* Wired setting */ - - s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - /* IP4 setting */ - - s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, - NULL); - - /* IP6 setting */ - - s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - - g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, - NM_SETTING_IP_CONFIG_GATEWAY, gw, - NULL); - - /* Addresses */ - add_one_ip_address (s_ip6, address, 64); - - /* DNS servers */ - nm_setting_ip_config_add_dns (s_ip6, dns); - - /* DNS searches */ - nm_setting_ip_config_add_dns_search (s_ip6, "wallaceandgromit.com"); - - write_test_connection_and_reread (connection, FALSE); -} - -static void -test_read_wired_mac_case (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - const char *mac; - char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 }; - - NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses1*"); - NMTST_EXPECT_NM_INFO ("*ipv4.addresses*semicolon at the end*addresses2*"); - NMTST_EXPECT_NM_INFO ("*ipv6.routes*semicolon at the end*routes1*"); - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_Connection_MAC_Case"); - g_test_assert_expected_messages (); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wired Connection MAC Case"); - g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "4e80a56d-c99f-4aad-a6dd-b449bc398c57"); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac); - g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac_address, sizeof (expected_mac_address))); -} - -static void -test_read_mac_old_format (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWired *s_wired; - const char *mac; - char expected_mac[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 }; - char expected_cloned_mac[ETH_ALEN] = { 0x00, 0x16, 0xaa, 0xbb, 0xcc, 0xfe }; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_MAC_Old_Format"); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - - mac = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac); - g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, ETH_ALEN)); - - mac = nm_setting_wired_get_cloned_mac_address (s_wired); - g_assert (mac); - g_assert (nm_utils_hwaddr_matches (mac, -1, expected_cloned_mac, ETH_ALEN)); -} - -static void -test_read_mac_ib_old_format (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingInfiniband *s_ib; - const char *mac; - guint8 expected_mac[INFINIBAND_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, - 0x90 }; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_MAC_IB_Old_Format"); - - s_ib = nm_connection_get_setting_infiniband (connection); - g_assert (s_ib); - - /* MAC address */ - mac = nm_setting_infiniband_get_mac_address (s_ib); - g_assert (mac); - g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, sizeof (expected_mac))); -} - -static void -test_read_valid_wireless_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wireless; - NMSettingIPConfig *s_ip4; - const char *bssid; - const guint8 expected_bssid[ETH_ALEN] = { 0x00, 0x1a, 0x33, 0x44, 0x99, 0x82 }; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wireless_Connection"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Test Wireless Connection"); - g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "2f962388-e5f3-45af-a62c-ac220b8f7baa"); - g_assert_cmpuint (nm_setting_connection_get_timestamp (s_con), ==, 1226604314); - g_assert (nm_setting_connection_get_autoconnect (s_con) == FALSE); - - s_wireless = nm_connection_get_setting_wireless (connection); - g_assert (s_wireless); - bssid = nm_setting_wireless_get_bssid (s_wireless); - g_assert (bssid); - g_assert (nm_utils_hwaddr_matches (bssid, -1, expected_bssid, sizeof (expected_bssid))); - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); -} - -static void -test_write_wireless_connection (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wireless; - NMSettingIPConfig *s_ip4; - NMSettingIPConfig *s_ip6; - const char *bssid = "aa:b9:a1:74:55:44"; - GBytes *ssid; - unsigned char tmpssid[] = { 0x31, 0x33, 0x33, 0x37 }; - guint64 timestamp = 0x12344433L; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Work Wireless", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, - NM_SETTING_CONNECTION_TIMESTAMP, timestamp, - NULL); - - /* Wireless setting */ - - s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_wireless)); - - ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); - - g_object_set (s_wireless, - NM_SETTING_WIRELESS_BSSID, bssid, - NM_SETTING_WIRELESS_SSID, ssid, - NM_SETTING_WIRED_MTU, 1000, - NULL); - - g_bytes_unref (ssid); - - /* IP4 setting */ - - s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NULL); - - /* IP6 setting */ - - s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - - g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NULL); - - write_test_connection_and_reread (connection, FALSE); -} - -static void -test_read_string_ssid (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWireless *s_wireless; - GBytes *ssid; - const guint8 *ssid_data; - gsize ssid_len; - const char *expected_ssid = "blah blah ssid 1234"; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_String_SSID"); - - s_wireless = nm_connection_get_setting_wireless (connection); - g_assert (s_wireless); - - ssid = nm_setting_wireless_get_ssid (s_wireless); - g_assert (ssid); - - ssid_data = g_bytes_get_data (ssid, &ssid_len); - g_assert_cmpmem (ssid_data, ssid_len, expected_ssid, strlen (expected_ssid)); -} - -static void -test_write_string_ssid (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wireless; - NMSettingIPConfig *s_ip4; - char *tmp; - gs_free char *testfile = NULL; - GBytes *ssid; - unsigned char tmpssid[] = { 65, 49, 50, 51, 32, 46, 92, 46, 36, 37, 126, 93 }; - gs_unref_keyfile GKeyFile *keyfile = NULL; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "String SSID Test", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, - NULL); - - /* Wireless setting */ - - s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_wireless)); - - ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); - g_object_set (s_wireless, NM_SETTING_WIRELESS_SSID, ssid, NULL); - g_bytes_unref (ssid); - - /* IP4 setting */ - - s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NULL); - - write_test_connection (connection, &testfile); - - /* Ensure the SSID was written out as a string */ - keyfile = keyfile_load_from_file (testfile); - tmp = g_key_file_get_string (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, NULL); - g_assert (tmp); - g_assert_cmpmem (tmp, strlen (tmp), tmpssid, sizeof (tmpssid)); - g_free (tmp); - - assert_reread_and_unlink (connection, TRUE, testfile); -} - -static void -test_read_intlist_ssid (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWireless *s_wifi; - GBytes *ssid; - const guint8 *ssid_data; - gsize ssid_len; - const char *expected_ssid = "blah1234"; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Intlist_SSID"); - - s_wifi = nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - - ssid = nm_setting_wireless_get_ssid (s_wifi); - g_assert (ssid != NULL); - ssid_data = g_bytes_get_data (ssid, &ssid_len); - g_assert_cmpmem (ssid_data, ssid_len, expected_ssid, strlen (expected_ssid)); -} - -static void -test_write_intlist_ssid (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wifi; - NMSettingIPConfig *s_ip4; - gs_free char *testfile = NULL; - GBytes *ssid; - unsigned char tmpssid[] = { 65, 49, 50, 51, 0, 50, 50 }; - gs_free_error GError *error = NULL; - gs_unref_keyfile GKeyFile *keyfile = NULL; - int *intlist; - gsize len = 0, i; - - connection = nm_simple_connection_new (); - g_assert (connection); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - g_assert (s_con); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Intlist SSID Test", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, - NULL); - - /* Wireless setting */ - s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); - g_assert (s_wifi); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); - g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL); - g_bytes_unref (ssid); - - /* IP4 setting */ - s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - g_assert (s_ip4); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - - write_test_connection (connection, &testfile); - - /* Ensure the SSID was written out as an int list */ - keyfile = keyfile_load_from_file (testfile); - - intlist = g_key_file_get_integer_list (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, &len, &error); - g_assert_no_error (error); - g_assert (intlist); - g_assert_cmpint (len, ==, sizeof (tmpssid)); - - for (i = 0; i < len; i++) - g_assert_cmpint (intlist[i], ==, tmpssid[i]); - g_free (intlist); - - assert_reread_and_unlink (connection, TRUE, testfile); -} - -static void -test_read_intlike_ssid (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWireless *s_wifi; - GBytes *ssid; - const char *expected_ssid = "101"; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID"); - - s_wifi = nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - - ssid = nm_setting_wireless_get_ssid (s_wifi); - g_assert (ssid); - g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid))); -} - -static void -test_read_intlike_ssid_2 (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWireless *s_wifi; - GBytes *ssid; - const char *expected_ssid = "11;12;13;"; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID_2"); - - s_wifi = nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - - ssid = nm_setting_wireless_get_ssid (s_wifi); - g_assert (ssid); - g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid))); -} - -static void -test_write_intlike_ssid (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wifi; - NMSettingIPConfig *s_ip4; - gs_free char *testfile = NULL; - GBytes *ssid; - unsigned char tmpssid[] = { 49, 48, 49 }; - gs_free_error GError *error = NULL; - gs_unref_keyfile GKeyFile *keyfile = NULL; - char *tmp; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - g_assert (s_con); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Intlike SSID Test", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, - NULL); - - /* Wireless setting */ - s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); - g_assert (s_wifi); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); - g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL); - g_bytes_unref (ssid); - - /* IP4 setting */ - s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - g_assert (s_ip4); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - - write_test_connection (connection, &testfile); - - /* Ensure the SSID was written out as a plain "101" */ - keyfile = keyfile_load_from_file (testfile); - - tmp = g_key_file_get_string (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, &error); - g_assert_no_error (error); - g_assert (tmp); - g_assert_cmpstr (tmp, ==, "101"); - g_free (tmp); - - assert_reread_and_unlink (connection, TRUE, testfile); -} - -static void -test_write_intlike_ssid_2 (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wifi; - NMSettingIPConfig *s_ip4; - gs_free char *testfile = NULL; - GBytes *ssid; - unsigned char tmpssid[] = { 49, 49, 59, 49, 50, 59, 49, 51, 59}; - gs_free_error GError *error = NULL; - gs_unref_keyfile GKeyFile *keyfile = NULL; - char *tmp; - - connection = nm_simple_connection_new (); - g_assert (connection); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - g_assert (s_con); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Intlike SSID Test 2", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, - NULL); - - /* Wireless setting */ - s_wifi = NM_SETTING_WIRELESS (nm_setting_wireless_new ()); - g_assert (s_wifi); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); - g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NULL); - g_bytes_unref (ssid); - - /* IP4 setting */ - s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - g_assert (s_ip4); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - - write_test_connection (connection, &testfile); - - /* Ensure the SSID was written out as a plain "11;12;13;" */ - keyfile = keyfile_load_from_file (testfile); - - tmp = g_key_file_get_string (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, &error); - g_assert_no_error (error); - g_assert (tmp); - g_assert_cmpstr (tmp, ==, "11\\;12\\;13\\;"); - g_free (tmp); - - assert_reread_and_unlink (connection, TRUE, testfile); -} - -static void -test_read_bt_dun_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingBluetooth *s_bluetooth; - NMSettingSerial *s_serial; - NMSettingGsm *s_gsm; - const char *bdaddr; - const guint8 expected_bdaddr[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/ATT_Data_Connect_BT"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "AT&T Data Connect BT"); - g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, "089130ab-ce28-46e4-ad77-d44869b03d19"); - - s_bluetooth = nm_connection_get_setting_bluetooth (connection); - g_assert (s_bluetooth); - bdaddr = nm_setting_bluetooth_get_bdaddr (s_bluetooth); - g_assert (bdaddr); - g_assert (nm_utils_hwaddr_matches (bdaddr, -1, expected_bdaddr, sizeof (expected_bdaddr))); - g_assert_cmpstr (nm_setting_bluetooth_get_connection_type (s_bluetooth), ==, NM_SETTING_BLUETOOTH_TYPE_DUN); - - s_gsm = nm_connection_get_setting_gsm (connection); - g_assert (s_gsm); - g_assert_cmpstr (nm_setting_gsm_get_apn (s_gsm), ==, "ISP.CINGULAR"); - g_assert_cmpstr (nm_setting_gsm_get_username (s_gsm), ==, "ISP@CINGULARGPRS.COM"); - g_assert_cmpstr (nm_setting_gsm_get_password (s_gsm), ==, "CINGULAR1"); - - s_serial = nm_connection_get_setting_serial (connection); - g_assert (s_serial); - g_assert (nm_setting_serial_get_parity (s_serial) == NM_SETTING_SERIAL_PARITY_ODD); -} - -static void -test_write_bt_dun_connection (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingBluetooth *s_bt; - NMSettingIPConfig *s_ip4; - NMSettingGsm *s_gsm; - const char *bdaddr = "aa:b9:a1:74:55:44"; - guint64 timestamp = 0x12344433L; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "T-Mobile Funkadelic", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME, - NM_SETTING_CONNECTION_TIMESTAMP, timestamp, - NULL); - - /* Bluetooth setting */ - - s_bt = NM_SETTING_BLUETOOTH (nm_setting_bluetooth_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_bt)); - - g_object_set (s_bt, - NM_SETTING_BLUETOOTH_BDADDR, bdaddr, - NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_DUN, - NULL); - - /* IP4 setting */ - - s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NULL); - - /* GSM setting */ - s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_gsm)); - - g_object_set (s_gsm, - NM_SETTING_GSM_APN, "internet2.voicestream.com", - NM_SETTING_GSM_USERNAME, "george.clinton", - NM_SETTING_GSM_PASSWORD, "parliament", - NULL); - - write_test_connection_and_reread (connection, TRUE); -} - -static void -test_read_gsm_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingSerial *s_serial; - NMSettingGsm *s_gsm; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/ATT_Data_Connect_Plain"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "AT&T Data Connect"); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_GSM_SETTING_NAME); - - /* Plain GSM, so no BT setting expected */ - g_assert (nm_connection_get_setting_bluetooth (connection) == NULL); - - s_gsm = nm_connection_get_setting_gsm (connection); - g_assert (s_gsm); - g_assert_cmpstr (nm_setting_gsm_get_apn (s_gsm), ==, "ISP.CINGULAR"); - g_assert_cmpstr (nm_setting_gsm_get_username (s_gsm), ==, "ISP@CINGULARGPRS.COM"); - g_assert_cmpstr (nm_setting_gsm_get_password (s_gsm), ==, "CINGULAR1"); - g_assert_cmpstr (nm_setting_gsm_get_network_id (s_gsm), ==, "24005"); - g_assert_cmpstr (nm_setting_gsm_get_pin (s_gsm), ==, "2345"); - g_assert_cmpstr (nm_setting_gsm_get_device_id (s_gsm), ==, "da812de91eec16620b06cd0ca5cbc7ea25245222"); - g_assert_cmpstr (nm_setting_gsm_get_sim_id (s_gsm), ==, "89148000000060671234"); - g_assert_cmpstr (nm_setting_gsm_get_sim_operator_id (s_gsm), ==, "310260"); - - s_serial = nm_connection_get_setting_serial (connection); - g_assert (s_serial); - g_assert_cmpint (nm_setting_serial_get_parity (s_serial), ==, NM_SETTING_SERIAL_PARITY_ODD); -} - -static void -test_write_gsm_connection (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingIPConfig *s_ip4; - NMSettingGsm *s_gsm; - guint64 timestamp = 0x12344433L; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "T-Mobile Funkadelic 2", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_CONNECTION_TIMESTAMP, timestamp, - NULL); - - /* IP4 setting */ - - s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NULL); - - /* GSM setting */ - s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_gsm)); - - g_object_set (s_gsm, - NM_SETTING_GSM_APN, "internet2.voicestream.com", - NM_SETTING_GSM_USERNAME, "george.clinton.again", - NM_SETTING_GSM_PASSWORD, "parliament2", - NM_SETTING_GSM_PIN, "123456", - NM_SETTING_GSM_NETWORK_ID, "254098", - NM_SETTING_GSM_HOME_ONLY, TRUE, - NM_SETTING_GSM_DEVICE_ID, "da812de91eec16620b06cd0ca5cbc7ea25245222", - NM_SETTING_GSM_SIM_ID, "89148000000060671234", - NM_SETTING_GSM_SIM_OPERATOR_ID, "310260", - NULL); - - write_test_connection_and_reread (connection, TRUE); -} - -static void -test_read_wired_8021x_tls_blob_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWired *s_wired; - NMSetting8021x *s_8021x; - const char *tmp; - GBytes *blob; - - NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); - NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Blob"); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired != NULL); - - s_8021x = nm_connection_get_setting_802_1x (connection); - g_assert (s_8021x != NULL); - - g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); - tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); - g_assert (g_strcmp0 (tmp, "tls") == 0); - - tmp = nm_setting_802_1x_get_identity (s_8021x); - g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); - - tmp = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (g_strcmp0 (tmp, "12345testing") == 0); - - g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_BLOB); - - /* Make sure it's not a path, since it's a blob */ - NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)); - tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); - g_test_assert_expected_messages (); - g_assert (tmp == NULL); - - /* Validate the path */ - blob = nm_setting_802_1x_get_ca_cert_blob (s_8021x); - g_assert (blob != NULL); - g_assert_cmpint (g_bytes_get_size (blob), ==, 568); - - tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); - g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem"); - - tmp = nm_setting_802_1x_get_private_key_path (s_8021x); - g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem"); -} - -static void -test_read_wired_8021x_tls_bad_path_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWired *s_wired; - NMSetting8021x *s_8021x; - const char *tmp; - char *tmp2; - - NMTST_EXPECT_NM_WARN ("*does not exist*"); - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Path_Missing"); - g_test_assert_expected_messages (); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired != NULL); - - s_8021x = nm_connection_get_setting_802_1x (connection); - g_assert (s_8021x != NULL); - - g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); - tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); - g_assert (g_strcmp0 (tmp, "tls") == 0); - - tmp = nm_setting_802_1x_get_identity (s_8021x); - g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); - - tmp = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (g_strcmp0 (tmp, "12345testing") == 0); - - g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH); - - tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); - g_assert_cmpstr (tmp, ==, "/some/random/cert/path.pem"); - - tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-key-and-cert.pem"); - - tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); - g_assert_cmpstr (tmp, ==, tmp2); - - tmp = nm_setting_802_1x_get_private_key_path (s_8021x); - g_assert_cmpstr (tmp, ==, tmp2); - - g_free (tmp2); -} - -static void -test_read_wired_8021x_tls_old_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWired *s_wired; - NMSetting8021x *s_8021x; - const char *tmp; - - NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*"); - NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); - NMTST_EXPECT_NM_WARN ("keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_Old"); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired != NULL); - - s_8021x = nm_connection_get_setting_802_1x (connection); - g_assert (s_8021x != NULL); - - g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); - tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); - g_assert (g_strcmp0 (tmp, "tls") == 0); - - tmp = nm_setting_802_1x_get_identity (s_8021x); - g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); - - tmp = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (g_strcmp0 (tmp, "12345testing") == 0); - - tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); - g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem") == 0); - - tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); - g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0); - - tmp = nm_setting_802_1x_get_private_key_path (s_8021x); - g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0); -} - -static void -test_read_wired_8021x_tls_new_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWired *s_wired; - NMSetting8021x *s_8021x; - const char *tmp; - char *tmp2; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Wired_TLS_New"); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired != NULL); - - s_8021x = nm_connection_get_setting_802_1x (connection); - g_assert (s_8021x != NULL); - - g_assert (nm_setting_802_1x_get_num_eap_methods (s_8021x) == 1); - tmp = nm_setting_802_1x_get_eap_method (s_8021x, 0); - g_assert (g_strcmp0 (tmp, "tls") == 0); - - tmp = nm_setting_802_1x_get_identity (s_8021x); - g_assert (g_strcmp0 (tmp, "Bill Smith") == 0); - - tmp = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (g_strcmp0 (tmp, "12345testing") == 0); - - tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-ca-cert.pem"); - tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); - g_assert_cmpstr (tmp, ==, tmp2); - g_free (tmp2); - - tmp2 = g_strdup_printf (TEST_KEYFILES_DIR "/test-key-and-cert.pem"); - - tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); - g_assert_cmpstr (tmp, ==, tmp2); - - tmp = nm_setting_802_1x_get_private_key_path (s_8021x); - g_assert_cmpstr (tmp, ==, tmp2); - - g_free (tmp2); -} - -#define TEST_WIRED_TLS_CA_CERT TEST_KEYFILES_DIR"/test-ca-cert.pem" -#define TEST_WIRED_TLS_CLIENT_CERT TEST_KEYFILES_DIR"/test-key-and-cert.pem" -#define TEST_WIRED_TLS_PRIVKEY TEST_KEYFILES_DIR"/test-key-and-cert.pem" - -static NMConnection * -create_wired_tls_connection (NMSetting8021xCKScheme scheme) -{ - NMTST_UUID_INIT (uuid); - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingIPConfig *s_ip4; - NMSetting *s_wired; - NMSetting8021x *s_8021x; - gboolean success; - gs_free_error GError *error = NULL; - - connection = nm_simple_connection_new (); - g_assert (connection != NULL); - - /* Connection setting */ - s_con = (NMSettingConnection *) nm_setting_connection_new (); - g_assert (s_con); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Wired Really Secure TLS", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NULL); - - /* IP4 setting */ - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - g_assert (s_ip4); - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - - /* Wired setting */ - s_wired = nm_setting_wired_new (); - g_assert (s_wired); - nm_connection_add_setting (connection, s_wired); - - /* 802.1x setting */ - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - nm_connection_add_setting (connection, NM_SETTING (s_8021x)); - - nm_setting_802_1x_add_eap_method (s_8021x, "tls"); - g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL); - - success = nm_setting_802_1x_set_ca_cert (s_8021x, - TEST_WIRED_TLS_CA_CERT, - scheme, - NULL, - &error); - g_assert_no_error (error); - g_assert (success); - - success = nm_setting_802_1x_set_client_cert (s_8021x, - TEST_WIRED_TLS_CLIENT_CERT, - scheme, - NULL, - &error); - g_assert_no_error (error); - g_assert (success); - - success = nm_setting_802_1x_set_private_key (s_8021x, - TEST_WIRED_TLS_PRIVKEY, - "test1", - scheme, - NULL, - &error); - g_assert_no_error (error); - g_assert (success); - - return connection; -} - -static char * -get_path (const char *file, gboolean relative) -{ - return relative ? g_path_get_basename (file) : g_strdup (file); -} - -static void -test_write_wired_8021x_tls_connection_path (void) -{ - gs_unref_object NMConnection *connection = NULL; - gs_unref_object NMConnection *reread = NULL; - char *tmp, *tmp2; - gboolean success; - gs_free char *testfile = NULL; - gs_unref_keyfile GKeyFile *keyfile = NULL; - gboolean relative = FALSE; - gboolean reread_same = FALSE; - - connection = create_wired_tls_connection (NM_SETTING_802_1X_CK_SCHEME_PATH); - g_assert (connection != NULL); - - write_test_connection_reread (connection, &testfile, &reread, &reread_same); - nmtst_assert_connection_verifies_without_normalization (reread); - _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); - _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); - _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY); - assert_reread_same (connection, reread); - g_clear_object (&reread); - - /* Read the connection back in and compare it to the one we just wrote out */ - reread = keyfile_read_connection_from_file (testfile); - - success = nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT); - if (!reread) { - g_warning ("Written and re-read connection weren't the same"); - g_assert (success); - } - - /* Ensure the cert and key values are properly written out */ - keyfile = keyfile_load_from_file (testfile); - - /* Depending on whether this test is being run from 'make check' or - * 'make distcheck' we might be using relative paths (check) or - * absolute ones (distcheck). - */ - tmp2 = g_path_get_dirname (testfile); - if (g_strcmp0 (tmp2, TEST_KEYFILES_DIR) == 0) - relative = TRUE; - g_free (tmp2); - - /* CA cert */ - tmp = g_key_file_get_string (keyfile, - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CA_CERT, - NULL); - tmp2 = get_path (TEST_WIRED_TLS_CA_CERT, relative); - g_assert_cmpstr (tmp, ==, tmp2); - g_free (tmp2); - g_free (tmp); - - /* Client cert */ - tmp = g_key_file_get_string (keyfile, - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CLIENT_CERT, - NULL); - tmp2 = get_path (TEST_WIRED_TLS_CLIENT_CERT, relative); - g_assert_cmpstr (tmp, ==, tmp2); - g_free (tmp2); - g_free (tmp); - - /* Private key */ - tmp = g_key_file_get_string (keyfile, - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PRIVATE_KEY, - NULL); - tmp2 = get_path (TEST_WIRED_TLS_PRIVKEY, relative); - g_assert_cmpstr (tmp, ==, tmp2); - g_free (tmp2); - g_free (tmp); - - unlink (testfile); -} - -static void -test_write_wired_8021x_tls_connection_blob (void) -{ - gs_unref_object NMConnection *connection = NULL; - gs_unref_object NMConnection *reread = NULL; - NMSettingConnection *s_con; - NMSetting8021x *s_8021x; - gs_free char *testfile = NULL; - char *new_ca_cert; - char *new_client_cert; - char *new_priv_key; - const char *uuid; - gboolean reread_same = FALSE; - GBytes *password_raw; - -#define PASSWORD_RAW "password-raw\0test" - - connection = create_wired_tls_connection (NM_SETTING_802_1X_CK_SCHEME_BLOB); - g_assert (connection != NULL); - - s_8021x = nm_connection_get_setting_802_1x (connection); - g_assert (s_8021x); - - password_raw = g_bytes_new (PASSWORD_RAW, NM_STRLEN (PASSWORD_RAW)); - g_object_set (s_8021x, - NM_SETTING_802_1X_PASSWORD_RAW, - password_raw, - NULL); - g_bytes_unref (password_raw); - - write_test_connection_reread (connection, &testfile, &reread, &reread_same); - nmtst_assert_connection_verifies_without_normalization (reread); - _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); - _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); - _setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY); - assert_reread_same (connection, reread); - g_clear_object (&reread); - - /* Check that the new certs got written out */ - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - uuid = nm_setting_connection_get_uuid (s_con); - g_assert (uuid); - - new_ca_cert = g_strdup_printf ("%s/%s-ca-cert.pem", TEST_SCRATCH_DIR, uuid); - g_assert (new_ca_cert); - g_assert (g_file_test (new_ca_cert, G_FILE_TEST_EXISTS)); - - new_client_cert = g_strdup_printf ("%s/%s-client-cert.pem", TEST_SCRATCH_DIR, uuid); - g_assert (new_client_cert); - g_assert (g_file_test (new_client_cert, G_FILE_TEST_EXISTS)); - - new_priv_key = g_strdup_printf ("%s/%s-private-key.pem", TEST_SCRATCH_DIR, uuid); - g_assert (new_priv_key); - g_assert (g_file_test (new_priv_key, G_FILE_TEST_EXISTS)); - - /* Read the connection back in and compare it to the one we just wrote out */ - reread = keyfile_read_connection_from_file (testfile); - - /* Ensure the re-read connection's certificates use the path scheme */ - s_8021x = nm_connection_get_setting_802_1x (reread); - g_assert (s_8021x); - g_assert (nm_setting_802_1x_get_ca_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH); - g_assert (nm_setting_802_1x_get_client_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH); - g_assert (nm_setting_802_1x_get_private_key_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH); - - password_raw = nm_setting_802_1x_get_password_raw (s_8021x); - g_assert (password_raw); - g_assert (nm_utils_gbytes_equal_mem (password_raw, PASSWORD_RAW, NM_STRLEN (PASSWORD_RAW))); - - unlink (testfile); - - /* Clean up written certs */ - unlink (new_ca_cert); - g_free (new_ca_cert); - - unlink (new_client_cert); - g_free (new_client_cert); - - unlink (new_priv_key); - g_free (new_priv_key); -} - -static void -test_read_dcb_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_dcb_connection"); -} - -static void -test_read_infiniband_connection (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingInfiniband *s_ib; - const char *mac; - guint8 expected_mac[INFINIBAND_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, - 0x90 }; - const char *expected_id = "Test InfiniBand Connection"; - const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57"; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_InfiniBand_Connection"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id); - g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, expected_uuid); - - s_ib = nm_connection_get_setting_infiniband (connection); - g_assert (s_ib); - - mac = nm_setting_infiniband_get_mac_address (s_ib); - g_assert (mac); - g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, sizeof (expected_mac))); -} - -static void -test_write_infiniband_connection (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingInfiniband *s_ib; - NMSettingIPConfig *s_ip4; - NMSettingIPConfig *s_ip6; - const char *mac = "99:88:77:66:55:44:ab:bc:cd:de:ef:f0:0a:1b:2c:3d:4e:5f:6f:ba"; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = (NMSettingConnection *) nm_setting_connection_new (); - g_assert (s_con); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Work InfiniBand", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_INFINIBAND_SETTING_NAME, - NULL); - - /* InfiniBand setting */ - s_ib = (NMSettingInfiniband *) nm_setting_infiniband_new (); - g_assert (s_ib); - nm_connection_add_setting (connection, NM_SETTING (s_ib)); - - g_object_set (s_ib, - NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, - NM_SETTING_INFINIBAND_MTU, 900, - NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", - NULL); - - /* IP4 setting */ - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - g_assert (s_ip4); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - - /* IP6 setting */ - s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); - g_assert (s_ip6); - nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); - - write_test_connection_and_reread (connection, FALSE); -} - -static void -test_read_bridge_main (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingIPConfig *s_ip4; - NMSettingBridge *s_bridge; - const char *expected_id = "Test Bridge Main"; - const char *expected_uuid = "8f061643-fe41-4d4c-a8d9-097d26e2ad3a"; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Bridge_Main"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id); - g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, expected_uuid); - g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "br0"); - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - - s_bridge = nm_connection_get_setting_bridge (connection); - g_assert (s_bridge); - g_assert_cmpuint (nm_setting_bridge_get_forward_delay (s_bridge), ==, 2); - g_assert_cmpuint (nm_setting_bridge_get_stp (s_bridge), ==, TRUE); - g_assert_cmpuint (nm_setting_bridge_get_priority (s_bridge), ==, 32744); - g_assert_cmpuint (nm_setting_bridge_get_hello_time (s_bridge), ==, 7); - g_assert_cmpuint (nm_setting_bridge_get_max_age (s_bridge), ==, 39); - g_assert_cmpuint (nm_setting_bridge_get_ageing_time (s_bridge), ==, 235352); - g_assert_cmpuint (nm_setting_bridge_get_multicast_snooping (s_bridge), ==, FALSE); -} - -static void -test_write_bridge_main (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingBridge *s_bridge; - NMSettingIPConfig *s_ip4; - NMSettingIPConfig *s_ip6; - - connection = nm_simple_connection_new (); - g_assert (connection); - - /* Connection setting */ - s_con = (NMSettingConnection *) nm_setting_connection_new (); - g_assert (s_con); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Write Bridge Main", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, - NM_SETTING_CONNECTION_INTERFACE_NAME, "br0", - NULL); - - /* Bridge setting */ - s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); - g_assert (s_bridge); - nm_connection_add_setting (connection, NM_SETTING (s_bridge)); - - /* IP4 setting */ - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - g_assert (s_ip4); - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - g_object_set (s_ip4, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, - NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, - NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1", - NULL); - - add_one_ip_address (s_ip4, "1.2.3.4", 24); - - /* IP6 setting */ - s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); - g_assert (s_ip6); - nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); - - write_test_connection_and_reread (connection, FALSE); -} - -static void -test_read_bridge_component (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingBridgePort *s_port; - NMSettingWired *s_wired; - const char *mac; - guint8 expected_mac[ETH_ALEN] = { 0x00, 0x22, 0x15, 0x59, 0x62, 0x97 }; - const char *expected_id = "Test Bridge Component"; - const char *expected_uuid = "d7b4f96c-c45e-4298-bef8-f48574f8c1c0"; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_Bridge_Component"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id); - g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, expected_uuid); - g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, "br0"); - g_assert (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BRIDGE_SETTING_NAME)); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac); - g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, sizeof (expected_mac))); - - s_port = nm_connection_get_setting_bridge_port (connection); - g_assert (s_port); - g_assert (nm_setting_bridge_port_get_hairpin_mode (s_port)); - g_assert_cmpuint (nm_setting_bridge_port_get_priority (s_port), ==, 28); - g_assert_cmpuint (nm_setting_bridge_port_get_path_cost (s_port), ==, 100); -} - -static void -test_write_bridge_component (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingBridgePort *s_port; - NMSettingWired *s_wired; - const char *mac = "99:88:77:66:55:44"; - - connection = nm_simple_connection_new (); - g_assert (connection); - - /* Connection setting */ - s_con = (NMSettingConnection *) nm_setting_connection_new (); - g_assert (s_con); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Write Bridge Component", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_CONNECTION_MASTER, "br0", - NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, - NULL); - - /* Wired setting */ - s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); - g_assert (s_wired); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - g_object_set (s_wired, - NM_SETTING_WIRED_MAC_ADDRESS, mac, - NM_SETTING_WIRED_MTU, 1300, - NULL); - - /* BridgePort setting */ - s_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); - g_assert (s_port); - nm_connection_add_setting (connection, NM_SETTING (s_port)); - - g_object_set (s_port, - NM_SETTING_BRIDGE_PORT_PRIORITY, 3, - NM_SETTING_BRIDGE_PORT_PATH_COST, 99, - NULL); - - write_test_connection_and_reread (connection, FALSE); -} - -static void -test_read_new_wired_group_name (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWired *s_wired; - const char *mac; - guint8 expected_mac[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_New_Wired_Group_Name"); - - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 1400); - - mac = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac); - g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac, sizeof (expected_mac))); -} - -static void -test_write_new_wired_group_name (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - gs_unref_keyfile GKeyFile *kf = NULL; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - gs_free char *testfile = NULL; - gs_free_error GError *error = NULL; - char *s; - int mtu; - - connection = nm_simple_connection_new (); - g_assert (connection); - - /* Connection setting */ - s_con = (NMSettingConnection *) nm_setting_connection_new (); - g_assert (s_con); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Write Wired New Group Name", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NULL); - - /* Wired setting */ - s_wired = (NMSettingWired *) nm_setting_wired_new (); - g_assert (s_wired); - g_object_set (s_wired, NM_SETTING_WIRED_MTU, 1400, NULL); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - write_test_connection (connection, &testfile); - - assert_reread (connection, TRUE, testfile); - - /* Look at the keyfile itself to ensure we wrote out the new group names and type */ - kf = keyfile_load_from_file (testfile); - - s = g_key_file_get_string (kf, NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE, &error); - g_assert_no_error (error); - g_assert_cmpstr (s, ==, "ethernet"); - g_free (s); - - mtu = g_key_file_get_integer (kf, "ethernet", NM_SETTING_WIRED_MTU, &error); - g_assert_no_error (error); - g_assert_cmpint (mtu, ==, 1400); - - unlink (testfile); -} - -static void -test_read_new_wireless_group_names (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingWireless *s_wifi; - NMSettingWirelessSecurity *s_wsec; - GBytes *ssid; - const char *expected_ssid = "foobar"; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_New_Wireless_Group_Names"); - - s_wifi = nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - - ssid = nm_setting_wireless_get_ssid (s_wifi); - g_assert (ssid); - g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid))); - - g_assert_cmpstr (nm_setting_wireless_get_mode (s_wifi), ==, NM_SETTING_WIRELESS_MODE_INFRA); - - s_wsec = nm_connection_get_setting_wireless_security (connection); - g_assert (s_wsec); - g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "wpa-psk"); - g_assert_cmpstr (nm_setting_wireless_security_get_psk (s_wsec), ==, "s3cu4e passphrase"); -} - -static void -test_write_new_wireless_group_names (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - gs_unref_keyfile GKeyFile *kf = NULL; - NMSettingConnection *s_con; - NMSettingWireless *s_wifi; - NMSettingWirelessSecurity *s_wsec; - GBytes *ssid; - unsigned char tmpssid[] = { 0x31, 0x33, 0x33, 0x37 }; - const char *expected_psk = "asdfasdfasdfa12315"; - gs_free char *testfile = NULL; - gs_free_error GError *error = NULL; - char *s; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = (NMSettingConnection *) nm_setting_connection_new (); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Write New Wireless Group Names", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, - NULL); - - /* Wi-Fi setting */ - s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - ssid = g_bytes_new (tmpssid, sizeof (tmpssid)); - g_object_set (s_wifi, - NM_SETTING_WIRELESS_SSID, ssid, - NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, - NULL); - g_bytes_unref (ssid); - - /* Wi-Fi security setting */ - s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - g_object_set (s_wsec, - NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", - NM_SETTING_WIRELESS_SECURITY_PSK, expected_psk, - NULL); - - write_test_connection (connection, &testfile); - - assert_reread (connection, TRUE, testfile); - - /* Look at the keyfile itself to ensure we wrote out the new group names and type */ - kf = keyfile_load_from_file (testfile); - - s = g_key_file_get_string (kf, NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE, &error); - g_assert_no_error (error); - g_assert_cmpstr (s, ==, "wifi"); - g_free (s); - - s = g_key_file_get_string (kf, "wifi", NM_SETTING_WIRELESS_MODE, &error); - g_assert_no_error (error); - g_assert_cmpstr (s, ==, NM_SETTING_WIRELESS_MODE_INFRA); - g_free (s); - - s = g_key_file_get_string (kf, "wifi-security", NM_SETTING_WIRELESS_SECURITY_PSK, &error); - g_assert_no_error (error); - g_assert_cmpstr (s, ==, expected_psk); - g_free (s); - - unlink (testfile); -} - -static void -test_read_missing_vlan_setting (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingVlan *s_vlan; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Missing_Vlan_Setting"); - - s_vlan = nm_connection_get_setting_vlan (connection); - g_assert (s_vlan); - g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 0); - g_assert_cmpint (nm_setting_vlan_get_flags (s_vlan), ==, NM_VLAN_FLAG_REORDER_HEADERS); -} - -static void -test_read_missing_vlan_flags (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingVlan *s_vlan; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Missing_Vlan_Flags"); - - s_vlan = nm_connection_get_setting_vlan (connection); - g_assert (s_vlan); - - g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 444); - g_assert_cmpstr (nm_setting_vlan_get_parent (s_vlan), ==, "em1"); - g_assert_cmpint (nm_setting_vlan_get_flags (s_vlan), ==, NM_VLAN_FLAG_REORDER_HEADERS); -} - -static void -test_read_missing_id_uuid (void) -{ - gs_unref_object NMConnection *connection = NULL; - gs_free char *expected_uuid = NULL; - const char *FILENAME = TEST_KEYFILES_DIR"/Test_Missing_ID_UUID"; - - expected_uuid = _nm_utils_uuid_generate_from_strings ("keyfile", FILENAME, NULL); - - connection = keyfile_read_connection_from_file (FILENAME); - - g_assert_cmpstr (nm_connection_get_id (connection), ==, "Test_Missing_ID_UUID"); - g_assert_cmpstr (nm_connection_get_uuid (connection), ==, expected_uuid); -} - -static void -test_read_minimal (void) -{ - gs_unref_object NMConnection *connection = NULL; - gs_unref_object NMConnection *con_archetype = NULL; - NMSettingConnection *s_con; - - con_archetype = nmtst_create_minimal_connection ("Test_minimal_x", - "a15bd68f-c32b-40b8-8d27-49e472a85919", - NM_SETTING_WIRED_SETTING_NAME, - &s_con); - nmtst_connection_normalize (con_archetype); - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_1"); - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), - NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), - NULL); - nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); - g_clear_object (&connection); - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_2"); - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), - NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), - NULL); - nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); - g_clear_object (&connection); -} - -static void -test_read_minimal_slave (void) -{ - gs_unref_object NMConnection *connection = NULL; - gs_unref_object NMConnection *con_archetype = NULL; - NMSettingConnection *s_con; - - con_archetype = nmtst_create_minimal_connection ("Test_minimal_slave_x", - "a56b4ca5-7075-43d4-82c7-5d0cb15f7654", - NM_SETTING_WIRED_SETTING_NAME, - &s_con); - g_object_set (s_con, - NM_SETTING_CONNECTION_MASTER, "br0", - NM_SETTING_CONNECTION_SLAVE_TYPE, "bridge", - NULL); - nmtst_connection_normalize (con_archetype); - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_1"); - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), - NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), - NULL); - nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); - g_clear_object (&connection); - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_2"); - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), - NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), - NULL); - nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); - g_clear_object (&connection); - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_3"); - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), - NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), - NULL); - nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); - g_clear_object (&connection); - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_minimal_slave_4"); - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, nm_connection_get_id (connection), - NM_SETTING_CONNECTION_UUID, nm_connection_get_uuid (connection), - NULL); - nmtst_assert_connection_equals (con_archetype, FALSE, connection, FALSE); - g_clear_object (&connection); -} - -static void -test_read_enum_property (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingIPConfig *s_ip6; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Enum_Property"); - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - g_assert (s_ip6); - g_assert_cmpint (nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)), ==, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR); -} - -static void -test_write_enum_property (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip6; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Write Enum Property", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, - NULL); - - /* Wired setting */ - s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - - /* IP6 setting */ - s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_ip6)); - g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_IP6_PRIVACY, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, - NULL); - - nmtst_connection_normalize (connection); - - write_test_connection_and_reread (connection, FALSE); -} - -static void -test_read_flags_property (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingGsm *s_gsm; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR"/Test_Flags_Property"); - - s_gsm = nm_connection_get_setting_gsm (connection); - g_assert (s_gsm); - g_assert_cmpint (nm_setting_gsm_get_password_flags (s_gsm), ==, - NM_SETTING_SECRET_FLAG_AGENT_OWNED | NM_SETTING_SECRET_FLAG_NOT_REQUIRED); -} - -static void -test_write_flags_property (void) -{ - NMTST_UUID_INIT (uuid); - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSetting *s_gsm; - - connection = nm_simple_connection_new (); - - /* Connection setting */ - - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Write Flags Property", - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, - NULL); - - /* GSM setting */ - s_gsm = nm_setting_gsm_new (); - nm_connection_add_setting (connection, s_gsm); - g_object_set (s_gsm, - NM_SETTING_GSM_APN, "myapn", - NM_SETTING_GSM_USERNAME, "adfasdfasdf", - NM_SETTING_GSM_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_NOT_REQUIRED, - NULL); - - nmtst_connection_normalize (connection); - - write_test_connection_and_reread (connection, FALSE); -} - -/*****************************************************************************/ - -static void -test_read_tc_config (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSettingTCConfig *s_tc; - NMTCQdisc *qdisc1, *qdisc2; - NMTCAction *action1, *action2; - NMTCTfilter *tfilter1, *tfilter2; - - connection = keyfile_read_connection_from_file (TEST_KEYFILES_DIR "/Test_TC_Config"); - - s_tc = nm_connection_get_setting_tc_config (connection); - g_assert (s_tc); - - g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 2); - - qdisc1 = nm_setting_tc_config_get_qdisc (s_tc, 0); - g_assert (qdisc1); - g_assert (g_strcmp0 (nm_tc_qdisc_get_kind (qdisc1), "fq_codel") == 0); - g_assert (nm_tc_qdisc_get_handle (qdisc1) == TC_H_MAKE (0x1234 << 16, 0x0000)); - g_assert (nm_tc_qdisc_get_parent (qdisc1) == TC_H_ROOT); - - qdisc2 = nm_setting_tc_config_get_qdisc (s_tc, 1); - g_assert (qdisc2); - g_assert (g_strcmp0 (nm_tc_qdisc_get_kind (qdisc2), "ingress") == 0); - g_assert (nm_tc_qdisc_get_handle (qdisc2) == TC_H_MAKE (TC_H_INGRESS, 0)); - g_assert (nm_tc_qdisc_get_parent (qdisc2) == TC_H_INGRESS); - - g_assert (nm_setting_tc_config_get_num_tfilters (s_tc) == 2); - - tfilter1 = nm_setting_tc_config_get_tfilter (s_tc, 0); - g_assert (tfilter1); - g_assert (g_strcmp0 (nm_tc_tfilter_get_kind (tfilter1), "matchall") == 0); - g_assert (nm_tc_tfilter_get_handle (tfilter1) == TC_H_UNSPEC); - g_assert (nm_tc_tfilter_get_parent (tfilter1) == TC_H_MAKE (0x1234 << 16, 0x0000)); - - action1 = nm_tc_tfilter_get_action (tfilter1); - g_assert (action1); - g_assert (g_strcmp0 (nm_tc_action_get_kind (action1), "drop") == 0); - - tfilter2 = nm_setting_tc_config_get_tfilter (s_tc, 1); - g_assert (tfilter2); - g_assert (g_strcmp0 (nm_tc_tfilter_get_kind (tfilter2), "matchall") == 0); - g_assert (nm_tc_tfilter_get_handle (tfilter2) == TC_H_UNSPEC); - g_assert (nm_tc_tfilter_get_parent (tfilter2) == TC_H_MAKE (TC_H_INGRESS, 0)); - - action2 = nm_tc_tfilter_get_action (tfilter2); - g_assert (action2); - g_assert (g_strcmp0 (nm_tc_action_get_kind (action2), "simple") == 0); - g_assert (g_strcmp0 (g_variant_get_bytestring (nm_tc_action_get_attribute (action2, "sdata")), - "Hello") == 0); -} - -static void -test_write_tc_config (void) -{ - gs_unref_object NMConnection *connection = NULL; - NMSetting *s_tc; - NMTCQdisc *qdisc1, *qdisc2; - NMTCTfilter *tfilter1, *tfilter2; - NMTCAction *action; - GError *error = NULL; - - connection = nmtst_create_minimal_connection ("Test TC", - NULL, - NM_SETTING_WIRED_SETTING_NAME, - NULL); - s_tc = nm_setting_tc_config_new (); - - qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error); - nmtst_assert_success (qdisc1, error); - nm_tc_qdisc_set_handle (qdisc1, TC_H_MAKE (0x1234 << 16, 0x0000)); - nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc1); - - qdisc2 = nm_tc_qdisc_new ("ingress", TC_H_INGRESS, &error); - nmtst_assert_success (qdisc2, error); - nm_tc_qdisc_set_handle (qdisc2, TC_H_MAKE (TC_H_INGRESS, 0)); - nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc2); - - tfilter1 = nm_tc_tfilter_new ("matchall", - TC_H_MAKE (0x1234 << 16, 0x0000), - &error); - nmtst_assert_success (tfilter1, error); - action = nm_tc_action_new ("drop", &error); - nmtst_assert_success (action, error); - nm_tc_tfilter_set_action (tfilter1, action); - nm_tc_action_unref (action); - nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter1); - nm_tc_tfilter_unref (tfilter1); - - tfilter2 = nm_tc_tfilter_new ("matchall", - TC_H_MAKE (TC_H_INGRESS, 0), - &error); - nmtst_assert_success (tfilter2, error); - action = nm_tc_action_new ("simple", &error); - nmtst_assert_success (action, error); - nm_tc_action_set_attribute (action, "sdata", g_variant_new_bytestring ("Hello")); - nm_tc_tfilter_set_action (tfilter2, action); - nm_tc_action_unref (action); - nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter2); - nm_tc_tfilter_unref (tfilter2); - - nm_connection_add_setting (connection, s_tc); - - nmtst_connection_normalize (connection); - write_test_connection_and_reread (connection, FALSE); - - nm_tc_qdisc_unref (qdisc1); - nm_tc_qdisc_unref (qdisc2); -} - -/*****************************************************************************/ - -static void -_escape_filename (gboolean with_extension, const char *filename, gboolean would_be_ignored) -{ - gs_free char *esc = NULL; - - g_assert (filename && filename[0]); - - if (!!would_be_ignored != !!nm_keyfile_utils_ignore_filename (filename, with_extension)) { - if (would_be_ignored) - g_error ("We expect filename \"%s\" to be ignored, but it isn't", filename); - else - g_error ("We expect filename \"%s\" not to be ignored, but it is", filename); - } - - esc = nm_keyfile_utils_create_filename (filename, with_extension); - g_assert (esc && esc[0]); - g_assert (!strchr (esc, '/')); - - if (nm_keyfile_utils_ignore_filename (esc, with_extension)) - g_error ("Escaping filename \"%s\" yielded \"%s\", but this is ignored", filename, esc); -} - -static void -test_nm_keyfile_plugin_utils_escape_filename (void) -{ - _escape_filename (FALSE, "ab", FALSE); - _escape_filename (FALSE, ".vim-file.swp", TRUE); - _escape_filename (FALSE, ".vim-file.Swp", TRUE); - _escape_filename (FALSE, ".vim-file.SWP", TRUE); - _escape_filename (FALSE, ".vim-file.swpx", TRUE); - _escape_filename (FALSE, ".vim-file.Swpx", TRUE); - _escape_filename (FALSE, ".vim-file.SWPX", TRUE); - _escape_filename (FALSE, ".pem-file.pem", TRUE); - _escape_filename (FALSE, ".pem-file.Pem", TRUE); - _escape_filename (FALSE, ".pem-file.PEM", TRUE); - _escape_filename (FALSE, ".pem-file.der", TRUE); - _escape_filename (FALSE, ".pem-file.Der", TRUE); - _escape_filename (FALSE, ".mkstemp.ABCEDF", TRUE); - _escape_filename (FALSE, ".mkstemp.abcdef", TRUE); - _escape_filename (FALSE, ".mkstemp.123456", TRUE); - _escape_filename (FALSE, ".mkstemp.A23456", TRUE); - _escape_filename (FALSE, ".#emacs-locking", TRUE); - _escape_filename (FALSE, "file-with-tilde~", TRUE); - _escape_filename (FALSE, ".file-with-dot", TRUE); - _escape_filename (FALSE, "/some/path/with/trailing/slash/", TRUE); - _escape_filename (FALSE, "/some/path/without/trailing/slash", FALSE); - - _escape_filename (TRUE, "lala", TRUE); -} - -/*****************************************************************************/ - -static void -_assert_keyfile_loaded_uuid (const char *dirname, - const char *uuid, - const char *loaded_path, - gboolean allow_relative, - const char *exp_full_filename, - const char *exp_uuid, - const char *exp_symlink_target, - const char *exp_loaded_path) -{ - gs_free char *full_filename = NULL; - gs_free char *symlink_target = NULL; - gs_free char *uuid2 = NULL; - gs_free char *loaded_path2 = NULL; - gs_free char *dirname3 = NULL; - gs_free char *filename3 = NULL; - gs_free char *uuid3 = NULL; - gs_free char *loaded_path3 = NULL; - gboolean success; - gs_free char *filename = NULL; - - g_assert (dirname && dirname[0] == '/'); - g_assert (exp_full_filename && exp_full_filename[0]); - g_assert (!exp_loaded_path || exp_loaded_path[0] == '/'); - - filename = g_path_get_basename (exp_full_filename); - - full_filename = nms_keyfile_loaded_uuid_filename (dirname, uuid, FALSE); - g_assert_cmpstr (full_filename, ==, full_filename); - nm_clear_g_free (&full_filename); - - - g_assert (nms_keyfile_loaded_uuid_write (dirname, uuid, loaded_path, allow_relative, &full_filename)); - g_assert_cmpstr (full_filename, ==, exp_full_filename); - nm_clear_g_free (&full_filename); - - if (exp_symlink_target) - g_assert (g_file_test (exp_full_filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_SYMLINK)); - else - g_assert (!g_file_test (exp_full_filename, G_FILE_TEST_EXISTS)); - symlink_target = g_file_read_link (exp_full_filename, NULL); - g_assert_cmpstr (symlink_target, ==, exp_symlink_target); - - - success = nms_keyfile_loaded_uuid_read (dirname, filename, &full_filename, &uuid2, &loaded_path2); - g_assert_cmpint (!!exp_uuid, ==, success); - if (success) - g_assert_cmpstr (full_filename, ==, exp_full_filename); - else - g_assert_cmpstr (full_filename, ==, NULL); - nm_clear_g_free (&full_filename); - g_assert_cmpstr (uuid2, ==, exp_uuid); - g_assert_cmpstr (loaded_path2, ==, exp_loaded_path); - - - success = nms_keyfile_loaded_uuid_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3); - g_assert_cmpint (!!exp_uuid, ==, success); - if (success) { - g_assert_cmpstr (dirname3, ==, dirname); - g_assert_cmpstr (filename3, ==, filename); - } else { - g_assert_cmpstr (dirname3, ==, NULL); - g_assert_cmpstr (filename3, ==, NULL); - } - g_assert_cmpstr (uuid3, ==, exp_uuid); - g_assert_cmpstr (loaded_path3, ==, exp_loaded_path); -} - -static void -test_loaded_uuid (void) -{ - const char *uuid = "3c03fd17-ddc3-4100-a954-88b6fafff959"; - gs_free char *filename = g_strdup_printf ("%s%s%s", - NM_KEYFILE_PATH_PREFIX_NMLOADED, - uuid, - NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); - gs_free char *full_filename = g_strdup_printf ("%s/%s", - TEST_SCRATCH_DIR, - filename); - const char *loaded_path0 = NM_KEYFILE_PATH_NMLOADED_NULL; - const char *loaded_path1 = "/some/where/but/not/scratch/dir"; - const char *filename2 = "foo1"; - gs_free char *loaded_path2 = g_strdup_printf ("%s/%s", - TEST_SCRATCH_DIR, - filename2); - - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, NULL, FALSE, full_filename, NULL, NULL, NULL); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, NULL, TRUE, full_filename, NULL, NULL, NULL); - - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path0, FALSE, full_filename, uuid, loaded_path0, loaded_path0); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path0, TRUE, full_filename, uuid, loaded_path0, loaded_path0); - - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path1, FALSE, full_filename, uuid, loaded_path1, loaded_path1); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path1, TRUE, full_filename, uuid, loaded_path1, loaded_path1); - - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path2, FALSE, full_filename, uuid, loaded_path2, loaded_path2); - _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path2, TRUE, full_filename, uuid, filename2, loaded_path2); - - (void) unlink (full_filename); -} - -/*****************************************************************************/ - -NMTST_DEFINE (); - -int main (int argc, char **argv) -{ - int errsv; - - _nm_utils_set_testing (NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK); - - nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); - - if (g_mkdir_with_parents (TEST_SCRATCH_DIR, 0755) != 0) { - errsv = errno; - g_error ("failure to create test directory \"%s\": %s", TEST_SCRATCH_DIR, nm_strerror_native (errsv)); - } - - /* The tests */ - g_test_add_func ("/keyfile/test_read_valid_wired_connection", test_read_valid_wired_connection); - g_test_add_func ("/keyfile/test_write_wired_connection", test_write_wired_connection); - - g_test_add_func ("/keyfile/test_read_ip6_wired_connection", test_read_ip6_wired_connection); - g_test_add_func ("/keyfile/test_write_ip6_wired_connection", test_write_ip6_wired_connection); - - g_test_add_func ("/keyfile/test_read_wired_mac_case", test_read_wired_mac_case); - g_test_add_func ("/keyfile/test_read_mac_old_format", test_read_mac_old_format); - g_test_add_func ("/keyfile/test_read_mac_ib_old_format", test_read_mac_ib_old_format); - - g_test_add_func ("/keyfile/test_read_valid_wireless_connection", test_read_valid_wireless_connection); - g_test_add_func ("/keyfile/test_write_wireless_connection", test_write_wireless_connection); - - g_test_add_func ("/keyfile/test_read_string_ssid", test_read_string_ssid); - g_test_add_func ("/keyfile/test_write_string_ssid", test_write_string_ssid); - - g_test_add_func ("/keyfile/test_read_intlist_ssid", test_read_intlist_ssid); - g_test_add_func ("/keyfile/test_write_intlist_ssid", test_write_intlist_ssid); - - g_test_add_func ("/keyfile/test_read_intlike_ssid", test_read_intlike_ssid); - g_test_add_func ("/keyfile/test_write_intlike_ssid", test_write_intlike_ssid); - - g_test_add_func ("/keyfile/test_read_intlike_ssid_2", test_read_intlike_ssid_2); - g_test_add_func ("/keyfile/test_write_intlike_ssid_2", test_write_intlike_ssid_2); - - g_test_add_func ("/keyfile/test_read_bt_dun_connection", test_read_bt_dun_connection); - g_test_add_func ("/keyfile/test_write_bt_dun_connection", test_write_bt_dun_connection); - - g_test_add_func ("/keyfile/test_read_gsm_connection", test_read_gsm_connection); - g_test_add_func ("/keyfile/test_write_gsm_connection", test_write_gsm_connection); - - g_test_add_func ("/keyfile/test_read_wired_8021x_tls_blob_connection", test_read_wired_8021x_tls_blob_connection); - g_test_add_func ("/keyfile/test_read_wired_8021x_tls_bad_path_connection", test_read_wired_8021x_tls_bad_path_connection); - - g_test_add_func ("/keyfile/test_read_wired_8021x_tls_old_connection", test_read_wired_8021x_tls_old_connection); - g_test_add_func ("/keyfile/test_read_wired_8021x_tls_new_connection", test_read_wired_8021x_tls_new_connection); - g_test_add_func ("/keyfile/test_write_wired_8021x_tls_connection_path", test_write_wired_8021x_tls_connection_path); - g_test_add_func ("/keyfile/test_write_wired_8021x_tls_connection_blob", test_write_wired_8021x_tls_connection_blob); - - g_test_add_func ("/keyfile/test_read_dcb_connection", test_read_dcb_connection); - - g_test_add_func ("/keyfile/test_read_infiniband_connection", test_read_infiniband_connection); - g_test_add_func ("/keyfile/test_write_infiniband_connection", test_write_infiniband_connection); - - g_test_add_func ("/keyfile/test_read_bridge_main", test_read_bridge_main); - g_test_add_func ("/keyfile/test_write_bridge_main", test_write_bridge_main); - g_test_add_func ("/keyfile/test_read_bridge_component", test_read_bridge_component); - g_test_add_func ("/keyfile/test_write_bridge_component", test_write_bridge_component); - - g_test_add_func ("/keyfile/test_read_new_wired_group_name", test_read_new_wired_group_name); - g_test_add_func ("/keyfile/test_write_new_wired_group_name", test_write_new_wired_group_name); - g_test_add_func ("/keyfile/test_read_new_wireless_group_names", test_read_new_wireless_group_names); - g_test_add_func ("/keyfile/test_write_new_wireless_group_names", test_write_new_wireless_group_names); - - g_test_add_func ("/keyfile/test_read_missing_vlan_setting", test_read_missing_vlan_setting); - g_test_add_func ("/keyfile/test_read_missing_vlan_flags", test_read_missing_vlan_flags); - g_test_add_func ("/keyfile/test_read_missing_id_uuid", test_read_missing_id_uuid); - - g_test_add_func ("/keyfile/test_read_minimal", test_read_minimal); - g_test_add_func ("/keyfile/test_read_minimal_slave", test_read_minimal_slave); - - g_test_add_func ("/keyfile/test_read_enum_property", test_read_enum_property); - g_test_add_func ("/keyfile/test_write_enum_property", test_write_enum_property); - g_test_add_func ("/keyfile/test_read_flags_property", test_read_flags_property); - g_test_add_func ("/keyfile/test_write_flags_property", test_write_flags_property); - - g_test_add_func ("/keyfile/test_read_tc_config", test_read_tc_config); - g_test_add_func ("/keyfile/test_write_tc_config", test_write_tc_config); - - g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename", test_nm_keyfile_plugin_utils_escape_filename); - - g_test_add_func ("/keyfile/test_loaded_uuid", test_loaded_uuid); - - return g_test_run (); -} diff --git a/src/settings/plugins/meson.build b/src/settings/plugins/meson.build index a1aa7823..83981aab 100644 --- a/src/settings/plugins/meson.build +++ b/src/settings/plugins/meson.build @@ -1,7 +1,3 @@ -if enable_ibft - subdir('ibft') -endif - if enable_ifcfg_rh subdir('ifcfg-rh') endif -- cgit 1.3.0-6-gf8a5