ucimap.c 11 KB

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