cli.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /*
  2. * cli - Command Line Interface 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 General Public License version 2
  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. #include <strings.h>
  15. #include <string.h>
  16. #include <stdlib.h>
  17. #include <unistd.h>
  18. #include "uci.h"
  19. #define MAX_ARGS 4 /* max command line arguments for batch mode */
  20. static const char *appname;
  21. static enum {
  22. CLI_FLAG_MERGE = (1 << 0),
  23. CLI_FLAG_QUIET = (1 << 1),
  24. CLI_FLAG_NOCOMMIT = (1 << 2),
  25. CLI_FLAG_BATCH = (1 << 3),
  26. } flags;
  27. static FILE *input;
  28. static struct uci_context *ctx;
  29. enum {
  30. /* section cmds */
  31. CMD_GET,
  32. CMD_SET,
  33. CMD_DEL,
  34. CMD_RENAME,
  35. CMD_REVERT,
  36. /* package cmds */
  37. CMD_SHOW,
  38. CMD_CHANGES,
  39. CMD_EXPORT,
  40. CMD_COMMIT,
  41. /* other cmds */
  42. CMD_ADD,
  43. CMD_IMPORT,
  44. CMD_HELP,
  45. };
  46. static int uci_cmd(int argc, char **argv);
  47. static void uci_usage(void)
  48. {
  49. fprintf(stderr,
  50. "Usage: %s [<options>] <command> [<arguments>]\n\n"
  51. "Commands:\n"
  52. "\tbatch\n"
  53. "\texport [<config>]\n"
  54. "\timport [<config>]\n"
  55. "\tchanges [<config>]\n"
  56. "\tcommit [<config>]\n"
  57. "\tadd <config> <section-type>\n"
  58. "\tshow [<config>[.<section>[.<option>]]]\n"
  59. "\tget <config>.<section>[.<option>]\n"
  60. "\tset <config>.<section>[.<option>]=<value>\n"
  61. "\trename <config>.<section>[.<option>]=<name>\n"
  62. "\trevert <config>[.<section>[.<option>]]\n"
  63. "\n"
  64. "Options:\n"
  65. "\t-f <file> use <file> as input instead of stdin\n"
  66. "\t-m when importing, merge data into an existing package\n"
  67. "\t-n name unnamed sections on export (default)\n"
  68. "\t-N don't name unnamed sections\n"
  69. "\t-p <path> add a search path for config change files\n"
  70. "\t-P <path> add a search path for config change files and use as default\n"
  71. "\t-q quiet mode (don't print error messages)\n"
  72. "\t-s force strict mode (stop on parser errors, default)\n"
  73. "\t-S disable strict mode\n"
  74. "\n",
  75. appname
  76. );
  77. }
  78. static void cli_perror(void)
  79. {
  80. if (flags & CLI_FLAG_QUIET)
  81. return;
  82. uci_perror(ctx, appname);
  83. }
  84. static void uci_show_section(struct uci_section *p)
  85. {
  86. struct uci_element *e;
  87. const char *cname, *sname;
  88. cname = p->package->e.name;
  89. sname = p->e.name;
  90. printf("%s.%s=%s\n", cname, sname, p->type);
  91. uci_foreach_element(&p->options, e) {
  92. printf("%s.%s.%s=%s\n", cname, sname, e->name, uci_to_option(e)->value);
  93. }
  94. }
  95. static void uci_show_package(struct uci_package *p)
  96. {
  97. struct uci_element *e;
  98. uci_foreach_element( &p->sections, e) {
  99. uci_show_section(uci_to_section(e));
  100. }
  101. }
  102. static void uci_show_changes(struct uci_package *p)
  103. {
  104. struct uci_element *e;
  105. uci_foreach_element(&p->saved_history, e) {
  106. struct uci_history *h = uci_to_history(e);
  107. if (h->cmd == UCI_CMD_REMOVE)
  108. printf("-");
  109. printf("%s.%s", p->e.name, h->section);
  110. if (e->name)
  111. printf(".%s", e->name);
  112. if (h->cmd != UCI_CMD_REMOVE)
  113. printf("=%s", h->value);
  114. printf("\n");
  115. }
  116. }
  117. static int package_cmd(int cmd, char *package)
  118. {
  119. struct uci_package *p = NULL;
  120. int ret;
  121. if (cmd == CMD_CHANGES)
  122. ctx->flags |= UCI_FLAG_SAVED_HISTORY;
  123. ret = uci_load(ctx, package, &p);
  124. if (cmd == CMD_CHANGES)
  125. ctx->flags &= ~UCI_FLAG_SAVED_HISTORY;
  126. if (ret != UCI_OK) {
  127. cli_perror();
  128. return 1;
  129. }
  130. if (!p)
  131. return 0;
  132. switch(cmd) {
  133. case CMD_CHANGES:
  134. uci_show_changes(p);
  135. break;
  136. case CMD_COMMIT:
  137. if (flags & CLI_FLAG_NOCOMMIT)
  138. return 0;
  139. if (uci_commit(ctx, &p, false) != UCI_OK)
  140. cli_perror();
  141. break;
  142. case CMD_EXPORT:
  143. uci_export(ctx, stdout, p, true);
  144. break;
  145. case CMD_SHOW:
  146. uci_show_package(p);
  147. break;
  148. }
  149. uci_unload(ctx, p);
  150. return 0;
  151. }
  152. static int uci_do_import(int argc, char **argv)
  153. {
  154. struct uci_package *package = NULL;
  155. char *name = NULL;
  156. int ret = UCI_OK;
  157. bool merge = false;
  158. if (argc > 2)
  159. return 255;
  160. if (argc == 2)
  161. name = argv[1];
  162. else if (flags & CLI_FLAG_MERGE)
  163. /* need a package to merge */
  164. return 255;
  165. if (flags & CLI_FLAG_MERGE) {
  166. if (uci_load(ctx, name, &package) != UCI_OK)
  167. package = NULL;
  168. else
  169. merge = true;
  170. }
  171. ret = uci_import(ctx, input, name, &package, (name != NULL));
  172. if (ret == UCI_OK) {
  173. if (merge) {
  174. ret = uci_save(ctx, package);
  175. } else {
  176. struct uci_element *e;
  177. /* loop through all config sections and overwrite existing data */
  178. uci_foreach_element(&ctx->root, e) {
  179. struct uci_package *p = uci_to_package(e);
  180. ret = uci_commit(ctx, &p, true);
  181. }
  182. }
  183. }
  184. if (ret != UCI_OK) {
  185. cli_perror();
  186. return 1;
  187. }
  188. return 0;
  189. }
  190. static int uci_do_package_cmd(int cmd, int argc, char **argv)
  191. {
  192. char **configs = NULL;
  193. char **p;
  194. if (argc > 2)
  195. return 255;
  196. if (argc == 2)
  197. return package_cmd(cmd, argv[1]);
  198. if ((uci_list_configs(ctx, &configs) != UCI_OK) || !configs) {
  199. cli_perror();
  200. return 1;
  201. }
  202. for (p = configs; *p; p++) {
  203. package_cmd(cmd, *p);
  204. }
  205. return 0;
  206. }
  207. static int uci_do_add(int argc, char **argv)
  208. {
  209. struct uci_package *p = NULL;
  210. struct uci_section *s = NULL;
  211. int ret;
  212. if (argc != 3)
  213. return 255;
  214. ret = uci_load(ctx, argv[1], &p);
  215. if (ret != UCI_OK)
  216. goto done;
  217. ret = uci_add_section(ctx, p, argv[2], &s);
  218. if (ret != UCI_OK)
  219. goto done;
  220. ret = uci_save(ctx, p);
  221. done:
  222. if (ret != UCI_OK)
  223. cli_perror();
  224. else if (s)
  225. fprintf(stdout, "%s\n", s->e.name);
  226. return ret;
  227. }
  228. static int uci_do_section_cmd(int cmd, int argc, char **argv)
  229. {
  230. struct uci_package *p = NULL;
  231. struct uci_element *e = NULL;
  232. char *package = NULL;
  233. char *section = NULL;
  234. char *option = NULL;
  235. char *value = NULL;
  236. char **ptr = NULL;
  237. int ret = UCI_OK;
  238. if (argc != 2)
  239. return 255;
  240. switch(cmd) {
  241. case CMD_SET:
  242. case CMD_RENAME:
  243. ptr = &value;
  244. break;
  245. default:
  246. break;
  247. }
  248. if (uci_parse_tuple(ctx, argv[1], &package, &section, &option, ptr) != UCI_OK)
  249. return 1;
  250. if (section && !section[0])
  251. return 1;
  252. if (uci_load(ctx, package, &p) != UCI_OK) {
  253. cli_perror();
  254. return 1;
  255. }
  256. if (!p)
  257. return 0;
  258. switch(cmd) {
  259. case CMD_GET:
  260. if (uci_lookup(ctx, &e, p, section, option) != UCI_OK)
  261. return 1;
  262. switch(e->type) {
  263. case UCI_TYPE_SECTION:
  264. value = uci_to_section(e)->type;
  265. break;
  266. case UCI_TYPE_OPTION:
  267. value = uci_to_option(e)->value;
  268. break;
  269. default:
  270. /* should not happen */
  271. return 1;
  272. }
  273. /* throw the value to stdout */
  274. printf("%s\n", value);
  275. break;
  276. case CMD_RENAME:
  277. ret = uci_rename(ctx, p, section, option, value);
  278. break;
  279. case CMD_REVERT:
  280. ret = uci_revert(ctx, &p, section, option);
  281. break;
  282. case CMD_SET:
  283. ret = uci_set(ctx, p, section, option, value, NULL);
  284. break;
  285. case CMD_DEL:
  286. ret = uci_delete(ctx, p, section, option);
  287. break;
  288. }
  289. /* no save necessary for get */
  290. if ((cmd == CMD_GET) || (cmd == CMD_REVERT))
  291. return 0;
  292. /* save changes, but don't commit them yet */
  293. if (ret == UCI_OK)
  294. ret = uci_save(ctx, p);
  295. if (ret != UCI_OK) {
  296. cli_perror();
  297. return 1;
  298. }
  299. return 0;
  300. }
  301. static int uci_batch_cmd(void)
  302. {
  303. char *argv[MAX_ARGS];
  304. char *str = NULL;
  305. int ret = 0;
  306. int i, j;
  307. for(i = 0; i <= MAX_ARGS; i++) {
  308. if (i == MAX_ARGS) {
  309. fprintf(stderr, "Too many arguments\n");
  310. return 1;
  311. }
  312. argv[i] = NULL;
  313. if ((ret = uci_parse_argument(ctx, input, &str, &argv[i])) != UCI_OK) {
  314. cli_perror();
  315. i = 0;
  316. break;
  317. }
  318. if (!argv[i][0])
  319. break;
  320. argv[i] = strdup(argv[i]);
  321. if (!argv[i]) {
  322. perror("uci");
  323. return 1;
  324. }
  325. }
  326. argv[i] = NULL;
  327. if (i > 0) {
  328. if (!strcasecmp(argv[0], "exit"))
  329. return 254;
  330. ret = uci_cmd(i, argv);
  331. } else
  332. return 0;
  333. for (j = 0; j < i; j++) {
  334. if (argv[j])
  335. free(argv[j]);
  336. }
  337. return ret;
  338. }
  339. static int uci_batch(void)
  340. {
  341. int ret = 0;
  342. while (!feof(input)) {
  343. struct uci_element *e, *tmp;
  344. ret = uci_batch_cmd();
  345. if (ret == 254)
  346. return 0;
  347. else if (ret == 255)
  348. fprintf(stderr, "Unknown command\n");
  349. /* clean up */
  350. uci_foreach_element_safe(&ctx->root, tmp, e) {
  351. uci_unload(ctx, uci_to_package(e));
  352. }
  353. }
  354. return 0;
  355. }
  356. static int uci_cmd(int argc, char **argv)
  357. {
  358. int cmd = 0;
  359. if (!strcasecmp(argv[0], "batch") && !(flags & CLI_FLAG_BATCH))
  360. return uci_batch();
  361. else if (!strcasecmp(argv[0], "show"))
  362. cmd = CMD_SHOW;
  363. else if (!strcasecmp(argv[0], "changes"))
  364. cmd = CMD_CHANGES;
  365. else if (!strcasecmp(argv[0], "export"))
  366. cmd = CMD_EXPORT;
  367. else if (!strcasecmp(argv[0], "commit"))
  368. cmd = CMD_COMMIT;
  369. else if (!strcasecmp(argv[0], "get"))
  370. cmd = CMD_GET;
  371. else if (!strcasecmp(argv[0], "set"))
  372. cmd = CMD_SET;
  373. else if (!strcasecmp(argv[0], "ren") ||
  374. !strcasecmp(argv[0], "rename"))
  375. cmd = CMD_RENAME;
  376. else if (!strcasecmp(argv[0], "revert"))
  377. cmd = CMD_REVERT;
  378. else if (!strcasecmp(argv[0], "del"))
  379. cmd = CMD_DEL;
  380. else if (!strcasecmp(argv[0], "import"))
  381. cmd = CMD_IMPORT;
  382. else if (!strcasecmp(argv[0], "help"))
  383. cmd = CMD_HELP;
  384. else if (!strcasecmp(argv[0], "add"))
  385. cmd = CMD_ADD;
  386. else
  387. cmd = -1;
  388. switch(cmd) {
  389. case CMD_GET:
  390. case CMD_SET:
  391. case CMD_DEL:
  392. case CMD_RENAME:
  393. case CMD_REVERT:
  394. return uci_do_section_cmd(cmd, argc, argv);
  395. case CMD_SHOW:
  396. case CMD_EXPORT:
  397. case CMD_COMMIT:
  398. case CMD_CHANGES:
  399. return uci_do_package_cmd(cmd, argc, argv);
  400. case CMD_IMPORT:
  401. return uci_do_import(argc, argv);
  402. case CMD_ADD:
  403. return uci_do_add(argc, argv);
  404. case CMD_HELP:
  405. uci_usage();
  406. return 0;
  407. default:
  408. return 255;
  409. }
  410. }
  411. int main(int argc, char **argv)
  412. {
  413. int ret;
  414. int c;
  415. appname = argv[0];
  416. input = stdin;
  417. ctx = uci_alloc_context();
  418. if (!ctx) {
  419. fprintf(stderr, "Out of memory\n");
  420. return 1;
  421. }
  422. while((c = getopt(argc, argv, "f:mnNp:P:sSq")) != -1) {
  423. switch(c) {
  424. case 'f':
  425. input = fopen(optarg, "r");
  426. if (!input) {
  427. perror("uci");
  428. return 1;
  429. }
  430. break;
  431. case 'm':
  432. flags |= CLI_FLAG_MERGE;
  433. break;
  434. case 's':
  435. ctx->flags |= UCI_FLAG_STRICT;
  436. break;
  437. case 'S':
  438. ctx->flags &= ~UCI_FLAG_STRICT;
  439. ctx->flags |= UCI_FLAG_PERROR;
  440. break;
  441. case 'n':
  442. ctx->flags |= UCI_FLAG_EXPORT_NAME;
  443. break;
  444. case 'N':
  445. ctx->flags &= ~UCI_FLAG_EXPORT_NAME;
  446. break;
  447. case 'p':
  448. uci_add_history_path(ctx, optarg);
  449. break;
  450. case 'P':
  451. uci_add_history_path(ctx, ctx->savedir);
  452. uci_set_savedir(ctx, optarg);
  453. flags |= CLI_FLAG_NOCOMMIT;
  454. break;
  455. case 'q':
  456. flags |= CLI_FLAG_QUIET;
  457. break;
  458. default:
  459. uci_usage();
  460. return 0;
  461. }
  462. }
  463. if (optind > 1)
  464. argv[optind - 1] = argv[0];
  465. argv += optind - 1;
  466. argc -= optind - 1;
  467. if (argc < 2) {
  468. uci_usage();
  469. return 0;
  470. }
  471. ret = uci_cmd(argc - 1, argv + 1);
  472. if (input != stdin)
  473. fclose(input);
  474. if (ret == 255) {
  475. uci_usage();
  476. return 0;
  477. }
  478. uci_free_context(ctx);
  479. return ret;
  480. }