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