ucimap.c 13 KB

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