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