ucimap.c 11 KB

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