/src/dhcp-manager/tests/test-dhcp-dhclient
/src/dhcp-manager/tests/test-dhcp-options
/src/dnsmasq-manager/tests/test-dnsmasq-utils
+/src/settings/plugins/ibft/tests/test-ibft
/src/settings/plugins/ifcfg-rh/tests/network-scripts/*-Test_Write_*
/src/settings/plugins/ifcfg-rh/tests/network-scripts/Test_Write_*
/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh
src/settings/plugins/ifcfg-rh/Makefile
src/settings/plugins/ifcfg-rh/tests/Makefile
src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile
+src/settings/plugins/ibft/Makefile
+src/settings/plugins/ibft/tests/Makefile
src/settings/plugins/ifcfg-suse/Makefile
src/settings/plugins/keyfile/Makefile
src/settings/plugins/keyfile/tests/Makefile
#endif
+static inline void
+nmtst_assert_ip4_address_equals (guint32 addr, const char *expected, const char *loc)
+{
+ guint32 addr2 = nmtst_inet4_from_string (expected);
+
+ if (addr != addr2)
+ g_error ("assert: %s: ip4 address '%s' expected, but got %s",
+ loc, expected ? expected : "any", nm_utils_inet4_ntop (addr, NULL));
+}
+#define nmtst_assert_ip4_address_equals(addr, expected) \
+ nmtst_assert_ip4_address_equals (addr, expected, G_STRLOC)
+
+#ifdef __NM_UTILS_H__
+static inline void
+nmtst_assert_hwaddr_equals (gconstpointer hwaddr1, gssize hwaddr1_len, const char *expected, const char *loc)
+{
+ guint8 buf2[NM_UTILS_HWADDR_LEN_MAX];
+ gsize hwaddr2_len = 1;
+ const char *p;
+ gboolean success;
+
+ g_assert (hwaddr1_len > 0 && hwaddr1_len <= NM_UTILS_HWADDR_LEN_MAX);
+
+ g_assert (expected);
+ for (p = expected; *p; p++) {
+ if (*p == ':' || *p == '-')
+ hwaddr2_len++;
+ }
+ g_assert (hwaddr2_len <= NM_UTILS_HWADDR_LEN_MAX);
+ g_assert (nm_utils_hwaddr_aton (expected, buf2, hwaddr2_len));
+
+ /* Manually check the entire hardware address instead of using
+ * nm_utils_hwaddr_matches() because that function doesn't compare
+ * entire InfiniBand addresses for various (legitimate) reasons.
+ */
+ success = (hwaddr1_len == hwaddr2_len);
+ if (success)
+ success = !memcmp (hwaddr1, buf2, hwaddr1_len);
+ if (!success) {
+ g_error ("assert: %s: hwaddr '%s' (%zd) expected, but got %s (%zd)",
+ loc, expected, hwaddr2_len, nm_utils_hwaddr_ntoa (hwaddr1, hwaddr1_len), hwaddr1_len);
+ }
+}
+#define nmtst_assert_hwaddr_equals(hwaddr1, hwaddr1_len, expected) \
+ nmtst_assert_hwaddr_equals (hwaddr1, hwaddr1_len, expected, G_STRLOC)
+#endif
#endif /* __NM_TEST_UTILS_H__ */
src/nm-logging.c
src/nm-manager.c
src/nm-sleep-monitor-systemd.c
+src/settings/plugins/ibft/plugin.c
src/settings/plugins/ifcfg-rh/reader.c
src/settings/nm-settings-utils.c
-SUBDIRS=keyfile example
+SUBDIRS=keyfile example ibft
@GNOME_CODE_COVERAGE_RULES@
--- /dev/null
+SUBDIRS = . tests
+
+@GNOME_CODE_COVERAGE_RULES@
+
+pkglib_LTLIBRARIES = libnm-settings-plugin-ibft.la
+
+noinst_LTLIBRARIES = libibft-io.la
+
+libibft_io_la_SOURCES = \
+ reader.c \
+ reader.h \
+ errors.c \
+ errors.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/ \
+ -I$(top_srcdir)/src/platform \
+ -I$(top_srcdir)/src/settings \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/libnm-core \
+ -I$(top_builddir)/libnm-core \
+ -DNETWORKMANAGER_COMPILATION \
+ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+ $(GLIB_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ -DG_LOG_DOMAIN=\""NetworkManager-ibft"\" \
+ -DSYSCONFDIR=\"$(sysconfdir)\" \
+ -DSBINDIR=\"$(sbindir)\"
+
+libnm_settings_plugin_ibft_la_SOURCES = \
+ plugin.c \
+ plugin.h \
+ nm-ibft-connection.c \
+ nm-ibft-connection.h
+
+libnm_settings_plugin_ibft_la_LDFLAGS = -module -avoid-version
+libnm_settings_plugin_ibft_la_LIBADD = libibft-io.la
+
+CLEANFILES = $(BUILT_SOURCES)
+
--- /dev/null
+/* -*- 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 <glib.h>
+#include "errors.h"
+
+GQuark
+ibft_plugin_error_quark (void)
+{
+ static GQuark error_quark = 0;
+
+ if (G_UNLIKELY (error_quark == 0))
+ error_quark = g_quark_from_static_string ("ibft-plugin-error-quark");
+ return error_quark;
+}
+
+
--- /dev/null
+/* -*- 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 __ERRORS_H__
+#define __ERRORS_H__
+
+#define IBFT_PLUGIN_ERROR (ibft_plugin_error_quark ())
+GQuark ibft_plugin_error_quark (void);
+
+
+#endif /* __ERRORS_H__ */
+
--- /dev/null
+/* -*- 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 <string.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+
+#include <glib/gstdio.h>
+
+#include "nm-ibft-connection.h"
+#include "errors.h"
+#include "reader.h"
+
+G_DEFINE_TYPE (NMIbftConnection, nm_ibft_connection, NM_TYPE_SETTINGS_CONNECTION)
+
+NMIbftConnection *
+nm_ibft_connection_new (const GPtrArray *block, GError **error)
+{
+ NMConnection *source;
+ GObject *object;
+
+ source = connection_from_block (block, error);
+ if (!source)
+ return NULL;
+
+ object = g_object_new (NM_TYPE_IBFT_CONNECTION, NULL);
+ /* Update settings with what was read from iscsiadm */
+ if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
+ source,
+ FALSE,
+ error))
+ g_clear_object (&object);
+
+ return (NMIbftConnection *) object;
+}
+
+/* GObject */
+
+static void
+nm_ibft_connection_init (NMIbftConnection *connection)
+{
+}
+
+static void
+nm_ibft_connection_class_init (NMIbftConnectionClass *ibft_connection_class)
+{
+}
+
--- /dev/null
+/* -*- 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 NM_IBFT_CONNECTION_H
+#define NM_IBFT_CONNECTION_H
+
+G_BEGIN_DECLS
+
+#include <nm-settings-connection.h>
+
+#define NM_TYPE_IBFT_CONNECTION (nm_ibft_connection_get_type ())
+#define NM_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IBFT_CONNECTION, NMIbftConnection))
+#define NM_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IBFT_CONNECTION, NMIbftConnectionClass))
+#define NM_IS_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IBFT_CONNECTION))
+#define NM_IS_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IBFT_CONNECTION))
+#define NM_IBFT_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IBFT_CONNECTION, NMIbftConnectionClass))
+
+typedef struct {
+ NMSettingsConnection parent;
+} NMIbftConnection;
+
+typedef struct {
+ NMSettingsConnectionClass parent;
+} NMIbftConnectionClass;
+
+GType nm_ibft_connection_get_type (void);
+
+NMIbftConnection *nm_ibft_connection_new (const GPtrArray *block,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* NM_IBFT_CONNECTION_H */
--- /dev/null
+/* -*- 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 <config.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <gmodule.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <nm-setting-connection.h>
+
+#include "nm-dbus-glib-types.h"
+#include "nm-system-config-interface.h"
+#include "nm-settings-error.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+
+#include "plugin.h"
+#include "errors.h"
+#include "reader.h"
+#include "nm-ibft-connection.h"
+
+static void system_config_interface_init (NMSystemConfigInterface *system_config_interface_class);
+
+G_DEFINE_TYPE_EXTENDED (SCPluginIbft, sc_plugin_ibft, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (NM_TYPE_SYSTEM_CONFIG_INTERFACE,
+ system_config_interface_init))
+
+#define SC_PLUGIN_IBFT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SC_TYPE_PLUGIN_IBFT, SCPluginIbftPrivate))
+
+
+typedef struct {
+ GHashTable *connections; /* uuid::connection */
+ gboolean initialized;
+} SCPluginIbftPrivate;
+
+static void
+read_connections (SCPluginIbft *self)
+{
+ SCPluginIbftPrivate *priv = SC_PLUGIN_IBFT_GET_PRIVATE (self);
+ GSList *blocks, *iter;
+ GError *error = NULL;
+ NMIbftConnection *connection;
+
+ if (!read_ibft_blocks ("/sbin/iscsiadm", &blocks, &error)) {
+ nm_log_dbg (LOGD_SETTINGS, _("ibft: failed to read iscsiadm records: %s"), error->message);
+ g_error_free (error);
+ return;
+ }
+
+ for (iter = blocks; iter; iter = iter->next) {
+ connection = nm_ibft_connection_new (iter->data, &error);
+ if (connection) {
+ nm_log_info (LOGD_SETTINGS, _("ibft: read connection '%s'"),
+ nm_connection_get_id (NM_CONNECTION (connection)));
+ g_hash_table_insert (priv->connections,
+ g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection))),
+ connection);
+ } else {
+ nm_log_warn (LOGD_SETTINGS, _("ibft: failed to read iscsiadm record: %s"), error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ g_slist_free_full (blocks, (GDestroyNotify) g_ptr_array_unref);
+}
+
+static GSList *
+get_connections (NMSystemConfigInterface *config)
+{
+ SCPluginIbft *self = SC_PLUGIN_IBFT (config);
+ SCPluginIbftPrivate *priv = SC_PLUGIN_IBFT_GET_PRIVATE (self);
+ GSList *list = NULL;
+ GHashTableIter iter;
+ NMIbftConnection *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
+init (NMSystemConfigInterface *config)
+{
+}
+
+static void
+sc_plugin_ibft_init (SCPluginIbft *self)
+{
+ SCPluginIbftPrivate *priv = SC_PLUGIN_IBFT_GET_PRIVATE (self);
+
+ priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+}
+
+static void
+dispose (GObject *object)
+{
+ SCPluginIbft *self = SC_PLUGIN_IBFT (object);
+ SCPluginIbftPrivate *priv = SC_PLUGIN_IBFT_GET_PRIVATE (self);
+
+ if (priv->connections) {
+ g_hash_table_destroy (priv->connections);
+ priv->connections = NULL;
+ }
+
+ G_OBJECT_CLASS (sc_plugin_ibft_parent_class)->dispose (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME:
+ g_value_set_string (value, "iBFT");
+ break;
+ case NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO:
+ g_value_set_string (value, "(c) 2014 Red Hat, Inc. To report bugs please use the NetworkManager mailing list.");
+ break;
+ case NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES:
+ g_value_set_uint (value, NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE);
+ 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)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+sc_plugin_ibft_class_init (SCPluginIbftClass *req_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+ g_type_class_add_private (req_class, sizeof (SCPluginIbftPrivate));
+
+ object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ g_object_class_override_property (object_class,
+ NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME,
+ NM_SYSTEM_CONFIG_INTERFACE_NAME);
+
+ g_object_class_override_property (object_class,
+ NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO,
+ NM_SYSTEM_CONFIG_INTERFACE_INFO);
+
+ g_object_class_override_property (object_class,
+ NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES,
+ NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES);
+
+ g_object_class_override_property (object_class,
+ NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME,
+ NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME);
+}
+
+static void
+system_config_interface_init (NMSystemConfigInterface *system_config_interface_class)
+{
+ /* interface implementation */
+ system_config_interface_class->get_connections = get_connections;
+ system_config_interface_class->init = init;
+}
+
+G_MODULE_EXPORT GObject *
+nm_system_config_factory (void)
+{
+ static SCPluginIbft *singleton = NULL;
+
+ if (!singleton)
+ singleton = SC_PLUGIN_IBFT (g_object_new (SC_TYPE_PLUGIN_IBFT, NULL));
+ else
+ g_object_ref (singleton);
+
+ return G_OBJECT (singleton);
+}
--- /dev/null
+/* -*- 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 _PLUGIN_H_
+#define _PLUGIN_H_
+
+#include <glib-object.h>
+
+#define SC_TYPE_PLUGIN_IBFT (sc_plugin_ibft_get_type ())
+#define SC_PLUGIN_IBFT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SC_TYPE_PLUGIN_IBFT, SCPluginIbft))
+#define SC_PLUGIN_IBFT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SC_TYPE_PLUGIN_IBFT, SCPluginIbftClass))
+#define SC_IS_PLUGIN_IBFT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_PLUGIN_IBFT))
+#define SC_IS_PLUGIN_IBFT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_PLUGIN_IBFT))
+#define SC_PLUGIN_IBFT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SC_TYPE_PLUGIN_IBFT, SCPluginIbftClass))
+
+typedef struct _SCPluginIbft SCPluginIbft;
+typedef struct _SCPluginIbftClass SCPluginIbftClass;
+
+struct _SCPluginIbft {
+ GObject parent;
+};
+
+struct _SCPluginIbftClass {
+ GObjectClass parent;
+};
+
+GType sc_plugin_ibft_get_type (void);
+
+#endif /* _PLUGIN_H_ */
+
--- /dev/null
+/* -*- 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 <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/wait.h>
+#include <sys/inotify.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <nm-connection.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
+#include <nm-setting-vlan.h>
+#include <nm-setting-wired.h>
+#include <nm-utils.h>
+
+#include "nm-platform.h"
+#include "nm-posix-signals.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+
+#include "errors.h"
+#include "reader.h"
+
+#define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, " " msg)
+
+static void
+iscsiadm_child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+ /* We are in the child process here; set a different process group to
+ * ensure signal isolation between child and parent.
+ */
+ pid_t pid = getpid ();
+ setpgid (pid, pid);
+
+ /*
+ * We blocked signals in main(). We need to restore original signal
+ * mask for iscsiadm here so that it can receive signals.
+ */
+ nm_unblock_posix_signals (NULL);
+}
+
+/* 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;
+
+ while (*src && g_ascii_isspace (*src))
+ 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"
+
+/**
+ * read_ibft_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
+read_ibft_blocks (const char *iscsiadm_path,
+ GSList **out_blocks,
+ GError **error)
+{
+ const char *argv[4] = { iscsiadm_path, "-m", "fw", NULL };
+ const char *envp[1] = { NULL };
+ GSList *blocks = NULL;
+ char *out = NULL, *err = NULL;
+ gint status = 0;
+ char **lines = NULL, **iter;
+ GPtrArray *block_lines = NULL;
+ gboolean success = FALSE;
+
+ 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,
+ iscsiadm_child_setup, NULL, &out, &err, &status, error))
+ goto done;
+
+ if (!WIFEXITED (status)) {
+ g_set_error (error, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: %s exited abnormally.", iscsiadm_path);
+ goto done;
+ }
+
+ if (WEXITSTATUS (status) != 0) {
+ g_set_error (error, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: %s exited with error %d. Message: '%s'",
+ iscsiadm_path, WEXITSTATUS (status), err ? err : "(none)");
+ goto done;
+ }
+
+ nm_log_dbg (LOGD_SETTINGS, "iBFT records:\n%s", out);
+
+ lines = g_strsplit_set (out, "\n\r", -1);
+ for (iter = lines; iter && *iter; iter++) {
+ if (!*iter[0])
+ continue;
+
+ if (!g_ascii_strncasecmp (*iter, TAG_BEGIN, STRLEN (TAG_BEGIN))) {
+ if (block_lines) {
+ PARSE_WARNING ("malformed iscsiadm record: missing END RECORD.");
+ g_ptr_array_unref (block_lines);
+ }
+ /* Start new record */
+ block_lines = g_ptr_array_new_full (15, g_free);
+ } else if (!g_ascii_strncasecmp (*iter, TAG_END, STRLEN (TAG_END))) {
+ if (block_lines) {
+ if (block_lines->len)
+ blocks = g_slist_prepend (blocks, block_lines);
+ else
+ g_ptr_array_unref (block_lines);
+ block_lines = NULL;
+ }
+ } else if (block_lines) {
+ char *s = remove_most_whitespace (*iter);
+
+ if (s)
+ g_ptr_array_add (block_lines, s);
+ else {
+ PARSE_WARNING ("malformed iscsiadm record: no = in '%s'.", *iter);
+ g_clear_pointer (&block_lines, g_ptr_array_unref);
+ }
+ }
+ }
+
+ if (block_lines) {
+ PARSE_WARNING ("malformed iscsiadm record: missing # END RECORD.");
+ g_clear_pointer (&block_lines, g_ptr_array_unref);
+ }
+ success = TRUE;
+
+done:
+ if (lines)
+ g_strfreev (lines);
+ g_free (out);
+ if (success)
+ *out_blocks = blocks;
+ else
+ g_slist_free_full (blocks, (GDestroyNotify) g_ptr_array_unref);
+ return success;
+}
+
+#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;
+}
+
+/**
+ * parse_ibft_config:
+ * @data: 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 @data and should not be used after @data is freed.
+ *
+ * Returns: %TRUE if at least , %FALSE on failure
+ */
+gboolean
+parse_ibft_config (const GPtrArray *data, GError **error, ...)
+{
+ gboolean success = FALSE;
+ const char **out_value, *p;
+ va_list ap;
+ const char *key;
+ guint i;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+ g_return_val_if_fail (data->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 < data->len; i++) {
+ p = match_iscsiadm_tag (g_ptr_array_index (data, i), key);
+ if (p) {
+ *out_value = p;
+ success = TRUE;
+ break;
+ }
+ }
+ }
+ va_end (ap);
+
+ if (!success) {
+ g_set_error_literal (error, IBFT_PLUGIN_ERROR, 0,
+ "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)
+{
+ NMSettingIP4Config *s_ip4 = NULL;
+ NMIP4Address *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 ipaddr = 0;
+ guint32 netmask = 0;
+ guint32 gateway = 0;
+ guint32 dns1 = 0;
+ guint32 dns2 = 0;
+ guint32 prefix;
+
+ g_assert (block);
+
+ if (!parse_ibft_config (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, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: malformed iscsiadm record: missing " ISCSI_BOOTPROTO_TAG);
+ goto error;
+ }
+
+ s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+
+ if (!g_ascii_strcasecmp (s_method, "dhcp")) {
+ g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+ goto success;
+ } else if (g_ascii_strcasecmp (s_method, "static") != 0) {
+ g_set_error (error, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: malformed iscsiadm record: unknown " ISCSI_BOOTPROTO_TAG " '%s'.",
+ s_method);
+ goto error;
+ }
+
+ /* Static configuration stuff */
+ g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
+
+ /* IP address */
+ if (!s_ipaddr || inet_pton (AF_INET, s_ipaddr, &ipaddr) != 1) {
+ g_set_error (error, IBFT_PLUGIN_ERROR, 0,
+ "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, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: malformed iscsiadm record: invalid subnet mask '%s'.",
+ s_netmask);
+ goto error;
+ }
+ prefix = nm_utils_ip4_netmask_to_prefix (netmask);
+
+ if (s_gateway && inet_pton (AF_INET, s_gateway, &gateway) != 1) {
+ g_set_error (error, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: malformed iscsiadm record: invalid IP gateway '%s'.",
+ s_gateway);
+ goto error;
+ }
+
+ if (s_dns1 && inet_pton (AF_INET, s_dns1, &dns1) != 1) {
+ g_set_error (error, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: malformed iscsiadm record: invalid DNS1 address '%s'.",
+ s_dns1);
+ goto error;
+ }
+
+ if (s_dns2 && inet_pton (AF_INET, s_dns2, &dns2) != 1) {
+ g_set_error (error, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: malformed iscsiadm record: invalid DNS2 address '%s'.",
+ s_dns2);
+ goto error;
+ }
+
+ addr = nm_ip4_address_new ();
+ nm_ip4_address_set_address (addr, ipaddr);
+ nm_ip4_address_set_prefix (addr, prefix);
+ nm_ip4_address_set_gateway (addr, gateway);
+ nm_setting_ip4_config_add_address (s_ip4, addr);
+ nm_ip4_address_unref (addr);
+
+ if (dns1)
+ nm_setting_ip4_config_add_dns (s_ip4, dns1);
+ if (dns2)
+ nm_setting_ip4_config_add_dns (s_ip4, 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, *uuid_data;
+ const char *s_hwaddr = NULL, *s_ip4addr = NULL, *s_vlanid;
+
+ if (!parse_ibft_config (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, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG);
+ return FALSE;
+ }
+
+ id = g_strdup_printf ("iBFT%s%s %s",
+ prefix ? " " : "",
+ prefix ? prefix : "",
+ iface);
+
+ uuid_data = g_strdup_printf ("%s%s%s",
+ s_vlanid ? s_vlanid : "0",
+ s_hwaddr,
+ s_ip4addr ? s_ip4addr : "DHCP");
+ uuid = nm_utils_uuid_generate_from_string (uuid_data);
+ g_free (uuid_data);
+
+ 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 (parse_ibft_config (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 = parse_ibft_config (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, IBFT_PLUGIN_ERROR, 0, "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_str = NULL;
+ GByteArray *hwaddr;
+
+ g_assert (block);
+ g_assert (connection);
+
+ if (!parse_ibft_config (block, NULL, ISCSI_HWADDR_TAG, &hwaddr_str, NULL)) {
+ g_set_error_literal (error, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG);
+ return FALSE;
+ }
+
+ hwaddr = nm_utils_hwaddr_atoba (hwaddr_str, ETH_ALEN);
+ if (!hwaddr) {
+ g_set_error (error, IBFT_PLUGIN_ERROR, 0,
+ "iBFT: malformed iscsiadm record: invalid " ISCSI_HWADDR_TAG " '%s'.",
+ hwaddr_str);
+ return FALSE;
+ }
+
+ s_wired = nm_setting_wired_new ();
+ g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, hwaddr, NULL);
+ g_byte_array_unref (hwaddr);
+
+ nm_connection_add_setting (connection, s_wired);
+ return TRUE;
+}
+
+NMConnection *
+connection_from_block (const GPtrArray *block, GError **error)
+{
+ NMConnection *connection = NULL;
+ gboolean is_vlan = FALSE;
+ const char *iface = NULL;
+
+ g_assert (block);
+
+ if (!parse_ibft_config (block, error, ISCSI_IFACE_TAG, &iface, NULL)) {
+ g_set_error_literal (error, IBFT_PLUGIN_ERROR, 0,
+ "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;
+}
+
--- /dev/null
+/* -*- 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 __READER_H__
+#define __READER_H__
+
+#include <glib.h>
+#include <nm-connection.h>
+
+gboolean read_ibft_blocks (const char *iscsiadm_path,
+ GSList **out_blocks,
+ GError **error);
+
+NMConnection *connection_from_block (const GPtrArray *block, GError **error);
+
+/* For testcases */
+gboolean parse_ibft_config (const GPtrArray *data, GError **error, ...) G_GNUC_NULL_TERMINATED;
+
+#endif /* __READER_H__ */
--- /dev/null
+if ENABLE_TESTS
+
+@GNOME_CODE_COVERAGE_RULES@
+
+AM_CPPFLAGS = \
+ $(GLIB_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/libnm-core \
+ -I$(top_builddir)/libnm-core \
+ -I$(top_srcdir)/src/ \
+ -I$(top_srcdir)/src/platform \
+ -I$(top_srcdir)/src/settings \
+ -I$(srcdir)/../ \
+ -DG_LOG_DOMAIN=\""NetworkManager-ibft"\" \
+ -DNETWORKMANAGER_COMPILATION \
+ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+ -DTEST_IBFT_DIR=\"$(abs_srcdir)\" \
+ -DTEST_SCRATCH_DIR=\"$(abs_builddir)/\"
+
+AM_LDFLAGS = \
+ $(GLIB_LIBS) \
+ $(DBUS_LIBS) \
+ $(CODE_COVERAGE_LDFLAGS)
+
+noinst_PROGRAMS = test-ibft
+
+test_ibft_SOURCES = \
+ test-ibft.c \
+ ../errors.c \
+ ../reader.c
+
+test_ibft_LDADD = \
+ $(top_builddir)/src/libNetworkManager.la
+
+TESTS = test-ibft
+
+EXTRA_DIST = \
+ iscsiadm-test-dhcp \
+ iscsiadm-test-static \
+ iscsiadm-test-bad-ipaddr \
+ iscsiadm-test-bad-gateway \
+ iscsiadm-test-bad-dns1 \
+ iscsiadm-test-bad-dns2 \
+ iscsiadm-test-bad-entry \
+ iscsiadm-test-bad-record \
+ iscsiadm-test-vlan
+
+endif
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+/* -*- 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 <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <nm-utils.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-vlan.h>
+
+#include "NetworkManagerUtils.h"
+
+#include "errors.h"
+#include "reader.h"
+#include "nm-logging.h"
+
+#include "nm-test-utils.h"
+
+static GPtrArray *
+read_block (const char *iscsiadm_path, const char *expected_mac)
+{
+ GSList *blocks = NULL, *iter;
+ GPtrArray *block = NULL;
+ GError *error = NULL;
+ gboolean success;
+
+ success = read_ibft_blocks (iscsiadm_path, &blocks, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert (blocks);
+
+ for (iter = blocks; iter; iter = iter->next) {
+ const char *s_hwaddr = NULL;
+
+ if (!parse_ibft_config (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);
+
+ g_slist_foreach (blocks, (GFunc) g_ptr_array_unref, NULL);
+ return block;
+}
+
+static void
+test_read_ibft_dhcp (void)
+{
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSettingWired *s_wired;
+ NMSettingIP4Config *s_ip4;
+ GError *error = NULL;
+ const GByteArray *array;
+ const char *expected_mac_address = "00:33:21:98:b9:f1";
+ GPtrArray *block;
+
+ block = read_block (TEST_IBFT_DIR "/iscsiadm-test-dhcp", expected_mac_address);
+
+ connection = 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);
+ array = nm_setting_wired_get_mac_address (s_wired);
+ g_assert (array);
+ nmtst_assert_hwaddr_equals (array->data, array->len, expected_mac_address);
+ 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_ip4_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+
+ g_object_unref (connection);
+}
+
+static void
+test_read_ibft_static (void)
+{
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSettingWired *s_wired;
+ NMSettingIP4Config *s_ip4;
+ GError *error = NULL;
+ const GByteArray *array;
+ const char *expected_mac_address = "00:33:21:98:b9:f0";
+ NMIP4Address *ip4_addr;
+ GPtrArray *block;
+
+ block = read_block (TEST_IBFT_DIR "/iscsiadm-test-static", expected_mac_address);
+
+ connection = 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);
+ array = nm_setting_wired_get_mac_address (s_wired);
+ g_assert (array);
+ nmtst_assert_hwaddr_equals (array->data, array->len, expected_mac_address);
+ 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_ip4_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
+
+ g_assert_cmpint (nm_setting_ip4_config_get_num_dns (s_ip4), ==, 2);
+ nmtst_assert_ip4_address_equals (nm_setting_ip4_config_get_dns (s_ip4, 0), "10.16.255.2");
+ nmtst_assert_ip4_address_equals (nm_setting_ip4_config_get_dns (s_ip4, 1), "10.16.255.3");
+
+ g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 1);
+ ip4_addr = nm_setting_ip4_config_get_address (s_ip4, 0);
+ g_assert (ip4_addr);
+ nmtst_assert_ip4_address_equals (nm_ip4_address_get_address (ip4_addr), "192.168.32.72");
+ g_assert_cmpint (nm_ip4_address_get_prefix (ip4_addr), ==, 22);
+ nmtst_assert_ip4_address_equals (nm_ip4_address_get_gateway (ip4_addr), "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;
+ GSList *blocks = NULL;
+ GError *error = NULL;
+ gboolean success;
+
+ g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS));
+
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*malformed iscsiadm record*");
+
+ success = read_ibft_blocks (iscsiadm_path, &blocks, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert (blocks == NULL);
+
+ 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 = connection_from_block (block, &error);
+ g_assert_error (error, IBFT_PLUGIN_ERROR, 0);
+ 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;
+ NMSettingIP4Config *s_ip4;
+ const GByteArray *array;
+ const char *expected_mac_address = "00:33:21:98:b9:f0";
+ NMIP4Address *ip4_addr;
+ GError *error = NULL;
+ GPtrArray *block;
+
+ block = read_block (TEST_IBFT_DIR "/iscsiadm-test-vlan", expected_mac_address);
+
+ connection = 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);
+ array = nm_setting_wired_get_mac_address (s_wired);
+ g_assert (array);
+ nmtst_assert_hwaddr_equals (array->data, array->len, expected_mac_address);
+
+ /* ===== 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);
+ g_assert_cmpstr (nm_setting_vlan_get_interface_name (s_vlan), ==, NULL);
+
+ /* ===== IPv4 SETTING ===== */
+ s_ip4 = nm_connection_get_setting_ip4_config (connection);
+ g_assert (s_ip4);
+ g_assert_cmpstr (nm_setting_ip4_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
+
+ g_assert_cmpint (nm_setting_ip4_config_get_num_dns (s_ip4), ==, 0);
+
+ g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 1);
+ ip4_addr = nm_setting_ip4_config_get_address (s_ip4, 0);
+ g_assert (ip4_addr);
+ nmtst_assert_ip4_address_equals (nm_ip4_address_get_address (ip4_addr), "192.168.6.200");
+ g_assert_cmpint (nm_ip4_address_get_prefix (ip4_addr), ==, 24);
+ nmtst_assert_ip4_address_equals (nm_ip4_address_get_gateway (ip4_addr), "0.0.0.0");
+
+ g_object_unref (connection);
+ g_ptr_array_ref (block);
+}
+
+NMTST_DEFINE ();
+
+#define TPATH "/settings/plugins/ibft/"
+
+int main (int argc, char **argv)
+{
+ nmtst_init_assert_logging (&argc, &argv);
+
+ 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_IBFT_DIR "/iscsiadm-test-bad-record", test_read_ibft_malformed);
+ g_test_add_data_func (TPATH "ibft/bad-entry-read", TEST_IBFT_DIR "/iscsiadm-test-bad-entry", test_read_ibft_malformed);
+ g_test_add_data_func (TPATH "ibft/bad-ipaddr-read", TEST_IBFT_DIR "/iscsiadm-test-bad-ipaddr", test_read_ibft_bad_address);
+ g_test_add_data_func (TPATH "ibft/bad-gateway-read", TEST_IBFT_DIR "/iscsiadm-test-bad-gateway", test_read_ibft_bad_address);
+ g_test_add_data_func (TPATH "ibft/bad-dns1-read", TEST_IBFT_DIR "/iscsiadm-test-bad-dns1", test_read_ibft_bad_address);
+ g_test_add_data_func (TPATH "ibft/bad-dns2-read", TEST_IBFT_DIR "/iscsiadm-test-bad-dns2", test_read_ibft_bad_address);
+
+ return g_test_run ();
+}
+