file.c 20 KB

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