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