file.c 12 KB

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