file.c 18 KB

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