file.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /*
  2. * libuci - Library for the Unified Configuration Interface
  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 Lesser General Public License version 2.1
  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. /*
  15. * This file contains the code for parsing uci config files
  16. */
  17. #define _GNU_SOURCE
  18. #include <sys/types.h>
  19. #include <sys/file.h>
  20. #include <stdbool.h>
  21. #include <unistd.h>
  22. #include <fcntl.h>
  23. #include <stdio.h>
  24. #include <ctype.h>
  25. #include <glob.h>
  26. static struct uci_backend uci_file_backend;
  27. /*
  28. * verify that the end of the line or command is reached.
  29. * throw an error if extra arguments are given on the command line
  30. */
  31. static void assert_eol(struct uci_context *ctx, char **str)
  32. {
  33. char *tmp;
  34. skip_whitespace(ctx, str);
  35. tmp = next_arg(ctx, str, false, false);
  36. if (*tmp && (ctx->flags & UCI_FLAG_STRICT))
  37. uci_parse_error(ctx, *str, "too many arguments");
  38. }
  39. /*
  40. * switch to a different config, either triggered by uci_load, or by a
  41. * 'package <...>' statement in the import file
  42. */
  43. static void uci_switch_config(struct uci_context *ctx)
  44. {
  45. struct uci_parse_context *pctx;
  46. struct uci_element *e;
  47. const char *name;
  48. pctx = ctx->pctx;
  49. name = pctx->name;
  50. /* add the last config to main config file list */
  51. if (pctx->package) {
  52. pctx->package->backend = ctx->backend;
  53. uci_list_add(&ctx->root, &pctx->package->e.list);
  54. pctx->package = NULL;
  55. pctx->section = NULL;
  56. }
  57. if (!name)
  58. return;
  59. /*
  60. * if an older config under the same name exists, unload it
  61. * ignore errors here, e.g. if the config was not found
  62. */
  63. e = uci_lookup_list(&ctx->root, name);
  64. if (e)
  65. UCI_THROW(ctx, UCI_ERR_DUPLICATE);
  66. pctx->package = uci_alloc_package(ctx, name);
  67. }
  68. /*
  69. * parse the 'package' uci command (next config package)
  70. */
  71. static void uci_parse_package(struct uci_context *ctx, char **str, bool single)
  72. {
  73. char *name = NULL;
  74. /* command string null-terminated by strtok */
  75. *str += strlen(*str) + 1;
  76. name = next_arg(ctx, str, true, true);
  77. assert_eol(ctx, str);
  78. if (single)
  79. return;
  80. ctx->pctx->name = name;
  81. uci_switch_config(ctx);
  82. }
  83. /*
  84. * parse the 'config' uci command (open a section)
  85. */
  86. static void uci_parse_config(struct uci_context *ctx, char **str)
  87. {
  88. struct uci_parse_context *pctx = ctx->pctx;
  89. struct uci_element *e;
  90. struct uci_ptr ptr;
  91. char *name = NULL;
  92. char *type = NULL;
  93. uci_fixup_section(ctx, ctx->pctx->section);
  94. if (!ctx->pctx->package) {
  95. if (!ctx->pctx->name)
  96. uci_parse_error(ctx, *str, "attempting to import a file without a package name");
  97. uci_switch_config(ctx);
  98. }
  99. /* command string null-terminated by strtok */
  100. *str += strlen(*str) + 1;
  101. type = next_arg(ctx, str, true, false);
  102. if (!uci_validate_type(type))
  103. uci_parse_error(ctx, type, "invalid character in field");
  104. name = next_arg(ctx, str, false, true);
  105. assert_eol(ctx, str);
  106. if (!name) {
  107. ctx->internal = !pctx->merge;
  108. UCI_NESTED(uci_add_section, ctx, pctx->package, type, &pctx->section);
  109. } else {
  110. UCI_NESTED(uci_fill_ptr, ctx, &ptr, &pctx->package->e, false);
  111. e = uci_lookup_list(&pctx->package->sections, name);
  112. if (e)
  113. ptr.s = uci_to_section(e);
  114. ptr.section = name;
  115. ptr.value = type;
  116. ctx->internal = !pctx->merge;
  117. UCI_NESTED(uci_set, ctx, &ptr);
  118. pctx->section = uci_to_section(ptr.last);
  119. }
  120. }
  121. /*
  122. * parse the 'option' uci command (open a value)
  123. */
  124. static void uci_parse_option(struct uci_context *ctx, char **str, bool list)
  125. {
  126. struct uci_parse_context *pctx = ctx->pctx;
  127. struct uci_element *e;
  128. struct uci_ptr ptr;
  129. char *name = NULL;
  130. char *value = NULL;
  131. if (!pctx->section)
  132. uci_parse_error(ctx, *str, "option/list command found before the first section");
  133. /* command string null-terminated by strtok */
  134. *str += strlen(*str) + 1;
  135. name = next_arg(ctx, str, true, true);
  136. value = next_arg(ctx, str, false, false);
  137. assert_eol(ctx, str);
  138. UCI_NESTED(uci_fill_ptr, ctx, &ptr, &pctx->section->e, false);
  139. e = uci_lookup_list(&pctx->section->options, name);
  140. if (e)
  141. ptr.o = uci_to_option(e);
  142. ptr.option = name;
  143. ptr.value = value;
  144. ctx->internal = !pctx->merge;
  145. if (list)
  146. UCI_NESTED(uci_add_list, ctx, &ptr);
  147. else
  148. UCI_NESTED(uci_set, ctx, &ptr);
  149. }
  150. /*
  151. * parse a complete input line, split up combined commands by ';'
  152. */
  153. static void uci_parse_line(struct uci_context *ctx, bool single)
  154. {
  155. struct uci_parse_context *pctx = ctx->pctx;
  156. char *word, *brk;
  157. word = pctx->buf;
  158. do {
  159. brk = NULL;
  160. word = strtok_r(word, " \t", &brk);
  161. if (!word)
  162. return;
  163. switch(word[0]) {
  164. case 0:
  165. case '#':
  166. return;
  167. case 'p':
  168. if ((word[1] == 0) || !strcmp(word + 1, "ackage"))
  169. uci_parse_package(ctx, &word, single);
  170. else
  171. goto invalid;
  172. break;
  173. case 'c':
  174. if ((word[1] == 0) || !strcmp(word + 1, "onfig"))
  175. uci_parse_config(ctx, &word);
  176. else
  177. goto invalid;
  178. break;
  179. case 'o':
  180. if ((word[1] == 0) || !strcmp(word + 1, "ption"))
  181. uci_parse_option(ctx, &word, false);
  182. else
  183. goto invalid;
  184. break;
  185. case 'l':
  186. if ((word[1] == 0) || !strcmp(word + 1, "ist"))
  187. uci_parse_option(ctx, &word, true);
  188. else
  189. goto invalid;
  190. break;
  191. default:
  192. goto invalid;
  193. }
  194. continue;
  195. invalid:
  196. uci_parse_error(ctx, word, "invalid command");
  197. } while (1);
  198. }
  199. /* max number of characters that escaping adds to the string */
  200. #define UCI_QUOTE_ESCAPE "'\\''"
  201. /*
  202. * escape an uci string for export
  203. */
  204. static char *uci_escape(struct uci_context *ctx, const char *str)
  205. {
  206. const char *end;
  207. int ofs = 0;
  208. if (!ctx->buf) {
  209. ctx->bufsz = LINEBUF;
  210. ctx->buf = malloc(LINEBUF);
  211. }
  212. while (1) {
  213. int len;
  214. end = strchr(str, '\'');
  215. if (!end)
  216. end = str + strlen(str);
  217. len = end - str;
  218. /* make sure that we have enough room in the buffer */
  219. while (ofs + len + sizeof(UCI_QUOTE_ESCAPE) + 1 > ctx->bufsz) {
  220. ctx->bufsz *= 2;
  221. ctx->buf = uci_realloc(ctx, ctx->buf, ctx->bufsz);
  222. }
  223. /* copy the string until the character before the quote */
  224. memcpy(&ctx->buf[ofs], str, len);
  225. ofs += len;
  226. /* end of string? return the buffer */
  227. if (*end == 0)
  228. break;
  229. memcpy(&ctx->buf[ofs], UCI_QUOTE_ESCAPE, sizeof(UCI_QUOTE_ESCAPE));
  230. ofs += strlen(&ctx->buf[ofs]);
  231. str = end + 1;
  232. }
  233. ctx->buf[ofs] = 0;
  234. return ctx->buf;
  235. }
  236. /*
  237. * export a single config package to a file stream
  238. */
  239. static void uci_export_package(struct uci_package *p, FILE *stream, bool header)
  240. {
  241. struct uci_context *ctx = p->ctx;
  242. struct uci_element *s, *o, *i;
  243. if (header)
  244. fprintf(stream, "package '%s'\n", uci_escape(ctx, p->e.name));
  245. uci_foreach_element(&p->sections, s) {
  246. struct uci_section *sec = uci_to_section(s);
  247. fprintf(stream, "\nconfig '%s'", uci_escape(ctx, sec->type));
  248. if (!sec->anonymous || (ctx->flags & UCI_FLAG_EXPORT_NAME))
  249. fprintf(stream, " '%s'", uci_escape(ctx, sec->e.name));
  250. fprintf(stream, "\n");
  251. uci_foreach_element(&sec->options, o) {
  252. struct uci_option *opt = uci_to_option(o);
  253. switch(opt->type) {
  254. case UCI_TYPE_STRING:
  255. fprintf(stream, "\toption '%s'", uci_escape(ctx, opt->e.name));
  256. fprintf(stream, " '%s'\n", uci_escape(ctx, opt->v.string));
  257. break;
  258. case UCI_TYPE_LIST:
  259. uci_foreach_element(&opt->v.list, i) {
  260. fprintf(stream, "\tlist '%s'", uci_escape(ctx, opt->e.name));
  261. fprintf(stream, " '%s'\n", uci_escape(ctx, i->name));
  262. }
  263. break;
  264. default:
  265. fprintf(stream, "\t# unknown type for option '%s'\n", uci_escape(ctx, opt->e.name));
  266. break;
  267. }
  268. }
  269. }
  270. fprintf(stream, "\n");
  271. }
  272. int uci_export(struct uci_context *ctx, FILE *stream, struct uci_package *package, bool header)
  273. {
  274. struct uci_element *e;
  275. UCI_HANDLE_ERR(ctx);
  276. UCI_ASSERT(ctx, stream != NULL);
  277. if (package)
  278. uci_export_package(package, stream, header);
  279. else {
  280. uci_foreach_element(&ctx->root, e) {
  281. uci_export_package(uci_to_package(e), stream, header);
  282. }
  283. }
  284. return 0;
  285. }
  286. int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct uci_package **package, bool single)
  287. {
  288. struct uci_parse_context *pctx;
  289. UCI_HANDLE_ERR(ctx);
  290. /* make sure no memory from previous parse attempts is leaked */
  291. uci_cleanup(ctx);
  292. uci_alloc_parse_context(ctx);
  293. pctx = ctx->pctx;
  294. pctx->file = stream;
  295. if (*package && single) {
  296. pctx->package = *package;
  297. pctx->merge = true;
  298. }
  299. /*
  300. * If 'name' was supplied, assume that the supplied stream does not contain
  301. * the appropriate 'package <name>' string to specify the config name
  302. * NB: the config file can still override the package name
  303. */
  304. if (name) {
  305. UCI_ASSERT(ctx, uci_validate_package(name));
  306. pctx->name = name;
  307. }
  308. while (!feof(pctx->file)) {
  309. uci_getln(ctx, 0);
  310. UCI_TRAP_SAVE(ctx, error);
  311. if (pctx->buf[0])
  312. uci_parse_line(ctx, single);
  313. UCI_TRAP_RESTORE(ctx);
  314. continue;
  315. error:
  316. if (ctx->flags & UCI_FLAG_PERROR)
  317. uci_perror(ctx, NULL);
  318. if ((ctx->err != UCI_ERR_PARSE) ||
  319. (ctx->flags & UCI_FLAG_STRICT))
  320. UCI_THROW(ctx, ctx->err);
  321. }
  322. uci_fixup_section(ctx, ctx->pctx->section);
  323. if (!pctx->package && name)
  324. uci_switch_config(ctx);
  325. if (package)
  326. *package = pctx->package;
  327. if (pctx->merge)
  328. pctx->package = NULL;
  329. pctx->name = NULL;
  330. uci_switch_config(ctx);
  331. /* no error happened, we can get rid of the parser context now */
  332. uci_cleanup(ctx);
  333. return 0;
  334. }
  335. static char *uci_config_path(struct uci_context *ctx, const char *name)
  336. {
  337. char *filename;
  338. UCI_ASSERT(ctx, uci_validate_package(name));
  339. filename = uci_malloc(ctx, strlen(name) + strlen(ctx->confdir) + 2);
  340. sprintf(filename, "%s/%s", ctx->confdir, name);
  341. return filename;
  342. }
  343. void uci_file_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite)
  344. {
  345. struct uci_package *p = *package;
  346. FILE *f = NULL;
  347. char *name = NULL;
  348. char *path = NULL;
  349. if (!p->path) {
  350. if (overwrite)
  351. p->path = uci_config_path(ctx, p->e.name);
  352. else
  353. UCI_THROW(ctx, UCI_ERR_INVAL);
  354. }
  355. /* open the config file for writing now, so that it is locked */
  356. f = uci_open_stream(ctx, p->path, SEEK_SET, true, true);
  357. /* flush unsaved changes and reload from history file */
  358. UCI_TRAP_SAVE(ctx, done);
  359. if (p->has_history) {
  360. if (!overwrite) {
  361. name = uci_strdup(ctx, p->e.name);
  362. path = uci_strdup(ctx, p->path);
  363. /* dump our own changes to the history file */
  364. if (!uci_list_empty(&p->history))
  365. UCI_INTERNAL(uci_save, ctx, p);
  366. /*
  367. * other processes might have modified the config
  368. * as well. dump and reload
  369. */
  370. uci_free_package(&p);
  371. uci_cleanup(ctx);
  372. UCI_INTERNAL(uci_import, ctx, f, name, &p, true);
  373. p->path = path;
  374. p->has_history = true;
  375. *package = p;
  376. /* freed together with the uci_package */
  377. path = NULL;
  378. /* check for updated history, flush */
  379. if (!uci_load_history(ctx, p, true))
  380. goto done;
  381. } else {
  382. /* flush history */
  383. if (!uci_load_history(ctx, NULL, true))
  384. goto done;
  385. }
  386. }
  387. rewind(f);
  388. ftruncate(fileno(f), 0);
  389. uci_export(ctx, f, p, false);
  390. UCI_TRAP_RESTORE(ctx);
  391. done:
  392. if (name)
  393. free(name);
  394. if (path)
  395. free(path);
  396. uci_close_stream(f);
  397. if (ctx->err)
  398. UCI_THROW(ctx, ctx->err);
  399. }
  400. /*
  401. * This function returns the filename by returning the string
  402. * after the last '/' character. By checking for a non-'\0'
  403. * character afterwards, directories are ignored (glob marks
  404. * those with a trailing '/'
  405. */
  406. static inline char *get_filename(char *path)
  407. {
  408. char *p;
  409. p = strrchr(path, '/');
  410. p++;
  411. if (!*p)
  412. return NULL;
  413. return p;
  414. }
  415. static char **uci_list_config_files(struct uci_context *ctx)
  416. {
  417. char **configs;
  418. glob_t globbuf;
  419. int size, i;
  420. char *buf;
  421. char *dir;
  422. dir = uci_malloc(ctx, strlen(ctx->confdir) + 1 + sizeof("/*"));
  423. sprintf(dir, "%s/*", ctx->confdir);
  424. if (glob(dir, GLOB_MARK, NULL, &globbuf) != 0)
  425. UCI_THROW(ctx, UCI_ERR_NOTFOUND);
  426. size = sizeof(char *) * (globbuf.gl_pathc + 1);
  427. for(i = 0; i < globbuf.gl_pathc; i++) {
  428. char *p;
  429. p = get_filename(globbuf.gl_pathv[i]);
  430. if (!p)
  431. continue;
  432. size += strlen(p) + 1;
  433. }
  434. configs = uci_malloc(ctx, size);
  435. buf = (char *) &configs[globbuf.gl_pathc + 1];
  436. for(i = 0; i < globbuf.gl_pathc; i++) {
  437. char *p;
  438. p = get_filename(globbuf.gl_pathv[i]);
  439. if (!p)
  440. continue;
  441. if (!uci_validate_package(p))
  442. continue;
  443. configs[i] = buf;
  444. strcpy(buf, p);
  445. buf += strlen(buf) + 1;
  446. }
  447. free(dir);
  448. return configs;
  449. }
  450. static struct uci_package *uci_file_load(struct uci_context *ctx, const char *name)
  451. {
  452. struct uci_package *package = NULL;
  453. char *filename;
  454. bool confdir;
  455. FILE *file = NULL;
  456. switch (name[0]) {
  457. case '.':
  458. /* relative path outside of /etc/config */
  459. if (name[1] != '/')
  460. UCI_THROW(ctx, UCI_ERR_NOTFOUND);
  461. /* fall through */
  462. case '/':
  463. /* absolute path outside of /etc/config */
  464. filename = uci_strdup(ctx, name);
  465. name = strrchr(name, '/') + 1;
  466. confdir = false;
  467. break;
  468. default:
  469. /* config in /etc/config */
  470. filename = uci_config_path(ctx, name);
  471. confdir = true;
  472. break;
  473. }
  474. file = uci_open_stream(ctx, filename, SEEK_SET, false, false);
  475. ctx->err = 0;
  476. UCI_TRAP_SAVE(ctx, done);
  477. UCI_INTERNAL(uci_import, ctx, file, name, &package, true);
  478. UCI_TRAP_RESTORE(ctx);
  479. if (package) {
  480. package->path = filename;
  481. package->has_history = confdir;
  482. uci_load_history(ctx, package, false);
  483. }
  484. done:
  485. uci_close_stream(file);
  486. if (ctx->err)
  487. UCI_THROW(ctx, ctx->err);
  488. return package;
  489. }
  490. static UCI_BACKEND(uci_file_backend, "file",
  491. .load = uci_file_load,
  492. .commit = uci_file_commit,
  493. .list_configs = uci_list_config_files,
  494. );