From 2fe29deb903e4f4a2e85eee7cdcd34f258ca0b33 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@openwrt.org>
Date: Tue, 21 Apr 2009 01:35:30 +0000
Subject: swconfig: implement uci loading support

SVN-Revision: 15315
---
 package/swconfig/Makefile     |   4 +-
 package/swconfig/src/Makefile |   2 +-
 package/swconfig/src/cli.c    | 133 ++++++++++++++-----------
 package/swconfig/src/swlib.c  |  43 ++++++++
 package/swconfig/src/swlib.h  |  21 +++-
 package/swconfig/src/uci.c    | 224 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 363 insertions(+), 64 deletions(-)
 create mode 100644 package/swconfig/src/uci.c

diff --git a/package/swconfig/Makefile b/package/swconfig/Makefile
index 058e1e2738..e18f556505 100644
--- a/package/swconfig/Makefile
+++ b/package/swconfig/Makefile
@@ -17,7 +17,7 @@ include $(INCLUDE_DIR)/package.mk
 define Package/swconfig
   SECTION:=base
   CATEGORY:=Base system
-  DEPENDS:=@LINUX_2_6_26||LINUX_2_6_27||LINUX_2_6_28||LINUX_2_6_29
+  DEPENDS:=@LINUX_2_6_26||LINUX_2_6_27||LINUX_2_6_28||LINUX_2_6_29 +libuci
   TITLE:=Switch configuration utility
 endef
 
@@ -34,7 +34,7 @@ define Build/Compile
 	CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \
 	$(MAKE) -C $(PKG_BUILD_DIR) \
 		$(TARGET_CONFIGURE_OPTS) \
-		LIBS="$(STAGING_DIR)/usr/lib/libnl.a -lm"
+		LIBS="-L$(STAGING_DIR)/usr/lib $(STAGING_DIR)/usr/lib/libnl.a -lm -luci"
 endef
 
 define Package/swconfig/install
diff --git a/package/swconfig/src/Makefile b/package/swconfig/src/Makefile
index 64816af541..7b95ca2166 100644
--- a/package/swconfig/src/Makefile
+++ b/package/swconfig/src/Makefile
@@ -8,5 +8,5 @@ all: swconfig
 %.o: %.c
 	$(CC) $(CFLAGS) -c -o $@ $^
 
-swconfig: cli.o swlib.o
+swconfig: cli.o swlib.o uci.o
 	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
diff --git a/package/swconfig/src/cli.c b/package/swconfig/src/cli.c
index c6035e585c..cf3a519482 100644
--- a/package/swconfig/src/cli.c
+++ b/package/swconfig/src/cli.c
@@ -22,6 +22,7 @@
 #include <getopt.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <uci.h>
 
 #include <linux/types.h>
 #include <linux/netlink.h>
@@ -32,10 +33,14 @@
 #include <linux/switch.h>
 #include "swlib.h"
 
-#define GET		1
-#define SET		2
+enum {
+	GET,
+	SET,
+	LOAD
+};
 
-void print_attrs(struct switch_attr *attr)
+static void
+print_attrs(const struct switch_attr *attr)
 {
 	int i = 0;
 	while (attr) {
@@ -62,7 +67,8 @@ void print_attrs(struct switch_attr *attr)
 	}
 }
 
-void list_attributes(struct switch_dev *dev)
+static void
+list_attributes(struct switch_dev *dev)
 {
 	printf("Switch %d: %s(%s), ports: %d, vlans: %d\n", dev->id, dev->dev_name, dev->name, dev->ports, dev->vlans);
 	printf("     --switch\n");
@@ -73,10 +79,38 @@ void list_attributes(struct switch_dev *dev)
 	print_attrs(dev->port_ops);
 }
 
-void print_usage(void)
+static void
+print_usage(void)
 {
-	printf("swconfig dev <dev> [port <port>|vlan <vlan>] (help|set <key> <value>|get <key>)\n");
-	exit(0);
+	printf("swconfig dev <dev> [port <port>|vlan <vlan>] (help|set <key> <value>|get <key>|load <config>)\n");
+	exit(1);
+}
+
+static void
+swconfig_load_uci(struct switch_dev *dev, const char *name)
+{
+	struct uci_context *ctx;
+	struct uci_package *p = NULL;
+	struct uci_element *e;
+	int ret = -1;
+
+	ctx = uci_alloc_context();
+	if (!ctx)
+		return;
+
+	uci_load(ctx, name, &p);
+	if (!p) {
+		uci_perror(ctx, "Failed to load config file: ");
+		goto out;
+	}
+
+	ret = swlib_apply_from_uci(dev, p);
+	if (ret < 0)
+		fprintf(stderr, "Failed to apply configuration for switch '%s'\n", dev->dev_name);
+
+out:
+	uci_free_context(ctx);
+	exit(ret);
 }
 
 int main(int argc, char **argv)
@@ -109,22 +143,18 @@ int main(int argc, char **argv)
 	for(i = 3; i < argc; i++)
 	{
 		int p;
-		if(!strcmp(argv[i], "help"))
-		{
+		if (!strcmp(argv[i], "help")) {
 			chelp = 1;
 			continue;
 		}
-		if(i + 1 >= argc)
+		if( i + 1 >= argc)
 			print_usage();
 		p = atoi(argv[i + 1]);
-		if(!strcmp(argv[i], "port"))
-		{
+		if (!strcmp(argv[i], "port")) {
 			cport = p;
-		} else if(!strcmp(argv[i], "vlan"))
-		{
+		} else if (!strcmp(argv[i], "vlan")) {
 			cvlan = p;
-		} else if(!strcmp(argv[i], "set"))
-		{
+		} else if (!strcmp(argv[i], "set")) {
 			if(argc <= i + 1)
 				print_usage();
 			cmd = SET;
@@ -134,11 +164,16 @@ int main(int argc, char **argv)
 			else
 				cvalue = NULL;
 			i++;
-		} else if(!strcmp(argv[i], "get"))
-		{
+		} else if (!strcmp(argv[i], "get")) {
 			cmd = GET;
 			ckey = argv[i + 1];
-		} else{
+		} else if (!strcmp(argv[i], "load")) {
+			if ((cport >= 0) || (cvlan >= 0))
+				print_usage();
+
+			ckey = argv[i + 1];
+			cmd = LOAD;
+		} else {
 			print_usage();
 		}
 		i++;
@@ -163,17 +198,19 @@ int main(int argc, char **argv)
 		goto out;
 	}
 
-	if(cport > -1)
-		a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_PORT, ckey);
-	else if(cvlan > -1)
-		a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_VLAN, ckey);
-	else
-		a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_GLOBAL, ckey);
+	if (cmd != LOAD) {
+		if(cport > -1)
+			a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_PORT, ckey);
+		else if(cvlan > -1)
+			a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_VLAN, ckey);
+		else
+			a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_GLOBAL, ckey);
 
-	if(!a)
-	{
-		fprintf(stderr, "Unknown attribute \"%s\"\n", ckey);
-		goto out;
+		if(!a)
+		{
+			fprintf(stderr, "Unknown attribute \"%s\"\n", ckey);
+			goto out;
+		}
 	}
 
 	switch(cmd)
@@ -183,38 +220,10 @@ int main(int argc, char **argv)
 				(cvalue == NULL))
 			print_usage();
 
-		switch(a->type) {
-		case SWITCH_TYPE_INT:
-			val.value.i = atoi(cvalue);
-			break;
-		case SWITCH_TYPE_STRING:
-			val.value.s = cvalue;
-			break;
-		case SWITCH_TYPE_PORTS:
-			val.len = 0;
-			while(cvalue && *cvalue)
-			{
-				ports[val.len].flags = 0;
-				ports[val.len].id = strtol(cvalue, &cvalue, 10);
-				while(*cvalue && !isspace(*cvalue)) {
-					if (*cvalue == 't')
-						ports[val.len].flags |= SWLIB_PORT_FLAG_TAGGED;
-					cvalue++;
-				}
-				if (*cvalue)
-					cvalue++;
-				val.len++;
-			}
-			val.value.ports = ports;
-			break;
-		default:
-			break;
-		}
 		if(cvlan > -1)
-			val.port_vlan = cvlan;
-		if(cport > -1)
-			val.port_vlan = cport;
-		if(swlib_set_attr(dev, a, &val) < 0)
+			cport = cvlan;
+
+		if(swlib_set_attr_string(dev, a, cport, cvalue) < 0)
 		{
 			fprintf(stderr, "failed\n");
 			retval = -1;
@@ -245,6 +254,10 @@ int main(int argc, char **argv)
 			printf("\n");
 			break;
 		}
+		break;
+	case LOAD:
+		swconfig_load_uci(dev, ckey);
+		break;
 	}
 
 out:
diff --git a/package/swconfig/src/swlib.c b/package/swconfig/src/swlib.c
index 3fde816414..fbc036555d 100644
--- a/package/swconfig/src/swlib.c
+++ b/package/swconfig/src/swlib.c
@@ -343,6 +343,49 @@ swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_v
 	return swlib_call(cmd, NULL, send_attr_val, val);
 }
 
+int swlib_set_attr_string(struct switch_dev *dev, struct switch_attr *a, int port_vlan, const char *str)
+{
+	struct switch_port *ports;
+	struct switch_val val;
+	char *ptr;
+
+	memset(&val, 0, sizeof(val));
+	val.port_vlan = port_vlan;
+	switch(a->type) {
+	case SWITCH_TYPE_INT:
+		val.value.i = atoi(str);
+		break;
+	case SWITCH_TYPE_STRING:
+		val.value.s = str;
+		break;
+	case SWITCH_TYPE_PORTS:
+		ports = alloca(sizeof(struct switch_port) * dev->ports);
+		memset(ports, 0, sizeof(struct switch_port) * dev->ports);
+		val.len = 0;
+		ptr = (char *)str;
+		while(ptr && *ptr)
+		{
+			ports[val.len].flags = 0;
+			ports[val.len].id = strtoul(ptr, &ptr, 10);
+			while(*ptr && !isspace(*ptr)) {
+				if (*ptr == 't')
+					ports[val.len].flags |= SWLIB_PORT_FLAG_TAGGED;
+				ptr++;
+			}
+			if (*ptr)
+				ptr++;
+			val.len++;
+		}
+		val.value.ports = ports;
+		break;
+	case SWITCH_TYPE_NOVAL:
+		break;
+	default:
+		return -1;
+	}
+	return swlib_set_attr(dev, a, &val);
+}
+
 
 struct attrlist_arg {
 	int id;
diff --git a/package/swconfig/src/swlib.h b/package/swconfig/src/swlib.h
index e00ff47a51..b3c6769de1 100644
--- a/package/swconfig/src/swlib.h
+++ b/package/swconfig/src/swlib.h
@@ -1,7 +1,7 @@
 /*
  * swlib.h: Switch configuration API (user space part)
  *
- * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
@@ -110,6 +110,7 @@ struct switch_dev;
 struct switch_attr;
 struct switch_port;
 struct switch_val;
+struct uci_package;
 
 struct switch_dev {
 	int id;
@@ -199,6 +200,17 @@ struct switch_attr *swlib_lookup_attr(struct switch_dev *dev,
 int swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr,
 		struct switch_val *val);
 
+/**
+ * swlib_set_attr_string: set the value for an attribute with type conversion
+ * @dev: switch device struct
+ * @attr: switch attribute struct
+ * @port_vlan: port or vlan (if applicable)
+ * @str: string value
+ * returns 0 on success
+ */
+int swlib_set_attr_string(struct switch_dev *dev, struct switch_attr *attr,
+		int port_vlan, const char *str);
+
 /**
  * swlib_get_attr: get the value for an attribute
  * @dev: switch device struct
@@ -210,4 +222,11 @@ int swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr,
 int swlib_get_attr(struct switch_dev *dev, struct switch_attr *attr,
 		struct switch_val *val);
 
+/**
+ * swlib_apply_from_uci: set up the switch from a uci configuration
+ * @dev: switch device struct
+ * @p: uci package which contains the desired global config
+ */
+int swlib_apply_from_uci(struct switch_dev *dev, struct uci_package *p);
+
 #endif
diff --git a/package/swconfig/src/uci.c b/package/swconfig/src/uci.c
new file mode 100644
index 0000000000..2df837d278
--- /dev/null
+++ b/package/swconfig/src/uci.c
@@ -0,0 +1,224 @@
+/*
+ * uci.c: UCI binding for the switch configuration utility
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundatio.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <uci.h>
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <linux/switch.h>
+#include "swlib.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+#endif
+
+struct swlib_setting {
+	struct switch_attr *attr;
+	const char *name;
+	int port_vlan;
+	const char *val;
+	struct swlib_setting *next;
+};
+
+struct swlib_setting early_settings[] = {
+	{ .name = "reset" },
+	{ .name = "enable_vlan" },
+};
+
+static struct swlib_setting *settings;
+static struct swlib_setting **head;
+
+static int
+swlib_map_settings(struct switch_dev *dev, int type, int port_vlan, struct uci_section *s)
+{
+	struct swlib_setting *setting;
+	struct switch_attr *attr;
+	struct uci_element *e;
+	struct uci_option *o;
+	int i;
+
+	uci_foreach_element(&s->options, e) {
+		o = uci_to_option(e);
+
+		if (o->type != UCI_TYPE_STRING)
+			continue;
+
+		if (!strcmp(e->name, "device"))
+			continue;
+
+		/* map early settings */
+		if (type == SWLIB_ATTR_GROUP_GLOBAL) {
+			int i;
+
+			for (i = 0; i < ARRAY_SIZE(early_settings); i++) {
+				if (strcmp(e->name, early_settings[i].name) != 0)
+					continue;
+
+				early_settings[i].val = o->v.string;
+				goto skip;
+			}
+		}
+
+		attr = swlib_lookup_attr(dev, type, e->name);
+		if (!attr)
+			continue;
+
+		setting = malloc(sizeof(struct swlib_setting));
+		memset(setting, 0, sizeof(struct swlib_setting));
+		setting->attr = attr;
+		setting->port_vlan = port_vlan;
+		setting->val = o->v.string;
+		*head = setting;
+		head = &setting->next;
+skip:
+		continue;
+	}
+}
+
+int swlib_apply_from_uci(struct switch_dev *dev, struct uci_package *p)
+{
+	struct switch_attr *attr;
+	struct uci_context *ctx = p->ctx;
+	struct uci_element *e;
+	struct uci_section *s;
+	struct uci_option *o;
+	struct switch_val val;
+	int i;
+
+	settings = NULL;
+	head = &settings;
+
+	uci_foreach_element(&p->sections, e) {
+		s = uci_to_section(e);
+
+		if (strcmp(s->type, "switch") != 0)
+			continue;
+
+		if (strcmp(e->name, dev->dev_name) != 0)
+			continue;
+
+		goto found;
+	}
+
+	/* not found */
+	return -1;
+
+found:
+	/* look up available early options, which need to be taken care
+	 * of in the correct order */
+	for (i = 0; i < ARRAY_SIZE(early_settings); i++) {
+		early_settings[i].attr = swlib_lookup_attr(dev,
+			SWLIB_ATTR_GROUP_GLOBAL, early_settings[i].name);
+	}
+	swlib_map_settings(dev, SWLIB_ATTR_GROUP_GLOBAL, 0, s);
+
+	/* look for port or vlan sections */
+	uci_foreach_element(&p->sections, e) {
+		struct uci_element *os;
+		s = uci_to_section(e);
+
+		if (!strcmp(s->type, "switch_port")) {
+			char *devn, *port, *port_err = NULL;
+			int port_n;
+
+			uci_foreach_element(&s->options, os) {
+				o = uci_to_option(os);
+				if (o->type != UCI_TYPE_STRING)
+					continue;
+
+				if (!strcmp(os->name, "device")) {
+					devn = o->v.string;
+					if (strcmp(devn, dev->dev_name) != 0)
+						devn = NULL;
+				} else if (!strcmp(os->name, "port")) {
+					port = o->v.string;
+				}
+			}
+			if (!dev || !port || !port[0])
+				continue;
+
+			port_n = strtoul(port, &port_err, 0);
+			if (port_err && port_err[0])
+				continue;
+
+			swlib_map_settings(dev, SWLIB_ATTR_GROUP_PORT, port_n, s);
+		} else if (!strcmp(s->type, "switch_vlan")) {
+			char *devn, *vlan, *vlan_err = NULL;
+			int vlan_n;
+
+			uci_foreach_element(&s->options, os) {
+				o = uci_to_option(os);
+				if (o->type != UCI_TYPE_STRING)
+					continue;
+
+				if (!strcmp(os->name, "device")) {
+					devn = o->v.string;
+					if (strcmp(devn, dev->dev_name) != 0)
+						devn = NULL;
+				} else if (!strcmp(os->name, "vlan")) {
+					vlan = o->v.string;
+				}
+			}
+			if (!dev || !vlan || !vlan[0])
+				continue;
+
+			vlan_n = strtoul(vlan, &vlan_err, 0);
+			if (vlan_err && vlan_err[0])
+				continue;
+
+			swlib_map_settings(dev, SWLIB_ATTR_GROUP_VLAN, vlan_n, s);
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(early_settings); i++) {
+		struct swlib_setting *st = &early_settings[i];
+		if (!st->attr || !st->val)
+			continue;
+		swlib_set_attr_string(dev, st->attr, st->port_vlan, st->val);
+
+	}
+
+	while (settings) {
+		struct swlib_setting *st = settings;
+
+		swlib_set_attr_string(dev, st->attr, st->port_vlan, st->val);
+		st = st->next;
+		free(settings);
+		settings = st;
+	}
+
+	/* Apply the config */
+	attr = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_GLOBAL, "apply");
+	if (!attr)
+		return 0;
+
+	memset(&val, 0, sizeof(val));
+	swlib_set_attr(dev, attr, &val);
+
+	return 0;
+}
-- 
cgit v1.2.3