191c97c4b9153df2ab589c8fcaf68bcbecbb3021
[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_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)
143 sd->sm->free(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(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 = 0;
392
393 ucimap_foreach_option(sm, om) {
394 if (om->offset == ofs) {
395 SET_BIT(sd->cmap, i);
396 break;
397 }
398 i++;
399 }
400 }
401
402 int
403 ucimap_store_section(struct uci_map *map, struct uci_package *p, void *section)
404 {
405 char *sptr = (char *)section - sizeof(struct uci_sectmap_data);
406 struct uci_sectmap_data *sd = (struct uci_sectmap_data *) sptr;
407 struct uci_sectmap *sm = sd->sm;
408 struct uci_section *s = NULL;
409 struct uci_optmap *om;
410 struct uci_element *e;
411 struct uci_ptr ptr;
412 int i = 0;
413 int ret;
414
415 uci_foreach_element(&p->sections, e) {
416 if (!strcmp(e->name, sd->section_name)) {
417 s = uci_to_section(e);
418 break;
419 }
420 }
421 if (!s)
422 return UCI_ERR_NOTFOUND;
423
424 ucimap_foreach_option(sm, om) {
425 union ucimap_data *data;
426 static char buf[32];
427 const char *str = NULL;
428
429 if (ucimap_is_list(om->type))
430 continue;
431
432 data = ucimap_get_data(sd, om);
433 if (!TEST_BIT(sd->cmap, i))
434 continue;
435
436 ucimap_fill_ptr(&ptr, s, om->name);
437 switch(om->type & UCIMAP_SUBTYPE) {
438 case UCIMAP_STRING:
439 str = data->s;
440 break;
441 case UCIMAP_INT:
442 sprintf(buf, "%d", data->i);
443 str = buf;
444 break;
445 case UCIMAP_BOOL:
446 sprintf(buf, "%d", !!data->b);
447 str = buf;
448 break;
449 default:
450 continue;
451 }
452 ptr.value = str;
453
454 ret = uci_set(s->package->ctx, &ptr);
455 if (ret)
456 return ret;
457
458 CLR_BIT(sd->cmap, i);
459 i++;
460 }
461
462 return 0;
463 }
464
465 void *
466 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
467 {
468 struct uci_sectmap_data *sd;
469 struct list_head *p;
470 void *ret;
471
472 list_for_each(p, &map->sdata) {
473 sd = list_entry(p, struct uci_sectmap_data, list);
474 if (sd->sm != f->sm)
475 continue;
476 if (strcmp(f->name, sd->section_name) != 0)
477 continue;
478 ret = (char *)sd + sizeof(struct uci_sectmap_data);
479 return ret;
480 }
481 return NULL;
482 }
483
484 void
485 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
486 {
487 struct uci_element *e;
488 struct list_head *p, *tmp;
489 int i;
490
491 INIT_LIST_HEAD(&map->fixup);
492 uci_foreach_element(&pkg->sections, e) {
493 struct uci_section *s = uci_to_section(e);
494
495 for (i = 0; i < map->n_sections; i++) {
496 if (strcmp(s->type, map->sections[i]->type) != 0)
497 continue;
498 ucimap_parse_section(map, map->sections[i], s);
499 }
500 }
501 list_for_each_safe(p, tmp, &map->fixup) {
502 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
503 void *ptr = ucimap_find_section(map, f);
504 struct ucimap_list *list;
505
506 if (!ptr)
507 continue;
508
509 switch(f->type & UCIMAP_TYPE) {
510 case UCIMAP_SIMPLE:
511 f->data->section = ptr;
512 break;
513 case UCIMAP_LIST:
514 list = f->data->list;
515 list->item[list->n_items++].section = ptr;
516 break;
517 }
518 free(f);
519 }
520 list_for_each_safe(p, tmp, &map->sdata) {
521 struct uci_sectmap_data *sd = list_entry(p, struct uci_sectmap_data, list);
522 void *section;
523
524 if (sd->done)
525 continue;
526
527 section = (char *) sd + sizeof(struct uci_sectmap_data);
528 if (sd->sm->add(map, section) != 0)
529 ucimap_free_section(map, sd);
530 }
531 }