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