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 }