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