ucimap.c 11 KB

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