e51cd2b8bc77e3436427c7f31aa6f6fcce821fb0
[project/uci.git] / ucimap.c
1 /*
2  * ucimap - library for mapping uci sections into data structures
3  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14 #include <strings.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include "ucimap.h"
19
20 struct uci_alloc {
21         enum ucimap_type type;
22         union {
23                 void **ptr;
24                 struct list_head *list;
25         } data;
26 };
27
28 struct uci_fixup {
29         struct list_head list;
30         struct uci_sectmap *sm;
31         const char *name;
32         struct uci_alloc target;
33 };
34
35 struct uci_sectmap_data {
36         struct list_head list;
37         struct uci_map *map;
38         struct uci_sectmap *sm;
39         const char *section_name;
40
41         /* list of allocations done by ucimap */
42         struct uci_alloc *allocmap;
43         unsigned long allocmap_len;
44
45         /* map for changed fields */
46         unsigned char *cmap;
47         bool done;
48 };
49
50
51 int
52 ucimap_init(struct uci_map *map)
53 {
54         INIT_LIST_HEAD(&map->sdata);
55         INIT_LIST_HEAD(&map->fixup);
56         return 0;
57 }
58
59 static void
60 ucimap_free_item(struct uci_alloc *a)
61 {
62         struct list_head *p, *tmp;
63         switch(a->type & UCIMAP_TYPE) {
64         case UCIMAP_SIMPLE:
65                 free(a->data.ptr);
66                 break;
67         case UCIMAP_LIST:
68                 list_for_each_safe(p, tmp, a->data.list) {
69                         struct uci_listmap *l = list_entry(p, struct uci_listmap, list);
70                         list_del(p);
71                         free(l);
72                 }
73                 break;
74         }
75 }
76
77 static inline void
78 ucimap_add_alloc(struct uci_alloc *a, void *ptr)
79 {
80         a->type = UCIMAP_SIMPLE;
81         a->data.ptr = ptr;
82 }
83
84 static void
85 ucimap_free_section(struct uci_map *map, struct uci_sectmap_data *sd)
86 {
87         void *section = sd;
88         int i;
89
90         section = (char *) section + sizeof(struct uci_sectmap_data);
91         if (!list_empty(&sd->list))
92                 list_del(&sd->list);
93
94         if (sd->sm->free_section)
95                 sd->sm->free_section(map, section);
96
97         for (i = 0; i < sd->allocmap_len; i++) {
98                 ucimap_free_item(&sd->allocmap[i]);
99         }
100
101         free(sd->allocmap);
102         free(sd);
103 }
104
105 void
106 ucimap_cleanup(struct uci_map *map)
107 {
108         struct list_head *ptr, *tmp;
109
110         list_for_each_safe(ptr, tmp, &map->sdata) {
111                 struct uci_sectmap_data *sd = list_entry(ptr, struct uci_sectmap_data, list);
112                 ucimap_free_section(map, sd);
113         }
114 }
115
116 static void
117 ucimap_add_fixup(struct uci_map *map, void *data, struct uci_optmap *om, const char *str)
118 {
119         struct uci_fixup *f;
120
121         f = malloc(sizeof(struct uci_fixup));
122         if (!f)
123                 return;
124
125         INIT_LIST_HEAD(&f->list);
126         f->sm = om->data.sm;
127         f->name = str;
128         f->target.type = om->type;
129         f->target.data.ptr = data;
130         list_add(&f->list, &map->fixup);
131 }
132
133 static void
134 ucimap_add_value(union uci_datamap *data, struct uci_optmap *om, struct uci_sectmap_data *sd, const char *str)
135 {
136         union uci_datamap tdata;
137         struct list_head *list = NULL;
138         char *eptr = NULL;
139         char *s;
140         int val;
141
142         if ((om->type & UCIMAP_TYPE) == UCIMAP_LIST) {
143                 if ((om->type & UCIMAP_SUBTYPE) == UCIMAP_SECTION) {
144                         ucimap_add_fixup(sd->map, data, om, str);
145                         return;
146                 }
147                 memset(&tdata, 0, sizeof(tdata));
148                 list = &data->list;
149                 data = &tdata;
150         }
151
152         switch(om->type & UCIMAP_SUBTYPE) {
153         case UCIMAP_STRING:
154                 if ((om->data.s.maxlen > 0) &&
155                         (strlen(str) > om->data.s.maxlen))
156                         return;
157
158                 s = strdup(str);
159                 data->s = s;
160                 ucimap_add_alloc(&sd->allocmap[sd->allocmap_len++], s);
161                 break;
162         case UCIMAP_BOOL:
163                 val = -1;
164                 if (strcmp(str, "on"))
165                         val = true;
166                 else if (strcmp(str, "1"))
167                         val = true;
168                 else if (strcmp(str, "enabled"))
169                         val = true;
170                 else if (strcmp(str, "off"))
171                         val = false;
172                 else if (strcmp(str, "0"))
173                         val = false;
174                 else if (strcmp(str, "disabled"))
175                         val = false;
176                 if (val == -1)
177                         return;
178
179                 data->b = val;
180                 break;
181         case UCIMAP_INT:
182                 val = strtol(str, &eptr, om->data.i.base);
183                 if (!eptr || *eptr == '\0')
184                         data->i = val;
185                 else
186                         return;
187                 break;
188         case UCIMAP_SECTION:
189                 ucimap_add_fixup(sd->map, data, om, str);
190                 break;
191         }
192
193         if ((om->type & UCIMAP_TYPE) == UCIMAP_LIST) {
194                 struct uci_listmap *item;
195
196                 item = malloc(sizeof(struct uci_listmap));
197                 if (!item)
198                         return;
199
200                 INIT_LIST_HEAD(&item->list);
201                 memcpy(&item->data, &tdata, sizeof(tdata));
202                 list_add(&item->list, list);
203         }
204 }
205
206
207 static int
208 ucimap_parse_options(struct uci_map *map, struct uci_sectmap *sm, struct uci_sectmap_data *sd, struct uci_section *s)
209 {
210         struct uci_element *e, *l;
211         struct uci_option *o;
212         unsigned long section;
213         union uci_datamap *data;
214         int i;
215
216         section = (unsigned long) sd + sizeof(struct uci_sectmap_data);
217         uci_foreach_element(&s->options, e) {
218                 struct uci_optmap *om = NULL;
219                 void *ptr = sm->options;
220                 int size = sm->options_size;
221
222                 if (!size)
223                         size = sizeof(struct uci_optmap);
224
225                 for (i = 0; i < sm->n_options; i++) {
226                         struct uci_optmap *tmp = ptr;
227
228                         if (strcmp(e->name, tmp->name) == 0) {
229                                 om = ptr;
230                                 break;
231                         }
232                         ptr = (unsigned char *)ptr + size;
233                 }
234                 if (!om)
235                         continue;
236
237                 data = (union uci_datamap *) (section + om->offset);
238                 o = uci_to_option(e);
239                 if ((o->type == UCI_TYPE_STRING) && ((om->type & UCIMAP_TYPE) == UCIMAP_SIMPLE)) {
240                         ucimap_add_value(data, om, sd, o->v.string);
241                         continue;
242                 }
243                 if ((o->type == UCI_TYPE_LIST) && ((om->type & UCIMAP_TYPE) == UCIMAP_LIST)) {
244                         struct list_head *list;
245
246                         list = (struct list_head *) (section + om->offset);
247                         INIT_LIST_HEAD(list);
248                         sd->allocmap[sd->allocmap_len].type = UCIMAP_LIST;
249                         sd->allocmap[sd->allocmap_len++].data.list = list;
250                         uci_foreach_element(&o->v.list, l) {
251                                 ucimap_add_value(data, om, sd, l->name);
252                         }
253                         continue;
254                 }
255         }
256
257         return 0;
258 }
259
260
261 static int
262 ucimap_parse_section(struct uci_map *map, struct uci_sectmap *sm, struct uci_section *s)
263 {
264         struct uci_sectmap_data *sd = NULL;
265         void *section = NULL;
266         char *section_name;
267         int err;
268
269         sd = malloc(sm->alloc_len + sizeof(struct uci_sectmap_data));
270         if (!sd)
271                 return UCI_ERR_MEM;
272
273         memset(sd, 0, sm->alloc_len + sizeof(struct uci_sectmap_data));
274         INIT_LIST_HEAD(&sd->list);
275
276         sd->map = map;
277         sd->sm = sm;
278         sd->allocmap = malloc(sm->n_options * sizeof(struct uci_alloc));
279         if (!sd->allocmap)
280                 goto error_mem;
281
282         section_name = strdup(s->e.name);
283         if (!section_name)
284                 goto error_mem;
285
286         sd->section_name = section_name;
287
288         sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
289         if (!sd->cmap)
290                 goto error_mem;
291
292         memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
293         ucimap_add_alloc(&sd->allocmap[sd->allocmap_len++], (void *)section_name);
294         ucimap_add_alloc(&sd->allocmap[sd->allocmap_len++], (void *)sd->cmap);
295
296         section = (char *)sd + sizeof(struct uci_sectmap_data);
297
298         err = sm->init_section(map, section, s);
299         if (err)
300                 goto error;
301
302         list_add(&sd->list, &map->sdata);
303         err = ucimap_parse_options(map, sm, sd, s);
304         if (err)
305                 goto error;
306
307         return 0;
308
309 error_mem:
310         if (sd->allocmap)
311                 free(sd->allocmap);
312         free(sd);
313         return UCI_ERR_MEM;
314
315 error:
316         ucimap_free_section(map, sd);
317         return err;
318 }
319
320 static int
321 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
322 {
323         struct uci_package *p = s->package;
324
325         memset(ptr, 0, sizeof(struct uci_ptr));
326
327         ptr->package = p->e.name;
328         ptr->p = p;
329
330         ptr->section = s->e.name;
331         ptr->s = s;
332
333         ptr->option = option;
334         return uci_lookup_ptr(p->ctx, ptr, NULL, false);
335 }
336
337 void
338 ucimap_set_changed(void *section, void *field)
339 {
340         char *sptr = (char *)section - sizeof(struct uci_sectmap_data);
341         struct uci_sectmap_data *sd = (struct uci_sectmap_data *) sptr;
342         struct uci_sectmap *sm = sd->sm;
343         int ofs = (char *)field - (char *)section;
344         int i;
345
346         for (i = 0; i < sm->n_options; i++) {
347                 if (sm->options[i].offset == ofs) {
348                         SET_BIT(sd->cmap, i);
349                         break;
350                 }
351         }
352 }
353
354 int
355 ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section)
356 {
357         char *sptr = (char *)section - sizeof(struct uci_sectmap_data);
358         struct uci_sectmap_data *sd = (struct uci_sectmap_data *) sptr;
359         struct uci_sectmap *sm = sd->sm;
360         struct uci_section *s = NULL;
361         struct uci_element *e;
362         struct uci_ptr ptr;
363         int i, ret;
364
365         uci_foreach_element(&p->sections, e) {
366                 if (!strcmp(e->name, sd->section_name)) {
367                         s = uci_to_section(e);
368                         break;
369                 }
370         }
371         if (!s)
372                 return UCI_ERR_NOTFOUND;
373
374         for (i = 0; i < sm->n_options; i++) {
375                 struct uci_optmap *om = &sm->options[i];
376                 static char buf[32];
377                 const char *str = NULL;
378                 void *p = (char *)section + om->offset;
379
380                 if (!TEST_BIT(sd->cmap, i))
381                         continue;
382
383                 ucimap_fill_ptr(&ptr, s, om->name);
384                 switch(om->type & UCIMAP_SUBTYPE) {
385                 case UCIMAP_STRING:
386                         str = *((char **) p);
387                         break;
388                 case UCIMAP_INT:
389                         sprintf(buf, "%d", *((int *) p));
390                         str = buf;
391                         break;
392                 case UCIMAP_BOOL:
393                         sprintf(buf, "%d", !!*((bool *)p));
394                         str = buf;
395                         break;
396                 }
397                 ptr.value = str;
398
399                 ret = uci_set(s->package->ctx, &ptr);
400                 if (ret)
401                         return ret;
402
403                 CLR_BIT(sd->cmap, i);
404         }
405
406         return 0;
407 }
408
409 void *
410 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
411 {
412         struct uci_sectmap_data *sd;
413         struct list_head *p;
414         void *ret;
415
416         list_for_each(p, &map->sdata) {
417                 sd = list_entry(p, struct uci_sectmap_data, list);
418                 if (sd->sm != f->sm)
419                         continue;
420                 if (strcmp(f->name, sd->section_name) != 0)
421                         continue;
422                 ret = (char *)sd + sizeof(struct uci_sectmap_data);
423                 return ret;
424         }
425         return NULL;
426 }
427
428 void
429 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
430 {
431         struct uci_element *e;
432         struct list_head *p, *tmp;
433         int i;
434
435         INIT_LIST_HEAD(&map->fixup);
436         uci_foreach_element(&pkg->sections, e) {
437                 struct uci_section *s = uci_to_section(e);
438
439                 for (i = 0; i < map->n_sections; i++) {
440                         if (strcmp(s->type, map->sections[i]->type) != 0)
441                                 continue;
442                         ucimap_parse_section(map, map->sections[i], s);
443                 }
444         }
445         list_for_each_safe(p, tmp, &map->fixup) {
446                 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
447                 void *ptr = ucimap_find_section(map, f);
448                 struct uci_listmap *li;
449
450                 if (!ptr)
451                         continue;
452
453                 switch(f->target.type & UCIMAP_TYPE) {
454                 case UCIMAP_SIMPLE:
455                         *f->target.data.ptr = ptr;
456                         break;
457                 case UCIMAP_LIST:
458                         li = malloc(sizeof(struct uci_listmap));
459                         memset(li, 0, sizeof(struct uci_listmap));
460                         INIT_LIST_HEAD(&li->list);
461                         li->data.section = ptr;
462                         list_add(&li->list, f->target.data.list);
463                         break;
464                 }
465         }
466         list_for_each_safe(p, tmp, &map->sdata) {
467                 struct uci_sectmap_data *sd = list_entry(p, struct uci_sectmap_data, list);
468                 void *section;
469
470                 if (sd->done)
471                         continue;
472
473                 section = (char *) sd + sizeof(struct uci_sectmap_data);
474                 if (sd->sm->add_section(map, section) != 0)
475                         ucimap_free_section(map, sd);
476         }
477 }