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