NetworkManager r3355 - in trunk: . system-settings/plugins system-settings/plugins/ifcfg-suse



Author: tambeti
Date: Thu Feb 28 20:28:20 2008
New Revision: 3355
URL: http://svn.gnome.org/viewvc/NetworkManager?rev=3355&view=rev

Log:
2008-02-28  Tambet Ingo  <tambet gmail com>

	Implement suse plugin for system settings daemon.

	* system-settings/plugins/ifcfg-suse/*: Implement.

	* system-settings/plugins/Makefile.am: Add ifcfg-suse to subdirs when targeting
	suse.

	* configure.in: Check (without failing) for gio.
	Create ifcfg-suse plugin's Makefile.


Added:
   trunk/system-settings/plugins/ifcfg-suse/
   trunk/system-settings/plugins/ifcfg-suse/Makefile.am
   trunk/system-settings/plugins/ifcfg-suse/parser.c
   trunk/system-settings/plugins/ifcfg-suse/parser.h
   trunk/system-settings/plugins/ifcfg-suse/plugin.c
   trunk/system-settings/plugins/ifcfg-suse/plugin.h
   trunk/system-settings/plugins/ifcfg-suse/shvar.c
   trunk/system-settings/plugins/ifcfg-suse/shvar.h
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/system-settings/plugins/Makefile.am

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Thu Feb 28 20:28:20 2008
@@ -53,18 +53,6 @@
 AM_GLIB_GNU_GETTEXT
 
 dnl
-dnl GNOME support
-dnl
-AC_ARG_WITH(docs, AC_HELP_STRING([--with-docs], [Build NetworkManager documentation]))
-AM_CONDITIONAL(WITH_DOCS, test "x$with_docs" = "xyes")
-case $with_docs in
-	yes) ;;
-	*)
-		with_docs=no
-		;;
-esac
-
-dnl
 dnl Make sha1.c happy on big endian systems
 dnl
 AC_C_BIGENDIAN
@@ -195,6 +183,11 @@
 AC_SUBST(GOBJECT_CFLAGS)
 AC_SUBST(GOBJECT_LIBS)
 
+# This is optional, at least for now.
+PKG_CHECK_MODULES(GIO, gio-2.0,,true)
+AC_SUBST(GIO_CFLAGS)
+AC_SUBST(GIO_LIBS)
+
 PKG_CHECK_MODULES(HAL, hal >= 0.5.0)
 AC_SUBST(HAL_CFLAGS)
 AC_SUBST(HAL_LIBS)
@@ -290,6 +283,7 @@
 system-settings/src/Makefile
 system-settings/plugins/Makefile
 system-settings/plugins/ifcfg-fedora/Makefile
+system-settings/plugins/ifcfg-suse/Makefile
 test/Makefile
 test/test-common/Makefile
 initscript/Makefile
@@ -325,8 +319,3 @@
 echo
 echo Distribution targeting: ${with_distro}
 echo 'if this is not correct, please specifiy your distro with --with-distro=DISTRO'
-
-echo
-echo Building documentation: ${with_docs}
-echo
-

Modified: trunk/system-settings/plugins/Makefile.am
==============================================================================
--- trunk/system-settings/plugins/Makefile.am	(original)
+++ trunk/system-settings/plugins/Makefile.am	Thu Feb 28 20:28:20 2008
@@ -1,2 +1,7 @@
+if TARGET_REDHAT
 SUBDIRS=ifcfg-fedora
+endif
 
+if TARGET_SUSE
+SUBDIRS=ifcfg-suse
+endif

Added: trunk/system-settings/plugins/ifcfg-suse/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/system-settings/plugins/ifcfg-suse/Makefile.am	Thu Feb 28 20:28:20 2008
@@ -0,0 +1,28 @@
+
+pkglib_LTLIBRARIES = libnm-settings-plugin-ifcfg-suse.la
+
+libnm_settings_plugin_ifcfg_suse_la_SOURCES = \
+	shvar.c \
+	shvar.h \
+	parser.c \
+	parser.h \
+	plugin.c \
+	plugin.h
+
+libnm_settings_plugin_ifcfg_suse_la_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(GMODULE_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	-DG_DISABLE_DEPRECATED \
+	-I${top_srcdir}/system-settings/src \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/libnm-util \
+	-DSYSCONFDIR=\"$(sysconfdir)\"
+
+libnm_settings_plugin_ifcfg_suse_la_LDFLAGS = -module -avoid-version
+libnm_settings_plugin_ifcfg_suse_la_LIBADD = \
+	$(GLIB_LIBS) \
+	$(GMODULE_LIBS) \
+	$(GIO_LIBS) \
+	$(top_builddir)/libnm-util/libnm-util.la
+

Added: trunk/system-settings/plugins/ifcfg-suse/parser.c
==============================================================================
--- (empty file)
+++ trunk/system-settings/plugins/ifcfg-suse/parser.c	Thu Feb 28 20:28:20 2008
@@ -0,0 +1,798 @@
+/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+
+/* NetworkManager system settings service
+ *
+ * SÃren Sandmann <sandmann daimi au dk>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * (C) Copyright 2007 Red Hat, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <sys/inotify.h>
+
+#include <glib.h>
+
+#include <nm-connection.h>
+#include <NetworkManager.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-wireless.h>
+#include <nm-utils.h>
+
+#include "shvar.h"
+#include "parser.h"
+#include "plugin.h"
+
+
+static gboolean
+get_int (const char *str, int *value)
+{
+	char *e;
+
+	*value = strtol (str, &e, 0);
+	if (*e != '\0')
+		return FALSE;
+
+	return TRUE;
+}
+
+#if 0
+static gboolean
+read_startmode (shvarFile *file)
+{
+	char *value;
+	gboolean automatic = TRUE;
+
+	value = svGetValue (file, "STARTMODE");
+	if (value) {
+		if (!g_ascii_strcasecmp (value, "manual"))
+			automatic = FALSE;
+		else if (!g_ascii_strcasecmp (value, "off")) {
+			// FIXME: actually ignore the device, not the connection
+			g_message ("Ignoring connection '%s' because NM_CONTROLLED was false", file);
+			automatic = FALSE;
+		}
+
+		g_free (value);
+	}
+
+	return automatic;
+}
+#endif
+
+static NMSetting *
+make_connection_setting (const char *file,
+                         shvarFile *ifcfg,
+                         const char *type,
+                         const char *suggested)
+{
+	NMSettingConnection *s_con;
+	char *basename = NULL;
+	int len;
+	char *ifcfg_name;
+
+	basename = g_path_get_basename (file);
+	if (!basename)
+		goto error;
+	len = strlen (basename);
+
+	if (len < strlen (IFCFG_TAG) + 1)
+		goto error;
+
+	if (strncmp (basename, IFCFG_TAG, strlen (IFCFG_TAG)))
+		goto error;
+
+	/* ignore .bak files */
+	if ((len > 4) && !strcmp (basename + len - 4, BAK_TAG))
+		goto error;
+
+	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+
+	ifcfg_name = (char *) (basename + strlen (IFCFG_TAG));
+
+ 	if (suggested) {
+		/* For cosmetic reasons, if the suggested name is the same as
+		 * the ifcfg files name, don't use it.
+		 */
+		if (strcmp (ifcfg_name, suggested)) {
+			s_con->id = g_strdup_printf ("System %s (%s)", suggested, ifcfg_name);
+			ifcfg_name = NULL;
+		}
+	}
+
+	if (ifcfg_name)
+		s_con->id = g_strdup_printf ("System %s", ifcfg_name);
+
+	s_con->type = g_strdup (type);
+	s_con->autoconnect = TRUE;
+
+	return (NMSetting *) s_con;
+
+error:
+	g_free (basename);
+	return NULL;
+}
+
+static guint32
+ip4_prefix_to_netmask (int prefix)
+{
+	guint32 msk = 0x80000000;
+	guint32 netmask = 0;
+
+	while (prefix > 0) {
+		netmask |= msk;
+		msk >>= 1;
+		prefix--;
+	}
+
+	return htonl (netmask);
+}
+
+static NMSetting *
+make_ip4_setting (shvarFile *ifcfg, GError **error)
+{
+	NMSettingIP4Config *s_ip4 = NULL;
+	char *value = NULL;
+	NMSettingIP4Address tmp = { 0, 0, 0 };
+	gboolean manual = TRUE;
+
+	value = svGetValue (ifcfg, "BOOTPROTO");
+	if (!value)
+		return NULL;
+
+	if (!g_ascii_strcasecmp (value, "bootp") || !g_ascii_strcasecmp (value, "dhcp")) {
+		manual = FALSE;
+		return NULL;
+	}
+
+	value = svGetValue (ifcfg, "IPADDR");
+	if (value) {
+		char **pieces;
+		struct in_addr ip4_addr;
+
+		pieces = g_strsplit (value, "/", 2);
+
+		if (inet_pton (AF_INET, pieces[0], &ip4_addr))
+			tmp.address = ip4_addr.s_addr;
+		else {
+			g_strfreev (pieces);
+			g_set_error (error, ifcfg_plugin_error_quark (), 0, "Invalid IP4 address '%s'", value);
+			goto error;
+		}
+
+		if (g_strv_length (pieces) == 2)
+			tmp.netmask = ip4_prefix_to_netmask (atoi (pieces[1]));
+
+		g_strfreev (pieces);
+		g_free (value);
+	}
+
+	if (tmp.netmask == 0) {
+		value = svGetValue (ifcfg, "PREFIXLEN");
+		if (value) {
+			tmp.netmask = ip4_prefix_to_netmask (atoi (value));
+			g_free (value);
+		}
+	}
+
+	if (tmp.netmask == 0) {
+		value = svGetValue (ifcfg, "NETMASK");
+		if (value) {
+			struct in_addr mask_addr;
+
+			if (inet_pton (AF_INET, value, &mask_addr))
+				tmp.netmask = mask_addr.s_addr;
+			else {
+				g_set_error (error, ifcfg_plugin_error_quark (), 0, "Invalid IP4 netmask '%s'", value);
+				goto error;
+			}
+			g_free (value);
+		}
+	}
+
+	s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+	s_ip4->manual = manual;
+	if (tmp.address || tmp.netmask || tmp.gateway) {
+		NMSettingIP4Address *addr;
+		addr = g_new0 (NMSettingIP4Address, 1);
+		memcpy (addr, &tmp, sizeof (NMSettingIP4Address));
+		s_ip4->addresses = g_slist_append (s_ip4->addresses, addr);
+	}
+
+	return NM_SETTING (s_ip4);
+
+error:
+	g_free (value);
+	if (s_ip4)
+		g_object_unref (s_ip4);
+	return NULL;
+}
+
+#if 0
+/*
+ * utils_bin2hexstr
+ *
+ * Convert a byte-array into a hexadecimal string.
+ *
+ * Code originally by Alex Larsson <alexl redhat com> and
+ *  copyright Red Hat, Inc. under terms of the LGPL.
+ *
+ */
+static char *
+utils_bin2hexstr (const char *bytes, int len, int final_len)
+{
+	static char	hex_digits[] = "0123456789abcdef";
+	char *		result;
+	int			i;
+
+	g_return_val_if_fail (bytes != NULL, NULL);
+	g_return_val_if_fail (len > 0, NULL);
+	g_return_val_if_fail (len < 256, NULL);	/* Arbitrary limit */
+
+	result = g_malloc0 (len * 2 + 1);
+	for (i = 0; i < len; i++)
+	{
+		result[2*i] = hex_digits[(bytes[i] >> 4) & 0xf];
+		result[2*i+1] = hex_digits[bytes[i] & 0xf];
+	}
+	/* Cut converted key off at the correct length for this cipher type */
+	if (final_len > -1)
+		result[final_len] = '\0';
+
+	return result;
+}
+#endif
+
+static char *
+get_one_wep_key (shvarFile *ifcfg, guint8 idx, GError **err)
+{
+	char *shvar_key;
+	char *key = NULL;
+	char *value = NULL;
+	char *p;
+
+	g_return_val_if_fail (idx <= 3, NULL);
+
+	shvar_key = g_strdup_printf ("WIRELESS_KEY_%d", idx);
+	value = svGetValue (ifcfg, shvar_key);
+	g_free (shvar_key);
+
+	/* Ignore empty keys */
+	if (!value)
+		return NULL;
+
+	if (strlen (value) < 1) {
+		g_free (value);
+		return NULL;
+	}
+
+	/* ASCII */
+	if (g_str_has_prefix (value, "s:")) {
+		p = value + 2;
+		if (strlen (p) != 5 || strlen (p) != 13)
+			g_set_error (err, ifcfg_plugin_error_quark (), 0, "Invalid hexadecimal WEP key.");
+		else {
+			while (*p) {
+				if (!isascii (*p)) {
+					g_set_error (err, ifcfg_plugin_error_quark (), 0, "Invalid hexadecimal WEP key.");
+					break;
+				}
+				p++;
+			}
+		}
+
+		if (!err)
+			key = g_strdup (p);
+	} else if (g_str_has_prefix (value, "h:")) {
+		/* Hashed passphrase */
+		p = value + 2;
+		if (p && (strlen (p) > 0 || strlen (p) < 65))
+			key = g_strdup (p);
+		else
+			g_set_error (err, ifcfg_plugin_error_quark (), 0, "Invalid WEP passphrase.");
+	} else {
+		/* Hexadecimal */
+		GString *str;
+
+		str = g_string_sized_new (26);
+		p = value + 2;
+		while (*p) {
+			if (g_ascii_isxdigit (*p))
+				str = g_string_append_c (str, *p);
+			else if (*p != '-') {
+				g_set_error (err, ifcfg_plugin_error_quark (), 0, "Invalid hexadecimal WEP key.");
+				break;
+			}
+			p++;
+		}
+
+		p = str->str;
+
+		if (p && (strlen (p) == 10 || strlen (p) == 26))
+			key = g_string_free (str, FALSE);
+		else
+			g_set_error (err, ifcfg_plugin_error_quark (), 0, "Invalid hexadecimal WEP key.");
+	}
+
+	g_free (value);
+
+	return key;
+}
+
+#define READ_WEP_KEY(idx) \
+	{ \
+		char *key = get_one_wep_key (ifcfg, idx, err); \
+		if (*err) \
+			goto error; \
+		if (key) { \
+			g_object_set_data_full (G_OBJECT (security), \
+			                        NM_SETTING_WIRELESS_SECURITY_WEP_KEY##idx, \
+			                        key, \
+			                        g_free); \
+			have_key = TRUE; \
+		} \
+	}
+
+
+static void
+read_wep_settings (shvarFile *ifcfg, NMSettingWirelessSecurity *security, GError **err)
+{
+	char *value;
+	gboolean have_key = FALSE;
+
+	READ_WEP_KEY(0)
+	READ_WEP_KEY(1)
+	READ_WEP_KEY(2)
+	READ_WEP_KEY(3)
+
+	if (have_key)
+		security->key_mgmt = g_strdup ("none");
+
+	value = svGetValue (ifcfg, "WIRELESS_DEFAULT_KEY");
+	if (value) {
+		gboolean success;
+		int key_idx = 0;
+
+		success = get_int (value, &key_idx);
+		if (success && (key_idx >= 0) && (key_idx <= 3))
+			security->wep_tx_keyidx = key_idx;
+		else
+			g_set_error (err, ifcfg_plugin_error_quark (), 0, "Invalid defualt WEP key '%s'", value);
+
+ 		g_free (value);
+	}
+
+error:
+	return;
+}
+
+/* Copied from applet/src/wireless-secuirty/wireless-security.c */
+static void
+ws_wpa_fill_default_ciphers (NMSettingWirelessSecurity *s_wireless_sec)
+{
+	// FIXME: allow protocol selection and filter on device capabilities
+	s_wireless_sec->proto = g_slist_append (s_wireless_sec->proto, g_strdup ("wpa"));
+	s_wireless_sec->proto = g_slist_append (s_wireless_sec->proto, g_strdup ("rsn"));
+
+	// FIXME: allow pairwise cipher selection and filter on device capabilities
+	s_wireless_sec->pairwise = g_slist_append (s_wireless_sec->pairwise, g_strdup ("tkip"));
+	s_wireless_sec->pairwise = g_slist_append (s_wireless_sec->pairwise, g_strdup ("ccmp"));
+
+	// FIXME: allow group cipher selection and filter on device capabilities
+	s_wireless_sec->group = g_slist_append (s_wireless_sec->group, g_strdup ("wep40"));
+	s_wireless_sec->group = g_slist_append (s_wireless_sec->group, g_strdup ("wep104"));
+	s_wireless_sec->group = g_slist_append (s_wireless_sec->group, g_strdup ("tkip"));
+	s_wireless_sec->group = g_slist_append (s_wireless_sec->group, g_strdup ("ccmp"));
+}
+
+static void
+read_wpa_psk_settings (shvarFile *ifcfg, NMSettingWirelessSecurity *security, GError **err)
+{
+	char *value;
+
+	value = svGetValue (ifcfg, "WIRELESS_WPA_PSK");
+	if (value) {
+		if (strlen (value) == 64) {
+			/* HEX key */
+			security->psk = value;
+		} else {
+			/* passphrase */
+
+			/* FIXME: */
+/* 			unsigned char *buf = g_malloc0 (WPA_PMK_LEN * 2); */
+/* 			pbkdf2_sha1 (value, (char *) s_wireless->ssid->data, s_wireless->ssid->len, 4096, buf, WPA_PMK_LEN); */
+/* 			security->psk = utils_bin2hexstr ((const char *) buf, WPA_PMK_LEN, WPA_PMK_LEN * 2); */
+/* 			g_free (buf); */
+			g_free (value);
+		}
+
+		ws_wpa_fill_default_ciphers (security);
+	} else
+		g_set_error (err, ifcfg_plugin_error_quark (), 0, "Missing WPA-PSK key.");
+}
+
+static void
+read_wpa_eap_settings (shvarFile *ifcfg, NMSettingWirelessSecurity *security, GError **err)
+{
+	char *value;
+
+	value = svGetValue (ifcfg, "WIRELESS_EAP_AUTH");
+	if (value) {
+		/* valid values are TLS PEAP TTLS */
+		security->eap = g_slist_append (NULL, value);
+	}
+
+	value = svGetValue (ifcfg, "WIRELESS_WPA_PROTO");
+	if (value) {
+		/* valid values are WPA RSN (WPA2) */
+		security->proto = g_slist_append (NULL, value);
+	}
+
+	security->identity = svGetValue (ifcfg, "WIRELESS_WPA_IDENTITY");
+
+	/* FIXME: This should be in get_secrets? */
+	value = svGetValue (ifcfg, "WIRELESS_WPA_PASSWORD");
+	if (value) {
+		g_free (value);
+	}
+
+	security->anonymous_identity = svGetValue (ifcfg, "WIRELESS_WPA_ANONID");
+
+	value = svGetValue (ifcfg, "WIRELESS_CA_CERT");
+	if (value) {
+		g_free (value);
+	}
+
+	value = svGetValue (ifcfg, "WIRELESS_CLIENT_CERT");
+	if (value) {
+		g_free (value);
+	}
+
+	/* FIXME: This should be in get_secrets? */
+	value = svGetValue (ifcfg, "WIRELESS_CLIENT_KEY");
+	if (value) {
+		g_free (value);
+	}
+
+	/* FIXME: This should be in get_secrets? */
+	value = svGetValue (ifcfg, "WIRELESS_CLIENT_KEY_PASSWORD");
+	if (value) {
+		g_free (value);
+	}
+
+	ws_wpa_fill_default_ciphers (security);
+}
+
+static NMSetting *
+make_wireless_security_setting (shvarFile *ifcfg, GError **err)
+{
+	NMSettingWirelessSecurity *s_wireless_sec = NULL;
+	char *value;
+
+	value = svGetValue (ifcfg, "WIRELESS_AUTH_MODE");
+	if (!value)
+		return NULL;
+
+	if (!g_ascii_strcasecmp (value, "no-encryption")) {
+		g_free (value);
+		return NULL;
+	}
+
+	s_wireless_sec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
+
+	if (!g_ascii_strcasecmp (value, "open")) {
+		s_wireless_sec->auth_alg = g_strdup ("open");
+		read_wep_settings (ifcfg, s_wireless_sec, err);
+	} else if (!g_ascii_strcasecmp (value, "sharedkey")) {
+		s_wireless_sec->auth_alg = g_strdup ("shared");
+		read_wep_settings (ifcfg, s_wireless_sec, err);
+	}
+
+	else if (!g_ascii_strcasecmp (value, "psk")) {
+		s_wireless_sec->key_mgmt = g_strdup ("wpa-psk");
+		read_wpa_psk_settings (ifcfg, s_wireless_sec, err);
+	} else if (!g_ascii_strcasecmp (value, "eap")) {
+		s_wireless_sec->key_mgmt = g_strdup ("wps-eap");
+		read_wpa_eap_settings (ifcfg, s_wireless_sec, err);
+	} else
+		g_set_error (err, ifcfg_plugin_error_quark (), 0, "Invalid authentication algoritm '%s'", value);
+
+	g_free (value);
+
+	if (*err == NULL)
+		return NM_SETTING (s_wireless_sec);
+
+	if (s_wireless_sec)
+		g_object_unref (s_wireless_sec);
+	return NULL;
+}
+
+static NMSetting *
+make_wireless_setting (shvarFile *ifcfg,
+                       NMSetting *security,
+                       GError **err)
+{
+	NMSettingWireless *s_wireless;
+	char *value;
+
+	s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
+
+	value = svGetValue (ifcfg, "WIRELESS_ESSID");
+	if (value) {
+		gsize len = strlen (value);
+
+		if (len > 32 || len == 0) {
+			g_set_error (err, ifcfg_plugin_error_quark (), 0,
+			             "Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)",
+			             value, len);
+			goto error;
+		}
+
+		s_wireless->ssid = g_byte_array_sized_new (strlen (value));
+		g_byte_array_append (s_wireless->ssid, (const guint8 *) value, len);
+		g_free (value);
+	}
+
+	value = svGetValue (ifcfg, "WIRLESS_MODE");
+	if (value) {
+		if (!g_ascii_strcasecmp (value, "ad-hoc")) {
+			s_wireless->mode = g_strdup ("adhoc");
+		} else if (!g_ascii_strcasecmp (value, "managed")) {
+			s_wireless->mode = g_strdup ("infrastructure");
+		} else {
+			g_set_error (err, ifcfg_plugin_error_quark (), 0,
+			             "Invalid mode '%s' (not ad-hoc or managed)", value);
+			g_free (value);
+			goto error;
+		}
+		g_free (value);
+	}
+
+	if (security)
+		s_wireless->security = g_strdup (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+
+	// FIXME: channel/freq, other L2 parameters like RTS
+
+	return NM_SETTING (s_wireless);
+
+error:
+	if (s_wireless)
+		g_object_unref (s_wireless);
+	return NULL;
+}
+
+static NMConnection *
+wireless_connection_from_ifcfg (const char *file, shvarFile *ifcfg, GError **err)
+{
+	NMConnection *connection = NULL;
+	NMSetting *con_setting = NULL;
+	NMSetting *wireless_setting = NULL;
+	NMSettingWireless *tmp;
+	NMSetting *security_setting = NULL;
+	char *printable_ssid = NULL;
+
+	connection = nm_connection_new ();
+	if (!connection) {
+		g_set_error (err, ifcfg_plugin_error_quark (), 0,
+		             "Failed to allocate new connection for %s.", file);
+		return NULL;
+	}
+
+	/* Wireless security */
+	security_setting = make_wireless_security_setting (ifcfg, err);
+	if (*err)
+		goto error;
+	if (security_setting)
+		nm_connection_add_setting (connection, security_setting);
+
+	/* Wireless */
+	wireless_setting = make_wireless_setting (ifcfg, security_setting, err);
+	if (!wireless_setting)
+		goto error;
+
+	nm_connection_add_setting (connection, wireless_setting);
+
+	tmp = NM_SETTING_WIRELESS (wireless_setting);
+	printable_ssid = nm_utils_ssid_to_utf8 ((const char *) tmp->ssid->data,
+	                                        (guint32) tmp->ssid->len);
+
+	con_setting = make_connection_setting (file, ifcfg,
+	                                       NM_SETTING_WIRELESS_SETTING_NAME,
+	                                       printable_ssid);
+	g_free (printable_ssid);
+
+	if (!con_setting) {
+		g_set_error (err, ifcfg_plugin_error_quark (), 0,
+		             "Failed to create connection setting.");
+		goto error;
+	}
+	nm_connection_add_setting (connection, con_setting);
+
+	if (!nm_connection_verify (connection)) {
+		g_set_error (err, ifcfg_plugin_error_quark (), 0,
+		             "Connection from %s was invalid.", file);
+		goto error;
+	}
+
+	return connection;
+
+error:
+	g_object_unref (connection);
+	if (con_setting)
+		g_object_unref (con_setting);
+	if (wireless_setting)
+		g_object_unref (wireless_setting);
+	return NULL;
+}
+
+static NMSetting *
+make_wired_setting (shvarFile *ifcfg, GError **err)
+{
+	NMSettingWired *s_wired;
+	char *value;
+	int mtu;
+
+	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
+
+	value = svGetValue (ifcfg, "MTU");
+	if (value) {
+		if (strlen (value) < 1)
+			/* Ignore empty MTU */
+			;
+		else if (get_int (value, &mtu)) {
+			if (mtu >= 0 && mtu < 65536)
+				s_wired->mtu = mtu;
+		} else {
+			g_set_error (err, ifcfg_plugin_error_quark (), 0, "Invalid MTU '%s'", value);
+			g_object_unref (s_wired);
+			s_wired = NULL;
+		}
+		g_free (value);
+	}
+
+	return (NMSetting *) s_wired;
+}
+
+static NMConnection *
+wired_connection_from_ifcfg (const char *file, shvarFile *ifcfg, GError **err)
+{
+	NMConnection *connection = NULL;
+	NMSetting *con_setting = NULL;
+	NMSetting *wired_setting = NULL;
+
+	connection = nm_connection_new ();
+	con_setting = make_connection_setting (file, ifcfg, NM_SETTING_WIRED_SETTING_NAME, NULL);
+	if (!con_setting) {
+		g_set_error (err, ifcfg_plugin_error_quark (), 0,
+		             "Failed to create connection setting.");
+		goto error;
+	}
+	nm_connection_add_setting (connection, con_setting);
+
+	wired_setting = make_wired_setting (ifcfg, err);
+	if (!wired_setting)
+		goto error;
+
+	nm_connection_add_setting (connection, wired_setting);
+
+	if (!nm_connection_verify (connection)) {
+		g_set_error (err, ifcfg_plugin_error_quark (), 0,
+		             "Connection from %s was invalid.", file);
+		goto error;
+	}
+
+	return connection;
+
+error:
+	g_object_unref (connection);
+	if (con_setting)
+		g_object_unref (con_setting);
+	if (wired_setting)
+		g_object_unref (wired_setting);
+	return NULL;
+}
+	
+NMConnection *
+parser_parse_ifcfg (const char *file, GError **err)
+{
+	NMConnection *connection = NULL;
+	shvarFile *parsed;
+	char *type;
+	char *nmc = NULL;
+	NMSetting *s_ip4;
+
+	g_return_val_if_fail (file != NULL, NULL);
+
+	parsed = svNewFile (file);
+	if (!parsed) {
+		g_set_error (err, ifcfg_plugin_error_quark (), 0,
+		             "Couldn't parse file '%s'", file);
+		return NULL;
+	}
+
+	nmc = svGetValue (parsed, "NM_CONTROLLED");
+	if (nmc) {
+		if (!svTrueValue (parsed, nmc, 1)) {
+			g_free (nmc);
+			// FIXME: actually ignore the device, not the connection
+			g_message ("Ignoring connection '%s' because NM_CONTROLLED was false", file);
+			goto done;
+		}
+		g_free (nmc);
+	}
+	
+	type = svGetValue (parsed, "WIRELESS_ESSID");
+	if (type) {
+		g_free (type);
+		connection = wireless_connection_from_ifcfg (file, parsed, err);
+	} else
+		connection = wired_connection_from_ifcfg (file, parsed, err);
+
+	if (!connection)
+		goto done;
+
+	s_ip4 = make_ip4_setting (parsed, err);
+	if (*err) {
+		g_object_unref (connection);
+		connection = NULL;
+		goto done;
+	} else if (s_ip4) {
+		nm_connection_add_setting (connection, s_ip4);
+	}
+
+	if (!nm_connection_verify (connection)) {
+		g_object_unref (connection);
+		connection = NULL;
+		g_set_error (err, ifcfg_plugin_error_quark (), 0,
+		             "Connection was invalid");
+	}
+
+done:
+	svCloseFile (parsed);
+	return connection;
+}
+
+guint32
+parser_parse_routes (const char *file, GError **err)
+{
+	FILE *f;
+	char *buf;
+	char buffer[512];
+	guint route = 0;
+
+	if ((f = fopen (SYSCONFDIR"/sysconfig/network/routes", "r"))) {
+		while (fgets (buffer, 512, f) && !feof (f)) {
+			buf = strtok (buffer, " ");
+			if (strcmp (buf, "default") == 0) {
+				buf = strtok (NULL, " ");
+				if (buf)
+					route = inet_addr (buf);
+				break;
+			}
+			fclose (f);
+		}
+	}
+
+	return route;
+}

Added: trunk/system-settings/plugins/ifcfg-suse/parser.h
==============================================================================
--- (empty file)
+++ trunk/system-settings/plugins/ifcfg-suse/parser.h	Thu Feb 28 20:28:20 2008
@@ -0,0 +1,35 @@
+/* NetworkManager system settings service
+ *
+ * SÃren Sandmann <sandmann daimi au dk>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * (C) Copyright 2007 Red Hat, Inc.
+ */
+
+#ifndef _PARSER_H_
+#define _PARSER_H_
+
+#include <glib.h>
+#include <nm-connection.h>
+
+#define IFCFG_TAG "ifcfg-"
+#define BAK_TAG ".bak"
+
+NMConnection * parser_parse_ifcfg  (const char *file, GError **error);
+guint32        parser_parse_routes (const char *file, GError **err);
+
+
+#endif /* _PARSER_H_ */

Added: trunk/system-settings/plugins/ifcfg-suse/plugin.c
==============================================================================
--- (empty file)
+++ trunk/system-settings/plugins/ifcfg-suse/plugin.c	Thu Feb 28 20:28:20 2008
@@ -0,0 +1,438 @@
+/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+
+/* NetworkManager system settings service
+ *
+ * SÃren Sandmann <sandmann daimi au dk>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * (C) Copyright 2007 Red Hat, Inc.
+ */
+
+#include <gmodule.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <gio/gio.h>
+
+#include <nm-setting-connection.h>
+#include <nm-setting-ip4-config.h>
+
+#include "plugin.h"
+#include "parser.h"
+#include "nm-system-config-interface.h"
+
+#define IFCFG_PLUGIN_NAME "ifcfg-suse"
+#define IFCFG_PLUGIN_INFO "(C) 2008 Novell, Inc.  To report bugs please use the NetworkManager mailing list."
+#define IFCFG_DIR SYSCONFDIR "/sysconfig/network"
+
+static void system_config_interface_init (NMSystemConfigInterface *system_config_interface_class);
+
+G_DEFINE_TYPE_EXTENDED (SCPluginIfcfg, sc_plugin_ifcfg, G_TYPE_OBJECT, 0,
+				    G_IMPLEMENT_INTERFACE (NM_TYPE_SYSTEM_CONFIG_INTERFACE,
+									  system_config_interface_init))
+
+#define SC_PLUGIN_IFCFG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SC_TYPE_PLUGIN_IFCFG, SCPluginIfcfgPrivate))
+
+
+#define IFCFG_FILE_PATH_TAG "ifcfg-file-path"
+
+typedef struct {
+	gboolean initialized;
+	GSList *connections;
+
+	GFileMonitor *monitor;
+	guint monitor_id;
+} SCPluginIfcfgPrivate;
+
+
+GQuark
+ifcfg_plugin_error_quark (void)
+{
+	static GQuark error_quark = 0;
+
+	if (G_UNLIKELY (error_quark == 0))
+		error_quark = g_quark_from_static_string ("ifcfg-plugin-error-quark");
+
+	return error_quark;
+}
+
+struct FindInfo {
+	const char *path;
+	gboolean found;
+};
+
+static gboolean
+is_ifcfg_file (const char *file)
+{
+	return g_str_has_prefix (file, IFCFG_TAG) && strcmp (file, IFCFG_TAG "lo");
+}
+
+static NMConnection *
+build_one_connection (const char *ifcfg_file)
+{
+	NMConnection *connection;
+	GError *err = NULL;
+
+	PLUGIN_PRINT (PLUGIN_NAME, "parsing %s ... ", ifcfg_file);
+
+	connection = parser_parse_ifcfg (ifcfg_file, &err);
+	if (connection) {
+		NMSettingConnection *s_con;
+
+		s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+		g_assert (s_con);
+		g_assert (s_con->id);
+		PLUGIN_PRINT (PLUGIN_NAME, "    found connection '%s'", s_con->id);
+	} else
+		PLUGIN_PRINT (PLUGIN_NAME, "    error: %s", err->message ? err->message : "(unknown)");
+
+	return connection;
+}
+
+typedef struct {
+	SCPluginIfcfg *plugin;
+	NMConnection *connection;
+	GFileMonitor *monitor;
+	guint monitor_id;
+} ConnectionMonitor;
+
+static void
+connection_monitor_destroy (gpointer data)
+{
+	ConnectionMonitor *monitor = (ConnectionMonitor *) data;
+
+	g_signal_handler_disconnect (monitor->monitor, monitor->monitor_id);
+	g_file_monitor_cancel (monitor->monitor);
+	g_object_unref (monitor->monitor);
+
+	g_free (monitor);
+}
+
+static void
+connection_file_changed (GFileMonitor *monitor,
+					GFile *file,
+					GFile *other_file,
+					GFileMonitorEvent event_type,
+					gpointer user_data)
+{
+	ConnectionMonitor *cm = (ConnectionMonitor *) user_data;
+	gboolean remove_connection = FALSE;
+
+	switch (event_type) {
+	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: {
+		NMConnection *new_connection;
+		GHashTable *new_settings;
+		char *filename;
+		char *ifcfg_file;
+
+		/* In case anything goes wrong */
+		remove_connection = TRUE;
+
+		filename = g_file_get_basename (file);
+		ifcfg_file = g_build_filename (IFCFG_DIR, filename, NULL);
+		g_free (filename);
+
+		new_connection = build_one_connection (ifcfg_file);
+		g_free (ifcfg_file);
+
+		if (new_connection) {
+			new_settings = nm_connection_to_hash (new_connection);
+			if (nm_connection_replace_settings (cm->connection, new_settings)) {
+				/* Nothing went wrong */
+				remove_connection = FALSE;
+				g_signal_emit_by_name (cm->plugin, "connection-updated", cm->connection);
+			}
+
+			g_object_unref (new_connection);
+		}
+
+		break;
+	}
+	case G_FILE_MONITOR_EVENT_DELETED:
+		remove_connection = TRUE;
+		break;
+	default:
+		break;
+	}
+
+	if (remove_connection) {
+		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (cm->plugin);
+
+		priv->connections = g_slist_remove (priv->connections, cm->connection);
+		g_signal_emit_by_name (cm->plugin, "connection-removed", cm->connection);
+		g_object_unref (cm->connection);
+		PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "    removed connection");
+	}
+}
+
+static void
+monitor_connection (NMSystemConfigInterface *config, NMConnection *connection, const char *filename)
+{
+	GFile *file;
+	GFileMonitor *monitor;
+
+	file = g_file_new_for_path (filename);
+	monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
+	g_object_unref (file);
+
+	if (monitor) {
+		ConnectionMonitor *cm;
+
+		cm = g_new (ConnectionMonitor, 1);
+		cm->plugin = SC_PLUGIN_IFCFG (config);
+		cm->connection = connection;
+		cm->monitor = monitor;
+		cm->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (connection_file_changed), cm);
+		g_object_set_data_full (G_OBJECT (connection), "file-monitor", cm, connection_monitor_destroy);
+	}
+}
+
+static void
+add_one_connection (NMSystemConfigInterface *config, const char *filename, gboolean emit_added)
+{
+	char *ifcfg_file;
+	NMConnection *connection;
+
+	if (!is_ifcfg_file (filename))
+		return;
+	
+	ifcfg_file = g_build_filename (IFCFG_DIR, filename, NULL);
+	connection = build_one_connection (ifcfg_file);
+	if (connection) {
+		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (config);
+
+		monitor_connection (config, connection, ifcfg_file);
+		priv->connections = g_slist_append (priv->connections, connection);
+
+		if (emit_added)
+			g_signal_emit_by_name (config, "connection-added", connection);
+	}
+
+	g_free (ifcfg_file);
+}
+
+static void
+update_default_routes (NMSystemConfigInterface *config, gboolean emit_updated)
+{
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (config);
+	GSList *iter;
+	NMConnection *connection;
+	NMSettingIP4Config *ip4_setting;
+	gboolean got_manual = FALSE;
+	guint32 default_route;
+
+	/* First, make sure we have any non-DHCP connections */
+	for (iter = priv->connections; iter; iter = iter->next) {
+		connection = NM_CONNECTION (iter->data);
+		ip4_setting = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+		if (ip4_setting && ip4_setting->manual) {
+			got_manual = TRUE;
+			break;
+		}
+	}
+
+	if (!got_manual)
+		return;
+
+	default_route = parser_parse_routes (IFCFG_DIR "/routes", NULL);
+	if (!default_route)
+		return;
+
+	for (iter = priv->connections; iter; iter = iter->next) {
+		connection = NM_CONNECTION (iter->data);
+		ip4_setting = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+		if (ip4_setting && ip4_setting->manual) {
+			GSList *address_iter;
+
+			for (address_iter = ip4_setting->addresses; address_iter; address_iter = address_iter->next) {
+				NMSettingIP4Address *addr = (NMSettingIP4Address *) address_iter->data;
+				
+				addr->gateway = default_route;
+				if (emit_updated)
+					g_signal_emit_by_name (config, "connection-updated", connection);
+			}
+		}
+	}
+}
+
+static GSList *
+get_connections (NMSystemConfigInterface *config)
+{
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (config);
+
+	if (!priv->initialized) {
+		GDir *dir;
+		const char *item;
+		GError *err = NULL;
+
+		dir = g_dir_open (IFCFG_DIR, 0, &err);
+		if (!dir) {
+			PLUGIN_WARN (PLUGIN_NAME, "couldn't access network directory '%s': %s.", IFCFG_DIR, err->message);
+			g_error_free (err);
+			return NULL;
+		}
+
+		while ((item = g_dir_read_name (dir)))
+			add_one_connection (config, item, FALSE);
+
+		g_dir_close (dir);
+		priv->initialized = TRUE;
+	}
+
+	if (!priv->connections)
+		/* No need to do any futher work, we have nothing. */
+		return priv->connections;
+
+	update_default_routes (config, FALSE);
+
+	return priv->connections;
+}
+
+static void
+ifcfg_dir_changed (GFileMonitor *monitor,
+			    GFile *file,
+			    GFile *other_file,
+			    GFileMonitorEvent event_type,
+			    gpointer user_data)
+{
+	NMSystemConfigInterface *config = NM_SYSTEM_CONFIG_INTERFACE (user_data);
+	char *name;
+
+	name = g_file_get_basename (file);
+
+	if (event_type == G_FILE_MONITOR_EVENT_CREATED) {
+		add_one_connection (config, name, TRUE);
+	}
+
+	if (!strcmp (name, "routes"))
+		update_default_routes (config, TRUE);
+
+	g_free (name);
+}
+
+static void
+init (NMSystemConfigInterface *config)
+{
+	GFile *file;
+	GFileMonitor *monitor;
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (config);
+
+	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->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (ifcfg_dir_changed), config);
+		priv->monitor = monitor;
+	}
+}
+
+static void
+release_one_connection (gpointer item, gpointer user_data)
+{
+	NMConnection *connection = NM_CONNECTION (item);
+	SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
+
+	g_signal_emit_by_name (plugin, "connection-removed", connection);
+	g_object_unref (connection);
+}
+
+static void
+sc_plugin_ifcfg_init (SCPluginIfcfg *plugin)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+	SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (object);
+
+	if (priv->connections) {
+		g_slist_foreach (priv->connections, release_one_connection, object);
+		g_slist_free (priv->connections);
+	}
+
+	if (priv->monitor) {
+		if (priv->monitor_id)
+			g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
+
+		g_file_monitor_cancel (priv->monitor);
+		g_object_unref (priv->monitor);
+	}
+
+	G_OBJECT_CLASS (sc_plugin_ifcfg_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, IFCFG_PLUGIN_NAME);
+		break;
+	case NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO:
+		g_value_set_string (value, IFCFG_PLUGIN_INFO);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+sc_plugin_ifcfg_class_init (SCPluginIfcfgClass *req_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+
+	g_type_class_add_private (req_class, sizeof (SCPluginIfcfgPrivate));
+
+	object_class->get_property = get_property;
+	object_class->dispose = dispose;
+
+	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);
+}
+
+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 GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+	static SCPluginIfcfg *singleton = NULL;
+
+	g_static_mutex_lock (&mutex);
+	if (!singleton)
+		singleton = SC_PLUGIN_IFCFG (g_object_new (SC_TYPE_PLUGIN_IFCFG, NULL));
+	g_object_ref (singleton);
+	g_static_mutex_unlock (&mutex);
+
+	return G_OBJECT (singleton);
+}

Added: trunk/system-settings/plugins/ifcfg-suse/plugin.h
==============================================================================
--- (empty file)
+++ trunk/system-settings/plugins/ifcfg-suse/plugin.h	Thu Feb 28 20:28:20 2008
@@ -0,0 +1,52 @@
+/* NetworkManager system settings service
+ *
+ * SÃren Sandmann <sandmann daimi au dk>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * (C) Copyright 2007 Red Hat, Inc.
+ */
+
+#ifndef _PLUGIN_H_
+#define _PLUGIN_H_
+
+#include <glib-object.h>
+
+#define PLUGIN_NAME "ifcfg"
+
+#define SC_TYPE_PLUGIN_IFCFG            (sc_plugin_ifcfg_get_type ())
+#define SC_PLUGIN_IFCFG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SC_TYPE_PLUGIN_IFCFG, SCPluginIfcfg))
+#define SC_PLUGIN_IFCFG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SC_TYPE_PLUGIN_IFCFG, SCPluginIfcfgClass))
+#define SC_IS_PLUGIN_IFCFG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_PLUGIN_IFCFG))
+#define SC_IS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SC_TYPE_PLUGIN_IFCFG))
+#define SC_PLUGIN_IFCFG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SC_TYPE_PLUGIN_IFCFG, SCPluginIfcfgClass))
+
+typedef struct _SCPluginIfcfg SCPluginIfcfg;
+typedef struct _SCPluginIfcfgClass SCPluginIfcfgClass;
+
+struct _SCPluginIfcfg {
+	GObject parent;
+};
+
+struct _SCPluginIfcfgClass {
+	GObjectClass parent;
+};
+
+GType sc_plugin_ifcfg_get_type (void);
+
+GQuark ifcfg_plugin_error_quark (void);
+
+#endif	/* _PLUGIN_H_ */
+

Added: trunk/system-settings/plugins/ifcfg-suse/shvar.c
==============================================================================
--- (empty file)
+++ trunk/system-settings/plugins/ifcfg-suse/shvar.c	Thu Feb 28 20:28:20 2008
@@ -0,0 +1,402 @@
+/*
+ * shvar.c
+ *
+ * Implementation of non-destructively reading/writing files containing
+ * only shell variable declarations and full-line comments.
+ *
+ * Includes explicit inheritance mechanism intended for use with
+ * Red Hat Linux ifcfg-* files.  There is no protection against
+ * inheritance loops; they will generally cause stack overflows.
+ * Furthermore, they are only intended for one level of inheritance;
+ * the value setting algorithm assumes this.
+ *
+ * Copyright 1999,2000 Red Hat, Inc.
+ *
+ * This 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "shvar.h"
+
+/* Open the file <name>, returning a shvarFile on success and NULL on failure.
+   Add a wrinkle to let the caller specify whether or not to create the file
+   (actually, return a structure anyway) if it doesn't exist. */
+static shvarFile *
+svOpenFile(const char *name, gboolean create)
+{
+    shvarFile *s = NULL;
+    int closefd = 0;
+
+    s = g_malloc0(sizeof(shvarFile));
+
+#if 1   /* NetworkManager local change */
+    s->fd = open(name, O_RDONLY); /* NOT O_CREAT */
+    if (s->fd != -1) closefd = 1;
+#else
+    s->fd = open(name, O_RDWR); /* NOT O_CREAT */
+    if (s->fd == -1) {
+	/* try read-only */
+	s->fd = open(name, O_RDONLY); /* NOT O_CREAT */
+	if (s->fd != -1) closefd = 1;
+    }
+#endif
+    s->fileName = g_strdup(name);
+
+    if (s->fd != -1) {
+	struct stat buf;
+	char *p, *q;
+
+	if (fstat(s->fd, &buf) < 0) goto bail;
+	s->arena = g_malloc0(buf.st_size + 1);
+
+	if (read(s->fd, s->arena, buf.st_size) < 0) goto bail;
+
+	/* we'd use g_strsplit() here, but we want a list, not an array */
+	for(p = s->arena; (q = strchr(p, '\n')) != NULL; p = q + 1) {
+		s->lineList = g_list_append(s->lineList, g_strndup(p, q - p));
+	}
+
+	/* closefd is set if we opened the file read-only, so go ahead and
+	   close it, because we can't write to it anyway */
+	if (closefd) {
+	    close(s->fd);
+	    s->fd = -1;
+	}
+
+        return s;
+    }
+
+    if (create) {
+        return s;
+    }
+
+bail:
+    if (s->fd != -1) close(s->fd);
+    if (s->arena) g_free (s->arena);
+    if (s->fileName) g_free (s->fileName);
+    g_free (s);
+    return NULL;
+}
+
+/* Open the file <name>, return shvarFile on success, NULL on failure */
+shvarFile *
+svNewFile(const char *name)
+{
+    return svOpenFile(name, FALSE);
+}
+
+/* Create a new file structure, returning actual data if the file exists,
+ * and a suitable starting point if it doesn't. */
+shvarFile *
+svCreateFile(const char *name)
+{
+    return svOpenFile(name, TRUE);
+}
+
+/* remove escaped characters in place */
+static void
+unescape(char *s) {
+    int len, i;
+
+    len = strlen(s);
+    if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
+	i = len - 2;
+	if (i == 0)
+	  s[0] = '\0';
+	else {
+	  memmove(s, s+1, i);
+	  s[i+1] = '\0';
+	  len = i;
+	}
+    }
+    for (i = 0; i < len; i++) {
+	if (s[i] == '\\') {
+	    memmove(s+i, s+i+1, len-(i+1));
+	    len--;
+	}
+	s[len] = '\0';
+    }
+}
+
+
+/* create a new string with all necessary characters escaped.
+ * caller must free returned string
+ */
+static const char escapees[] = "\"'\\$~`";		/* must be escaped */
+static const char spaces[] = " \t|&;()<>";		/* only require "" */
+static char *
+escape(const char *s) {
+    char *new;
+    int i, j, mangle = 0, space = 0;
+    int newlen, slen;
+    static int esclen, splen;
+
+    if (!esclen) esclen = strlen(escapees);
+    if (!splen) splen = strlen(spaces);
+    slen = strlen(s);
+
+    for (i = 0; i < slen; i++) {
+	if (strchr(escapees, s[i])) mangle++;
+	if (strchr(spaces, s[i])) space++;
+    }
+    if (!mangle && !space) return strdup(s);
+
+    newlen = slen + mangle + 3;	/* 3 is extra ""\0 */
+    new = g_malloc0(newlen);
+    if (!new) return NULL;
+
+    j = 0;
+    new[j++] = '"';
+    for (i = 0; i < slen; i++) {
+	if (strchr(escapees, s[i])) {
+	    new[j++] = '\\';
+	}
+	new[j++] = s[i];
+    }
+    new[j++] = '"';
+    g_assert(j == slen + mangle + 2); /* j is the index of the '\0' */
+
+    return new;
+}
+
+/* Get the value associated with the key, and leave the current pointer
+ * pointing at the line containing the value.  The char* returned MUST
+ * be freed by the caller.
+ */
+char *
+svGetValue(shvarFile *s, const char *key)
+{
+    char *value = NULL;
+    char *line;
+    char *keyString;
+    int len;
+
+    g_assert(s);
+    g_assert(key);
+
+    keyString = g_malloc0(strlen(key) + 2);
+    strcpy(keyString, key);
+    keyString[strlen(key)] = '=';
+    len = strlen(keyString);
+
+    for (s->current = s->lineList; s->current; s->current = s->current->next) {
+	line = s->current->data;
+	if (!strncmp(keyString, line, len)) {
+	    value = g_strdup(line + len);
+	    unescape(value);
+	    break;
+	}
+    }
+    g_free(keyString);
+
+    if (value) {
+	if (value[0]) {
+	    return value;
+	} else {
+	    g_free(value);
+	    return NULL;
+	}
+    }
+    if (s->parent) value = svGetValue(s->parent, key);
+    return value;
+}
+
+/* return 1 if <key> resolves to any truth value (e.g. "yes", "y", "true")
+ * return 0 if <key> resolves to any non-truth value (e.g. "no", "n", "false")
+ * return <default> otherwise
+ */
+int
+svTrueValue(shvarFile *s, const char *key, int def)
+{
+    char *tmp;
+    int returnValue = def;
+
+    tmp = svGetValue(s, key);
+    if (!tmp) return returnValue;
+
+    if ( (!strcasecmp("yes", tmp)) ||
+	 (!strcasecmp("true", tmp)) ||
+	 (!strcasecmp("t", tmp)) ||
+	 (!strcasecmp("y", tmp)) ) returnValue = 1;
+    else
+    if ( (!strcasecmp("no", tmp)) ||
+	 (!strcasecmp("false", tmp)) ||
+	 (!strcasecmp("f", tmp)) ||
+	 (!strcasecmp("n", tmp)) ) returnValue = 0;
+
+    g_free (tmp);
+    return returnValue;
+}
+
+
+/* Set the variable <key> equal to the value <value>.
+ * If <key> does not exist, and the <current> pointer is set, append
+ * the key=value pair after that line.  Otherwise, prepend the pair
+ * to the top of the file.  Here's the algorithm, as the C code
+ * seems to be rather dense:
+ *
+ * if (value == NULL), then:
+ *     if val2 (parent): change line to key= or append line key=
+ *     if val1 (this)  : delete line
+ *     else noop
+ * else use this table:
+ *                                val2
+ *             NULL              value               other
+ * v   NULL    append line       noop                append line
+ * a
+ * l   value   noop              noop                noop
+ * 1
+ *     other   change line       delete line         change line
+ *
+ * No changes are ever made to the parent config file, only to the
+ * specific file passed on the command line.
+ *
+ */
+void
+svSetValue(shvarFile *s, const char *key, const char *value)
+{
+    char *newval = NULL, *val1 = NULL, *val2 = NULL;
+    char *keyValue;
+
+    g_assert(s);
+    g_assert(key);
+    /* value may be NULL */
+
+    if (value) newval = escape(value);
+    keyValue = g_strdup_printf("%s=%s", key, newval ? newval : "");
+
+    val1 = svGetValue(s, key);
+    if (val1 && newval && !strcmp(val1, newval)) goto bail;
+    if (s->parent) val2 = svGetValue(s->parent, key);
+
+    if (!newval || !newval[0]) {
+	/* delete value somehow */
+	if (val2) {
+	    /* change/append line to get key= */
+	    if (s->current) s->current->data = keyValue;
+	    else s->lineList = g_list_append(s->lineList, keyValue);
+	    s->freeList = g_list_append(s->freeList, keyValue);
+	    s->modified = 1;
+	} else if (val1) {
+	    /* delete line */
+	    s->lineList = g_list_remove_link(s->lineList, s->current);
+	    g_list_free_1(s->current);
+	    s->modified = 1;
+	    goto bail; /* do not need keyValue */
+	}
+	goto end;
+    }
+
+    if (!val1) {
+	if (val2 && !strcmp(val2, newval)) goto end;
+	/* append line */
+	s->lineList = g_list_append(s->lineList, keyValue);
+	s->freeList = g_list_append(s->freeList, keyValue);
+	s->modified = 1;
+	goto end;
+    }
+
+    /* deal with a whole line of noops */
+    if (val1 && !strcmp(val1, newval)) goto end;
+
+    /* At this point, val1 && val1 != value */
+    if (val2 && !strcmp(val2, newval)) {
+	/* delete line */
+	s->lineList = g_list_remove_link(s->lineList, s->current);
+	g_list_free_1(s->current);
+	s->modified = 1;
+	goto bail; /* do not need keyValue */
+    } else {
+	/* change line */
+	if (s->current) s->current->data = keyValue;
+	else s->lineList = g_list_append(s->lineList, keyValue);
+	s->freeList = g_list_append(s->freeList, keyValue);
+	s->modified = 1;
+    }
+
+end:
+    if (newval) free(newval);
+    if (val1) free(val1);
+    if (val2) free(val2);
+    return;
+
+bail:
+    if (keyValue) free (keyValue);
+    goto end;
+}
+
+/* Write the current contents iff modified.  Returns -1 on error
+ * and 0 on success.  Do not write if no values have been modified.
+ * The mode argument is only used if creating the file, not if
+ * re-writing an existing file, and is passed unchanged to the
+ * open() syscall.
+ */
+int
+svWriteFile(shvarFile *s, int mode)
+{
+    FILE *f;
+    int tmpfd;
+
+    if (s->modified) {
+	if (s->fd == -1)
+	    s->fd = open(s->fileName, O_WRONLY|O_CREAT, mode);
+	if (s->fd == -1)
+	    return -1;
+	if (ftruncate(s->fd, 0) < 0)
+	    return -1;
+
+	tmpfd = dup(s->fd);
+	f = fdopen(tmpfd, "w");
+	fseek(f, 0, SEEK_SET);
+	for (s->current = s->lineList; s->current; s->current = s->current->next) {
+	    char *line = s->current->data;
+	    fprintf(f, "%s\n", line);
+	}
+	fclose(f);
+    }
+
+    return 0;
+}
+
+ 
+/* Close the file descriptor (if open) and delete the shvarFile.
+ * Returns -1 on error and 0 on success.
+ */
+int
+svCloseFile(shvarFile *s)
+{
+
+    g_assert(s);
+
+    if (s->fd != -1) close(s->fd);
+
+    g_free(s->arena);
+    for (s->current = s->freeList; s->current; s->current = s->current->next) {
+        g_free(s->current->data);
+    }
+    g_free(s->fileName);
+    g_list_free(s->freeList);
+    g_list_free(s->lineList); /* implicitly frees s->current */
+    g_free(s);
+    return 0;
+}

Added: trunk/system-settings/plugins/ifcfg-suse/shvar.h
==============================================================================
--- (empty file)
+++ trunk/system-settings/plugins/ifcfg-suse/shvar.h	Thu Feb 28 20:28:20 2008
@@ -0,0 +1,103 @@
+/*
+ * shvar.h
+ *
+ * Interface for non-destructively reading/writing files containing
+ * only shell variable declarations and full-line comments.
+ *
+ * Includes explicit inheritance mechanism intended for use with
+ * Red Hat Linux ifcfg-* files.  There is no protection against
+ * inheritance loops; they will generally cause stack overflows.
+ * Furthermore, they are only intended for one level of inheritance;
+ * the value setting algorithm assumes this.
+ *
+ * Copyright 1999 Red Hat, Inc.
+ *
+ * This 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#ifndef _SHVAR_H
+#define _SHVAR_H
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _shvarFile shvarFile;
+struct _shvarFile {
+	char		*fileName;	/* read-only */
+	int		fd;		/* read-only */
+	char		*arena;		/* ignore */
+	GList		*lineList;	/* read-only */
+	GList		*freeList;	/* ignore */
+	GList		*current;	/* set implicitly or explicitly,
+					   points to element of lineList */
+	shvarFile	*parent;	/* set explicitly */
+	int		modified;	/* ignore */
+};
+
+
+/* Create the file <name>, return shvarFile on success, NULL on failure */
+shvarFile *
+svCreateFile(const char *name);
+
+/* Open the file <name>, return shvarFile on success, NULL on failure */
+shvarFile *
+svNewFile(const char *name);
+
+/* Get the value associated with the key, and leave the current pointer
+ * pointing at the line containing the value.  The char* returned MUST
+ * be freed by the caller.
+ */
+char *
+svGetValue(shvarFile *s, const char *key);
+
+/* return 1 if <key> resolves to any truth value (e.g. "yes", "y", "true")
+ * return 0 if <key> resolves to any non-truth value (e.g. "no", "n", "false")
+ * return <def> otherwise
+ */
+int
+svTrueValue(shvarFile *s, const char *key, int def);
+
+/* Set the variable <key> equal to the value <value>.
+ * If <key> does not exist, and the <current> pointer is set, append
+ * the key=value pair after that line.  Otherwise, prepend the pair
+ * to the top of the file.
+ */
+void
+svSetValue(shvarFile *s, const char *key, const char *value);
+
+
+/* Write the current contents iff modified.  Returns -1 on error
+ * and 0 on success.  Do not write if no values have been modified.
+ * The mode argument is only used if creating the file, not if
+ * re-writing an existing file, and is passed unchanged to the
+ * open() syscall.
+ */
+int
+svWriteFile(shvarFile *s, int mode);
+
+/* Close the file descriptor (if open) and delete the shvarFile.
+ * Returns -1 on error and 0 on success.
+ */
+int
+svCloseFile(shvarFile *s);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! _SHVAR_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]