123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- /*
- * ucimap - library for mapping uci sections into data structures
- * Copyright (C) 2008 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 Foundation
- *
- * 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 <strings.h>
- #include <stdbool.h>
- #include <string.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include "ucimap.h"
- struct uci_alloc {
- enum ucimap_type type;
- union {
- void **ptr;
- } data;
- };
- struct uci_fixup {
- struct list_head list;
- struct uci_sectionmap *sm;
- const char *name;
- enum ucimap_type type;
- union ucimap_data *data;
- };
- #define ucimap_foreach_option(_sm, _o) \
- if (!(_sm)->options_size) \
- (_sm)->options_size = sizeof(struct uci_optmap); \
- for (_o = &(_sm)->options[0]; \
- ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
- (_sm)->options_size * (_sm)->n_options); \
- _o = (struct uci_optmap *) ((char *)(_o) + \
- (_sm)->options_size))
- static inline bool
- ucimap_is_alloc(enum ucimap_type type)
- {
- switch(type & UCIMAP_SUBTYPE) {
- case UCIMAP_STRING:
- return true;
- default:
- return false;
- }
- }
- static inline bool
- ucimap_is_fixup(enum ucimap_type type)
- {
- switch(type & UCIMAP_SUBTYPE) {
- case UCIMAP_SECTION:
- return true;
- default:
- return false;
- }
- }
- static inline bool
- ucimap_is_simple(enum ucimap_type type)
- {
- return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
- }
- static inline bool
- ucimap_is_list(enum ucimap_type type)
- {
- return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
- }
- static inline bool
- ucimap_is_custom(enum ucimap_type type)
- {
- return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
- }
- static inline void *
- ucimap_section_ptr(struct ucimap_section_data *sd)
- {
- return ((char *) sd - sd->sm->smap_offset);
- }
- static inline union ucimap_data *
- ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
- {
- void *data;
- data = (char *) ucimap_section_ptr(sd) + om->offset;
- return data;
- }
- int
- ucimap_init(struct uci_map *map)
- {
- INIT_LIST_HEAD(&map->sdata);
- INIT_LIST_HEAD(&map->fixup);
- return 0;
- }
- static void
- ucimap_free_item(struct uci_alloc *a)
- {
- switch(a->type & UCIMAP_TYPE) {
- case UCIMAP_SIMPLE:
- case UCIMAP_LIST:
- free(a->data.ptr);
- break;
- }
- }
- static void
- ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
- {
- struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
- a->type = UCIMAP_SIMPLE;
- a->data.ptr = ptr;
- }
- static void
- ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
- {
- void *section;
- int i;
- section = ucimap_section_ptr(sd);
- if (!list_empty(&sd->list))
- list_del(&sd->list);
- if (sd->sm->free)
- sd->sm->free(map, section);
- for (i = 0; i < sd->allocmap_len; i++) {
- ucimap_free_item(&sd->allocmap[i]);
- }
- free(sd->allocmap);
- free(sd);
- }
- void
- ucimap_cleanup(struct uci_map *map)
- {
- struct list_head *ptr, *tmp;
- list_for_each_safe(ptr, tmp, &map->sdata) {
- struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
- ucimap_free_section(map, sd);
- }
- }
- static void
- ucimap_add_fixup(struct uci_map *map, union ucimap_data *data, struct uci_optmap *om, const char *str)
- {
- struct uci_fixup *f;
- f = malloc(sizeof(struct uci_fixup));
- if (!f)
- return;
- INIT_LIST_HEAD(&f->list);
- f->sm = om->data.sm;
- f->name = str;
- f->type = om->type;
- f->data = data;
- list_add(&f->list, &map->fixup);
- }
- static void
- ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
- {
- union ucimap_data tdata = *data;
- char *eptr = NULL;
- char *s;
- int val;
- if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
- data = &data->list->item[data->list->n_items++];
- switch(om->type & UCIMAP_SUBTYPE) {
- case UCIMAP_STRING:
- if ((om->data.s.maxlen > 0) &&
- (strlen(str) > om->data.s.maxlen))
- return;
- s = strdup(str);
- tdata.s = s;
- ucimap_add_alloc(sd, s);
- break;
- case UCIMAP_BOOL:
- val = -1;
- if (strcmp(str, "on"))
- val = true;
- else if (strcmp(str, "1"))
- val = true;
- else if (strcmp(str, "enabled"))
- val = true;
- else if (strcmp(str, "off"))
- val = false;
- else if (strcmp(str, "0"))
- val = false;
- else if (strcmp(str, "disabled"))
- val = false;
- if (val == -1)
- return;
- tdata.b = val;
- break;
- case UCIMAP_INT:
- val = strtol(str, &eptr, om->data.i.base);
- if (!eptr || *eptr == '\0')
- tdata.i = val;
- else
- return;
- break;
- case UCIMAP_SECTION:
- ucimap_add_fixup(sd->map, data, om, str);
- return;
- case UCIMAP_CUSTOM:
- tdata.s = (char *) data;
- break;
- }
- if (om->parse) {
- if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
- return;
- }
- if (ucimap_is_custom(om->type))
- return;
- memcpy(data, &tdata, sizeof(union ucimap_data));
- }
- static int
- ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
- {
- struct uci_element *e, *l;
- struct uci_option *o;
- union ucimap_data *data;
- uci_foreach_element(&s->options, e) {
- struct uci_optmap *om = NULL, *tmp;
- ucimap_foreach_option(sm, tmp) {
- if (strcmp(e->name, tmp->name) == 0) {
- om = tmp;
- break;
- }
- }
- if (!om)
- continue;
- data = ucimap_get_data(sd, om);
- o = uci_to_option(e);
- if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
- ucimap_add_value(data, om, sd, o->v.string);
- } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
- uci_foreach_element(&o->v.list, l) {
- ucimap_add_value(data, om, sd, l->name);
- }
- }
- }
- return 0;
- }
- static int
- ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct uci_section *s)
- {
- struct ucimap_section_data *sd = NULL;
- struct uci_optmap *om;
- char *section_name;
- void *section;
- int n_alloc = 2;
- int err;
- sd = malloc(sm->alloc_len);
- if (!sd)
- return UCI_ERR_MEM;
- memset(sd, 0, sm->alloc_len);
- INIT_LIST_HEAD(&sd->list);
- sd->map = map;
- sd->sm = sm;
- ucimap_foreach_option(sm, om) {
- if (ucimap_is_list(om->type)) {
- union ucimap_data *data;
- struct uci_element *e;
- int n_elements = 0;
- int size;
- data = ucimap_get_data(sd, om);
- uci_foreach_element(&s->options, e) {
- struct uci_option *o = uci_to_option(e);
- struct uci_element *tmp;
- if (strcmp(e->name, om->name) != 0)
- continue;
- uci_foreach_element(&o->v.list, tmp) {
- n_elements++;
- }
- break;
- }
- n_alloc += n_elements + 1;
- size = sizeof(struct ucimap_list) +
- n_elements * sizeof(union ucimap_data);
- data->list = malloc(size);
- memset(data->list, 0, size);
- } else if (ucimap_is_alloc(om->type)) {
- n_alloc++;
- }
- }
- sd->allocmap = malloc(n_alloc * sizeof(struct uci_alloc));
- if (!sd->allocmap)
- goto error_mem;
- section_name = strdup(s->e.name);
- if (!section_name)
- goto error_mem;
- sd->section_name = section_name;
- sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
- if (!sd->cmap)
- goto error_mem;
- memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
- ucimap_add_alloc(sd, (void *)section_name);
- ucimap_add_alloc(sd, (void *)sd->cmap);
- ucimap_foreach_option(sm, om) {
- if (!ucimap_is_list(om->type))
- continue;
- ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
- }
- section = ucimap_section_ptr(sd);
- err = sm->init(map, section, s);
- if (err)
- goto error;
- list_add(&sd->list, &map->sdata);
- err = ucimap_parse_options(map, sm, sd, s);
- if (err)
- goto error;
- return 0;
- error_mem:
- if (sd->allocmap)
- free(sd->allocmap);
- free(sd);
- return UCI_ERR_MEM;
- error:
- ucimap_free_section(map, sd);
- return err;
- }
- static int
- ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
- {
- struct uci_package *p = s->package;
- memset(ptr, 0, sizeof(struct uci_ptr));
- ptr->package = p->e.name;
- ptr->p = p;
- ptr->section = s->e.name;
- ptr->s = s;
- ptr->option = option;
- return uci_lookup_ptr(p->ctx, ptr, NULL, false);
- }
- void
- ucimap_set_changed(struct ucimap_section_data *sd, void *field)
- {
- void *section = ucimap_section_ptr(sd);
- struct uci_sectionmap *sm = sd->sm;
- struct uci_optmap *om;
- int ofs = (char *)field - (char *)section;
- int i = 0;
- ucimap_foreach_option(sm, om) {
- if (om->offset == ofs) {
- SET_BIT(sd->cmap, i);
- break;
- }
- i++;
- }
- }
- int
- ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section)
- {
- char *sptr = (char *)section - sizeof(struct ucimap_section_data);
- struct ucimap_section_data *sd = (struct ucimap_section_data *) sptr;
- struct uci_sectionmap *sm = sd->sm;
- struct uci_section *s = NULL;
- struct uci_optmap *om;
- struct uci_element *e;
- struct uci_ptr ptr;
- int i = 0;
- int ret;
- uci_foreach_element(&p->sections, e) {
- if (!strcmp(e->name, sd->section_name)) {
- s = uci_to_section(e);
- break;
- }
- }
- if (!s)
- return UCI_ERR_NOTFOUND;
- ucimap_foreach_option(sm, om) {
- union ucimap_data *data;
- static char buf[32];
- const char *str = NULL;
- if (ucimap_is_list(om->type))
- continue;
- data = ucimap_get_data(sd, om);
- if (!TEST_BIT(sd->cmap, i))
- continue;
- ucimap_fill_ptr(&ptr, s, om->name);
- switch(om->type & UCIMAP_SUBTYPE) {
- case UCIMAP_STRING:
- str = data->s;
- break;
- case UCIMAP_INT:
- sprintf(buf, "%d", data->i);
- str = buf;
- break;
- case UCIMAP_BOOL:
- sprintf(buf, "%d", !!data->b);
- str = buf;
- break;
- default:
- continue;
- }
- ptr.value = str;
- ret = uci_set(s->package->ctx, &ptr);
- if (ret)
- return ret;
- CLR_BIT(sd->cmap, i);
- i++;
- }
- return 0;
- }
- void *
- ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
- {
- struct ucimap_section_data *sd;
- struct list_head *p;
- list_for_each(p, &map->sdata) {
- sd = list_entry(p, struct ucimap_section_data, list);
- if (sd->sm != f->sm)
- continue;
- if (strcmp(f->name, sd->section_name) != 0)
- continue;
- return ucimap_section_ptr(sd);
- }
- return NULL;
- }
- void
- ucimap_parse(struct uci_map *map, struct uci_package *pkg)
- {
- struct uci_element *e;
- struct list_head *p, *tmp;
- int i;
- INIT_LIST_HEAD(&map->fixup);
- uci_foreach_element(&pkg->sections, e) {
- struct uci_section *s = uci_to_section(e);
- for (i = 0; i < map->n_sections; i++) {
- if (strcmp(s->type, map->sections[i]->type) != 0)
- continue;
- ucimap_parse_section(map, map->sections[i], s);
- }
- }
- list_for_each_safe(p, tmp, &map->fixup) {
- struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
- void *ptr = ucimap_find_section(map, f);
- struct ucimap_list *list;
- if (!ptr)
- continue;
- switch(f->type & UCIMAP_TYPE) {
- case UCIMAP_SIMPLE:
- f->data->section = ptr;
- break;
- case UCIMAP_LIST:
- list = f->data->list;
- list->item[list->n_items++].section = ptr;
- break;
- }
- free(f);
- }
- list_for_each_safe(p, tmp, &map->sdata) {
- struct ucimap_section_data *sd = list_entry(p, struct ucimap_section_data, list);
- void *section;
- if (sd->done)
- continue;
- section = ucimap_section_ptr(sd);
- if (sd->sm->add(map, section) != 0)
- ucimap_free_section(map, sd);
- }
- }
|