file.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  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 Lesser 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/stat.h>
  20. #include <sys/file.h>
  21. #include <stdbool.h>
  22. #include <unistd.h>
  23. #include <fcntl.h>
  24. #include <stdio.h>
  25. #include <ctype.h>
  26. #include <glob.h>
  27. #include <string.h>
  28. #include <stdlib.h>
  29. #include <errno.h>
  30. #include "uci.h"
  31. #include "uci_internal.h"
  32. #define LINEBUF 32
  33. /*
  34. * Fetch a new line from the input stream and resize buffer if necessary
  35. */
  36. __private void uci_getln(struct uci_context *ctx, size_t offset)
  37. {
  38. struct uci_parse_context *pctx = ctx->pctx;
  39. char *p;
  40. size_t ofs;
  41. if (pctx->buf == NULL) {
  42. pctx->buf = uci_malloc(ctx, LINEBUF);
  43. pctx->bufsz = LINEBUF;
  44. }
  45. /* It takes 2 slots for fgets to read 1 char. */
  46. if (offset >= pctx->bufsz - 1) {
  47. pctx->bufsz *= 2;
  48. pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz);
  49. }
  50. ofs = offset;
  51. do {
  52. p = &pctx->buf[ofs];
  53. p[0] = 0;
  54. p = fgets(p, pctx->bufsz - ofs, pctx->file);
  55. if (!p || !*p)
  56. return;
  57. ofs += strlen(p);
  58. pctx->buf_filled = ofs;
  59. if (pctx->buf[ofs - 1] == '\n') {
  60. pctx->line++;
  61. return;
  62. }
  63. pctx->bufsz *= 2;
  64. pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz);
  65. } while (1);
  66. }
  67. /*
  68. * parse a character escaped by '\'
  69. * returns true if the escaped character is to be parsed
  70. * returns false if the escaped character is to be ignored
  71. */
  72. static bool parse_backslash(struct uci_context *ctx)
  73. {
  74. struct uci_parse_context *pctx = ctx->pctx;
  75. /* skip backslash */
  76. pctx->pos += 1;
  77. /* undecoded backslash at the end of line, fetch the next line */
  78. if (!pctx_cur_char(pctx) ||
  79. pctx_cur_char(pctx) == '\n' ||
  80. (pctx_cur_char(pctx) == '\r' &&
  81. pctx_char(pctx, pctx_pos(pctx) + 1) == '\n' &&
  82. !pctx_char(pctx, pctx_pos(pctx) + 2))) {
  83. uci_getln(ctx, pctx->pos);
  84. return false;
  85. }
  86. /* FIXME: decode escaped char, necessary? */
  87. return true;
  88. }
  89. /*
  90. * move the string pointer forward until a non-whitespace character or
  91. * EOL is reached
  92. */
  93. static void skip_whitespace(struct uci_context *ctx)
  94. {
  95. struct uci_parse_context *pctx = ctx->pctx;
  96. while (pctx_cur_char(pctx) && isspace(pctx_cur_char(pctx)))
  97. pctx->pos += 1;
  98. }
  99. static inline void addc(struct uci_context *ctx, size_t *pos_dest, size_t *pos_src)
  100. {
  101. struct uci_parse_context *pctx = ctx->pctx;
  102. pctx_char(pctx, *pos_dest) = pctx_char(pctx, *pos_src);
  103. *pos_dest += 1;
  104. *pos_src += 1;
  105. }
  106. static int uci_increase_pos(struct uci_parse_context *pctx, size_t add)
  107. {
  108. if (pctx->pos + add > pctx->buf_filled)
  109. return -EINVAL;
  110. pctx->pos += add;
  111. return 0;
  112. }
  113. /*
  114. * parse a double quoted string argument from the command line
  115. */
  116. static void parse_double_quote(struct uci_context *ctx, size_t *target)
  117. {
  118. struct uci_parse_context *pctx = ctx->pctx;
  119. char c;
  120. /* skip quote character */
  121. pctx->pos += 1;
  122. while (1) {
  123. c = pctx_cur_char(pctx);
  124. switch(c) {
  125. case '"':
  126. pctx->pos += 1;
  127. return;
  128. case 0:
  129. /* Multi-line str value */
  130. uci_getln(ctx, pctx->pos);
  131. if (!pctx_cur_char(pctx)) {
  132. uci_parse_error(ctx, "EOF with unterminated \"");
  133. }
  134. break;
  135. case '\\':
  136. if (!parse_backslash(ctx))
  137. continue;
  138. /* fall through */
  139. default:
  140. addc(ctx, target, &pctx->pos);
  141. break;
  142. }
  143. }
  144. }
  145. /*
  146. * parse a single quoted string argument from the command line
  147. */
  148. static void parse_single_quote(struct uci_context *ctx, size_t *target)
  149. {
  150. struct uci_parse_context *pctx = ctx->pctx;
  151. char c;
  152. /* skip quote character */
  153. pctx->pos += 1;
  154. while (1) {
  155. c = pctx_cur_char(pctx);
  156. switch(c) {
  157. case '\'':
  158. pctx->pos += 1;
  159. return;
  160. case 0:
  161. /* Multi-line str value */
  162. uci_getln(ctx, pctx->pos);
  163. if (!pctx_cur_char(pctx))
  164. uci_parse_error(ctx, "EOF with unterminated '");
  165. break;
  166. default:
  167. addc(ctx, target, &pctx->pos);
  168. }
  169. }
  170. }
  171. /*
  172. * parse a string from the command line and detect the quoting style
  173. */
  174. static void parse_str(struct uci_context *ctx, size_t *target)
  175. {
  176. struct uci_parse_context *pctx = ctx->pctx;
  177. bool next = true;
  178. do {
  179. switch(pctx_cur_char(pctx)) {
  180. case '\'':
  181. parse_single_quote(ctx, target);
  182. break;
  183. case '"':
  184. parse_double_quote(ctx, target);
  185. break;
  186. case '#':
  187. pctx_cur_char(pctx) = 0;
  188. /* fall through */
  189. case 0:
  190. goto done;
  191. case ';':
  192. next = false;
  193. goto done;
  194. case '\\':
  195. if (!parse_backslash(ctx))
  196. continue;
  197. /* fall through */
  198. default:
  199. addc(ctx, target, &pctx->pos);
  200. break;
  201. }
  202. } while (pctx_cur_char(pctx) && !isspace(pctx_cur_char(pctx)));
  203. done:
  204. /*
  205. * if the string was unquoted and we've stopped at a whitespace
  206. * character, skip to the next one, because the whitespace will
  207. * be overwritten by a null byte here
  208. */
  209. if (pctx_cur_char(pctx) && next)
  210. pctx->pos += 1;
  211. /* terminate the parsed string */
  212. pctx_char(pctx, *target) = 0;
  213. }
  214. /*
  215. * extract the next argument from the command line
  216. */
  217. static int next_arg(struct uci_context *ctx, bool required, bool name, bool package)
  218. {
  219. struct uci_parse_context *pctx = ctx->pctx;
  220. size_t val, ptr;
  221. skip_whitespace(ctx);
  222. val = ptr = pctx_pos(pctx);
  223. if (pctx_cur_char(pctx) == ';') {
  224. pctx_cur_char(pctx) = 0;
  225. pctx->pos += 1;
  226. } else {
  227. parse_str(ctx, &ptr);
  228. }
  229. if (!pctx_char(pctx, val)) {
  230. if (required)
  231. uci_parse_error(ctx, "insufficient arguments");
  232. goto done;
  233. }
  234. if (name && !uci_validate_str(pctx_str(pctx, val), name, package))
  235. uci_parse_error(ctx, "invalid character in name field");
  236. done:
  237. return val;
  238. }
  239. int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, char **result)
  240. {
  241. int ofs_result;
  242. UCI_HANDLE_ERR(ctx);
  243. UCI_ASSERT(ctx, str != NULL);
  244. UCI_ASSERT(ctx, result != NULL);
  245. if (ctx->pctx && (ctx->pctx->file != stream))
  246. uci_cleanup(ctx);
  247. if (!ctx->pctx)
  248. uci_alloc_parse_context(ctx);
  249. ctx->pctx->file = stream;
  250. if (!*str) {
  251. ctx->pctx->pos = 0;
  252. uci_getln(ctx, 0);
  253. }
  254. ofs_result = next_arg(ctx, false, false, false);
  255. *result = pctx_str(ctx->pctx, ofs_result);
  256. *str = pctx_cur_str(ctx->pctx);
  257. return 0;
  258. }
  259. static int
  260. uci_fill_ptr(struct uci_context *ctx, struct uci_ptr *ptr, struct uci_element *e)
  261. {
  262. UCI_ASSERT(ctx, ptr != NULL);
  263. UCI_ASSERT(ctx, e != NULL);
  264. memset(ptr, 0, sizeof(struct uci_ptr));
  265. switch(e->type) {
  266. case UCI_TYPE_OPTION:
  267. ptr->o = uci_to_option(e);
  268. goto fill_option;
  269. case UCI_TYPE_SECTION:
  270. ptr->s = uci_to_section(e);
  271. goto fill_section;
  272. case UCI_TYPE_PACKAGE:
  273. ptr->p = uci_to_package(e);
  274. goto fill_package;
  275. default:
  276. UCI_THROW(ctx, UCI_ERR_INVAL);
  277. }
  278. fill_option:
  279. ptr->option = ptr->o->e.name;
  280. ptr->s = ptr->o->section;
  281. fill_section:
  282. ptr->section = ptr->s->e.name;
  283. ptr->p = ptr->s->package;
  284. fill_package:
  285. ptr->package = ptr->p->e.name;
  286. ptr->flags |= UCI_LOOKUP_DONE;
  287. return 0;
  288. }
  289. /*
  290. * verify that the end of the line or command is reached.
  291. * throw an error if extra arguments are given on the command line
  292. */
  293. static void assert_eol(struct uci_context *ctx)
  294. {
  295. char *tmp;
  296. int ofs_tmp;
  297. skip_whitespace(ctx);
  298. ofs_tmp = next_arg(ctx, false, false, false);
  299. tmp = pctx_str(ctx->pctx, ofs_tmp);
  300. if (*tmp && (ctx->flags & UCI_FLAG_STRICT))
  301. uci_parse_error(ctx, "too many arguments");
  302. }
  303. /*
  304. * switch to a different config, either triggered by uci_load, or by a
  305. * 'package <...>' statement in the import file
  306. */
  307. static void uci_switch_config(struct uci_context *ctx)
  308. {
  309. struct uci_parse_context *pctx;
  310. struct uci_element *e;
  311. const char *name;
  312. pctx = ctx->pctx;
  313. name = pctx->name;
  314. /* add the last config to main config file list */
  315. if (pctx->package) {
  316. pctx->package->backend = ctx->backend;
  317. uci_list_add(&ctx->root, &pctx->package->e.list);
  318. pctx->package = NULL;
  319. pctx->section = NULL;
  320. }
  321. if (!name)
  322. return;
  323. /*
  324. * if an older config under the same name exists, unload it
  325. * ignore errors here, e.g. if the config was not found
  326. */
  327. e = uci_lookup_list(&ctx->root, name);
  328. if (e)
  329. UCI_THROW(ctx, UCI_ERR_DUPLICATE);
  330. pctx->package = uci_alloc_package(ctx, name);
  331. }
  332. /*
  333. * parse the 'package' uci command (next config package)
  334. */
  335. static void uci_parse_package(struct uci_context *ctx, bool single)
  336. {
  337. struct uci_parse_context *pctx = ctx->pctx;
  338. int ofs_name;
  339. char *name;
  340. /* command string null-terminated by strtok */
  341. if (uci_increase_pos(pctx, strlen(pctx_cur_str(pctx)) + 1))
  342. uci_parse_error(ctx, "package without name");
  343. ofs_name = next_arg(ctx, true, true, true);
  344. assert_eol(ctx);
  345. name = pctx_str(pctx, ofs_name);
  346. if (single)
  347. return;
  348. ctx->pctx->name = name;
  349. uci_switch_config(ctx);
  350. }
  351. /*
  352. * parse the 'config' uci command (open a section)
  353. */
  354. static void uci_parse_config(struct uci_context *ctx)
  355. {
  356. struct uci_parse_context *pctx = ctx->pctx;
  357. struct uci_element *e;
  358. struct uci_ptr ptr;
  359. int ofs_name, ofs_type;
  360. char *name;
  361. char *type;
  362. if (!ctx->pctx->package) {
  363. if (!ctx->pctx->name)
  364. uci_parse_error(ctx, "attempting to import a file without a package name");
  365. uci_switch_config(ctx);
  366. }
  367. /* command string null-terminated by strtok */
  368. if (uci_increase_pos(pctx, strlen(pctx_cur_str(pctx)) + 1))
  369. uci_parse_error(ctx, "config without name");
  370. ofs_type = next_arg(ctx, true, false, false);
  371. type = pctx_str(pctx, ofs_type);
  372. if (!uci_validate_type(type))
  373. uci_parse_error(ctx, "invalid character in type field");
  374. ofs_name = next_arg(ctx, false, true, false);
  375. assert_eol(ctx);
  376. type = pctx_str(pctx, ofs_type);
  377. name = pctx_str(pctx, ofs_name);
  378. if (!name || !name[0]) {
  379. ctx->internal = !pctx->merge;
  380. UCI_NESTED(uci_add_section, ctx, pctx->package, type, &pctx->section);
  381. } else {
  382. uci_fill_ptr(ctx, &ptr, &pctx->package->e);
  383. e = uci_lookup_list(&pctx->package->sections, name);
  384. if (e) {
  385. ptr.s = uci_to_section(e);
  386. if ((ctx->flags & UCI_FLAG_STRICT) && strcmp(ptr.s->type, type))
  387. uci_parse_error(ctx, "section of different type overwrites prior section with same name");
  388. }
  389. ptr.section = name;
  390. ptr.value = type;
  391. ctx->internal = !pctx->merge;
  392. UCI_NESTED(uci_set, ctx, &ptr);
  393. pctx->section = ptr.s;
  394. }
  395. }
  396. /*
  397. * parse the 'option' uci command (open a value)
  398. */
  399. static void uci_parse_option(struct uci_context *ctx, bool list)
  400. {
  401. struct uci_parse_context *pctx = ctx->pctx;
  402. struct uci_element *e;
  403. struct uci_ptr ptr;
  404. int ofs_name, ofs_value;
  405. char *name = NULL;
  406. char *value = NULL;
  407. if (!pctx->section)
  408. uci_parse_error(ctx, "option/list command found before the first section");
  409. /* command string null-terminated by strtok */
  410. if (uci_increase_pos(pctx, strlen(pctx_cur_str(pctx)) + 1))
  411. uci_parse_error(ctx, "option without name");
  412. ofs_name = next_arg(ctx, true, true, false);
  413. ofs_value = next_arg(ctx, false, false, false);
  414. assert_eol(ctx);
  415. name = pctx_str(pctx, ofs_name);
  416. value = pctx_str(pctx, ofs_value);
  417. uci_fill_ptr(ctx, &ptr, &pctx->section->e);
  418. e = uci_lookup_list(&pctx->section->options, name);
  419. if (e)
  420. ptr.o = uci_to_option(e);
  421. ptr.option = name;
  422. ptr.value = value;
  423. ctx->internal = !pctx->merge;
  424. if (list)
  425. UCI_NESTED(uci_add_list, ctx, &ptr);
  426. else
  427. UCI_NESTED(uci_set, ctx, &ptr);
  428. }
  429. /*
  430. * parse a complete input line, split up combined commands by ';'
  431. */
  432. static void uci_parse_line(struct uci_context *ctx, bool single)
  433. {
  434. struct uci_parse_context *pctx = ctx->pctx;
  435. char *word;
  436. /* Skip whitespace characters at the start of line */
  437. skip_whitespace(ctx);
  438. do {
  439. word = strtok(pctx_cur_str(pctx), " \t");
  440. if (!word)
  441. return;
  442. switch(word[0]) {
  443. case 0:
  444. case '#':
  445. return;
  446. case 'p':
  447. if ((word[1] == 0) || !strcmp(word + 1, "ackage"))
  448. uci_parse_package(ctx, single);
  449. else
  450. goto invalid;
  451. break;
  452. case 'c':
  453. if ((word[1] == 0) || !strcmp(word + 1, "onfig"))
  454. uci_parse_config(ctx);
  455. else
  456. goto invalid;
  457. break;
  458. case 'o':
  459. if ((word[1] == 0) || !strcmp(word + 1, "ption"))
  460. uci_parse_option(ctx, false);
  461. else
  462. goto invalid;
  463. break;
  464. case 'l':
  465. if ((word[1] == 0) || !strcmp(word + 1, "ist"))
  466. uci_parse_option(ctx, true);
  467. else
  468. goto invalid;
  469. break;
  470. default:
  471. goto invalid;
  472. }
  473. continue;
  474. invalid:
  475. uci_parse_error(ctx, "invalid command");
  476. } while (1);
  477. }
  478. /* max number of characters that escaping adds to the string */
  479. #define UCI_QUOTE_ESCAPE "'\\''"
  480. /*
  481. * escape an uci string for export
  482. */
  483. static const char *uci_escape(struct uci_context *ctx, const char *str)
  484. {
  485. const char *end;
  486. int ofs = 0;
  487. if (!ctx->buf) {
  488. ctx->bufsz = LINEBUF;
  489. ctx->buf = malloc(LINEBUF);
  490. if (!ctx->buf)
  491. return str;
  492. }
  493. while (1) {
  494. int len;
  495. end = strchr(str, '\'');
  496. if (!end)
  497. end = str + strlen(str);
  498. len = end - str;
  499. /* make sure that we have enough room in the buffer */
  500. while (ofs + len + (int) sizeof(UCI_QUOTE_ESCAPE) + 1 > ctx->bufsz) {
  501. ctx->bufsz *= 2;
  502. ctx->buf = uci_realloc(ctx, ctx->buf, ctx->bufsz);
  503. }
  504. /* copy the string until the character before the quote */
  505. memcpy(&ctx->buf[ofs], str, len);
  506. ofs += len;
  507. /* end of string? return the buffer */
  508. if (*end == 0)
  509. break;
  510. memcpy(&ctx->buf[ofs], UCI_QUOTE_ESCAPE, sizeof(UCI_QUOTE_ESCAPE));
  511. ofs += strlen(&ctx->buf[ofs]);
  512. str = end + 1;
  513. }
  514. ctx->buf[ofs] = 0;
  515. return ctx->buf;
  516. }
  517. /*
  518. * export a single config package to a file stream
  519. */
  520. static void uci_export_package(struct uci_package *p, FILE *stream, bool header)
  521. {
  522. struct uci_context *ctx = p->ctx;
  523. struct uci_element *s, *o, *i;
  524. if (header)
  525. fprintf(stream, "package %s\n", uci_escape(ctx, p->e.name));
  526. uci_foreach_element(&p->sections, s) {
  527. struct uci_section *sec = uci_to_section(s);
  528. fprintf(stream, "\nconfig %s", uci_escape(ctx, sec->type));
  529. if (!sec->anonymous || (ctx->flags & UCI_FLAG_EXPORT_NAME))
  530. fprintf(stream, " '%s'", uci_escape(ctx, sec->e.name));
  531. fprintf(stream, "\n");
  532. uci_foreach_element(&sec->options, o) {
  533. struct uci_option *opt = uci_to_option(o);
  534. switch(opt->type) {
  535. case UCI_TYPE_STRING:
  536. fprintf(stream, "\toption %s", uci_escape(ctx, opt->e.name));
  537. fprintf(stream, " '%s'\n", uci_escape(ctx, opt->v.string));
  538. break;
  539. case UCI_TYPE_LIST:
  540. uci_foreach_element(&opt->v.list, i) {
  541. fprintf(stream, "\tlist %s", uci_escape(ctx, opt->e.name));
  542. fprintf(stream, " '%s'\n", uci_escape(ctx, i->name));
  543. }
  544. break;
  545. default:
  546. fprintf(stream, "\t# unknown type for option '%s'\n", uci_escape(ctx, opt->e.name));
  547. break;
  548. }
  549. }
  550. }
  551. fprintf(stream, "\n");
  552. }
  553. int uci_export(struct uci_context *ctx, FILE *stream, struct uci_package *package, bool header)
  554. {
  555. struct uci_element *e;
  556. UCI_HANDLE_ERR(ctx);
  557. UCI_ASSERT(ctx, stream != NULL);
  558. if (package)
  559. uci_export_package(package, stream, header);
  560. else {
  561. uci_foreach_element(&ctx->root, e) {
  562. uci_export_package(uci_to_package(e), stream, header);
  563. }
  564. }
  565. return 0;
  566. }
  567. int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct uci_package **package, bool single)
  568. {
  569. struct uci_parse_context *pctx;
  570. UCI_HANDLE_ERR(ctx);
  571. /* make sure no memory from previous parse attempts is leaked */
  572. uci_cleanup(ctx);
  573. uci_alloc_parse_context(ctx);
  574. pctx = ctx->pctx;
  575. pctx->file = stream;
  576. if (package && *package && single) {
  577. pctx->package = *package;
  578. pctx->merge = true;
  579. }
  580. /*
  581. * If 'name' was supplied, assume that the supplied stream does not contain
  582. * the appropriate 'package <name>' string to specify the config name
  583. * NB: the config file can still override the package name
  584. */
  585. if (name) {
  586. UCI_ASSERT(ctx, uci_validate_package(name));
  587. pctx->name = name;
  588. }
  589. while (!feof(pctx->file)) {
  590. pctx->pos = 0;
  591. uci_getln(ctx, 0);
  592. UCI_TRAP_SAVE(ctx, error);
  593. if (pctx->buf[0])
  594. uci_parse_line(ctx, single);
  595. UCI_TRAP_RESTORE(ctx);
  596. continue;
  597. error:
  598. if (ctx->flags & UCI_FLAG_PERROR)
  599. uci_perror(ctx, NULL);
  600. if ((ctx->err != UCI_ERR_PARSE) ||
  601. (ctx->flags & UCI_FLAG_STRICT))
  602. UCI_THROW(ctx, ctx->err);
  603. }
  604. if (!pctx->package && name)
  605. uci_switch_config(ctx);
  606. if (package)
  607. *package = pctx->package;
  608. if (pctx->merge)
  609. pctx->package = NULL;
  610. pctx->name = NULL;
  611. uci_switch_config(ctx);
  612. /* no error happened, we can get rid of the parser context now */
  613. uci_cleanup(ctx);
  614. return 0;
  615. }
  616. static char *uci_config_path(struct uci_context *ctx, const char *name)
  617. {
  618. char *filename;
  619. UCI_ASSERT(ctx, uci_validate_package(name));
  620. filename = uci_malloc(ctx, strlen(name) + strlen(ctx->confdir) + 2);
  621. sprintf(filename, "%s/%s", ctx->confdir, name);
  622. return filename;
  623. }
  624. static void uci_file_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite)
  625. {
  626. struct uci_package *p = *package;
  627. FILE *f1, *f2 = NULL;
  628. char *volatile name = NULL;
  629. char *volatile path = NULL;
  630. char *filename = NULL;
  631. struct stat statbuf;
  632. volatile bool do_rename = false;
  633. int fd, sz;
  634. if (!p->path) {
  635. if (overwrite)
  636. p->path = uci_config_path(ctx, p->e.name);
  637. else
  638. UCI_THROW(ctx, UCI_ERR_INVAL);
  639. }
  640. sz = snprintf(NULL, 0, "%s/.%s.uci-XXXXXX", ctx->confdir, p->e.name);
  641. filename = alloca(sz + 1);
  642. snprintf(filename, sz + 1, "%s/.%s.uci-XXXXXX", ctx->confdir, p->e.name);
  643. /* open the config file for writing now, so that it is locked */
  644. f1 = uci_open_stream(ctx, p->path, NULL, SEEK_SET, true, true);
  645. /* flush unsaved changes and reload from delta file */
  646. UCI_TRAP_SAVE(ctx, done);
  647. if (p->has_delta) {
  648. if (!overwrite) {
  649. name = uci_strdup(ctx, p->e.name);
  650. path = uci_strdup(ctx, p->path);
  651. /* dump our own changes to the delta file */
  652. if (!uci_list_empty(&p->delta))
  653. UCI_INTERNAL(uci_save, ctx, p);
  654. /*
  655. * other processes might have modified the config
  656. * as well. dump and reload
  657. */
  658. uci_free_package(&p);
  659. uci_cleanup(ctx);
  660. UCI_INTERNAL(uci_import, ctx, f1, name, &p, true);
  661. p->path = path;
  662. p->has_delta = true;
  663. *package = p;
  664. /* freed together with the uci_package */
  665. path = NULL;
  666. }
  667. /* flush delta */
  668. if (!uci_load_delta(ctx, p, true))
  669. goto done;
  670. }
  671. fd = mkstemp(filename);
  672. if (fd == -1)
  673. UCI_THROW(ctx, UCI_ERR_IO);
  674. if ((flock(fd, LOCK_EX) < 0) && (errno != ENOSYS))
  675. UCI_THROW(ctx, UCI_ERR_IO);
  676. if (lseek(fd, 0, SEEK_SET) < 0)
  677. UCI_THROW(ctx, UCI_ERR_IO);
  678. f2 = fdopen(fd, "w+");
  679. if (!f2)
  680. UCI_THROW(ctx, UCI_ERR_IO);
  681. uci_export(ctx, f2, p, false);
  682. fflush(f2);
  683. fsync(fileno(f2));
  684. uci_close_stream(f2);
  685. do_rename = true;
  686. UCI_TRAP_RESTORE(ctx);
  687. done:
  688. free(name);
  689. free(path);
  690. uci_close_stream(f1);
  691. if (do_rename) {
  692. path = realpath(p->path, NULL);
  693. if (!path || stat(path, &statbuf) || chmod(filename, statbuf.st_mode) || rename(filename, path)) {
  694. unlink(filename);
  695. UCI_THROW(ctx, UCI_ERR_IO);
  696. }
  697. free(path);
  698. }
  699. if (ctx->err)
  700. UCI_THROW(ctx, ctx->err);
  701. }
  702. /*
  703. * This function returns the filename by returning the string
  704. * after the last '/' character. By checking for a non-'\0'
  705. * character afterwards, directories are ignored (glob marks
  706. * those with a trailing '/'
  707. */
  708. static inline char *get_filename(char *path)
  709. {
  710. char *p;
  711. p = strrchr(path, '/');
  712. p++;
  713. if (!*p)
  714. return NULL;
  715. return p;
  716. }
  717. static char **uci_list_config_files(struct uci_context *ctx)
  718. {
  719. char **configs;
  720. glob_t globbuf;
  721. int size, j, skipped;
  722. size_t i;
  723. char *buf;
  724. char *dir;
  725. dir = uci_malloc(ctx, strlen(ctx->confdir) + 1 + sizeof("/*"));
  726. sprintf(dir, "%s/*", ctx->confdir);
  727. if (glob(dir, GLOB_MARK, NULL, &globbuf) != 0) {
  728. free(dir);
  729. UCI_THROW(ctx, UCI_ERR_NOTFOUND);
  730. }
  731. size = sizeof(char *) * (globbuf.gl_pathc + 1);
  732. skipped = 0;
  733. for(i = 0; i < globbuf.gl_pathc; i++) {
  734. char *p;
  735. p = get_filename(globbuf.gl_pathv[i]);
  736. if (!p) {
  737. skipped++;
  738. continue;
  739. }
  740. size += strlen(p) + 1;
  741. }
  742. configs = uci_malloc(ctx, size - skipped);
  743. buf = (char *) &configs[globbuf.gl_pathc + 1 - skipped];
  744. j = 0;
  745. for(i = 0; i < globbuf.gl_pathc; i++) {
  746. char *p;
  747. p = get_filename(globbuf.gl_pathv[i]);
  748. if (!p)
  749. continue;
  750. if (!uci_validate_package(p))
  751. continue;
  752. configs[j++] = buf;
  753. strcpy(buf, p);
  754. buf += strlen(buf) + 1;
  755. }
  756. free(dir);
  757. globfree(&globbuf);
  758. return configs;
  759. }
  760. static struct uci_package *uci_file_load(struct uci_context *ctx,
  761. const char *volatile name)
  762. {
  763. struct uci_package *package = NULL;
  764. char *filename;
  765. bool confdir;
  766. FILE *volatile file = NULL;
  767. switch (name[0]) {
  768. case '.':
  769. /* relative path outside of /etc/config */
  770. if (name[1] != '/')
  771. UCI_THROW(ctx, UCI_ERR_NOTFOUND);
  772. /* fall through */
  773. case '/':
  774. /* absolute path outside of /etc/config */
  775. filename = uci_strdup(ctx, name);
  776. name = strrchr(name, '/') + 1;
  777. confdir = false;
  778. break;
  779. default:
  780. /* config in /etc/config */
  781. filename = uci_config_path(ctx, name);
  782. confdir = true;
  783. break;
  784. }
  785. UCI_TRAP_SAVE(ctx, done);
  786. file = uci_open_stream(ctx, filename, NULL, SEEK_SET, false, false);
  787. ctx->err = 0;
  788. UCI_INTERNAL(uci_import, ctx, file, name, &package, true);
  789. UCI_TRAP_RESTORE(ctx);
  790. if (package) {
  791. package->path = filename;
  792. package->has_delta = confdir;
  793. uci_load_delta(ctx, package, false);
  794. }
  795. done:
  796. uci_close_stream(file);
  797. if (ctx->err) {
  798. free(filename);
  799. UCI_THROW(ctx, ctx->err);
  800. }
  801. return package;
  802. }
  803. __private UCI_BACKEND(uci_file_backend, "file",
  804. .load = uci_file_load,
  805. .commit = uci_file_commit,
  806. .list_configs = uci_list_config_files,
  807. );