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