/* * uci.c: UCI binding for the switch configuration utility * * Copyright (C) 2009 Felix Fietkau * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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", .val = "1" }, { .name = "enable_vlan", .val = "1" }, }; static struct swlib_setting *settings; static struct swlib_setting **head; static bool swlib_match_name(struct switch_dev *dev, const char *name) { return (strcmp(name, dev->dev_name) == 0 || strcmp(name, dev->alias) == 0); } static void 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; 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_element *e; struct uci_section *s; struct uci_option *o; struct uci_ptr ptr; struct switch_val val; int i; settings = NULL; head = &settings; uci_foreach_element(&p->sections, e) { struct uci_element *n; s = uci_to_section(e); if (strcmp(s->type, "switch") != 0) continue; uci_foreach_element(&s->options, n) { struct uci_option *o = uci_to_option(n); if (strcmp(n->name, "name") != 0) continue; if (o->type != UCI_TYPE_STRING) continue; if (swlib_match_name(dev, o->v.string)) goto found; break; } if (!swlib_match_name(dev, e->name)) 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_vlan")) { char *devn = NULL, *vlan = NULL, *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 (!swlib_match_name(dev, devn)) devn = NULL; } else if (!strcmp(os->name, "vlan")) { vlan = o->v.string; } } if (!devn || !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); } } uci_foreach_element(&p->sections, e) { struct uci_element *os; char *devn = NULL, *port = NULL, *port_err = NULL; int port_n; s = uci_to_section(e); if (strcmp(s->type, "switch_port")) continue; 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 (!swlib_match_name(dev, devn)) devn = NULL; } else if (!strcmp(os->name, "port")) { port = o->v.string; } } if (!devn || !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); } 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; }