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 <stdarg.h> 18 #include <errno.h> 19 #include <unistd.h> 20 #include "uci.h" 21 22 #define MAX_ARGS 4 /* max command line arguments for batch mode */ 23 24 static const char *delimiter = " "; 25 static const char *appname; 26 static enum { 27 CLI_FLAG_MERGE = (1 << 0), 28 CLI_FLAG_QUIET = (1 << 1), 29 CLI_FLAG_NOCOMMIT = (1 << 2), 30 CLI_FLAG_BATCH = (1 << 3), 31 CLI_FLAG_SHOW_EXT = (1 << 4), 32 } flags; 33 34 static FILE *input; 35 36 static struct uci_context *ctx; 37 enum { 38 /* section cmds */ 39 CMD_GET, 40 CMD_SET, 41 CMD_ADD_LIST, 42 CMD_DEL_LIST, 43 CMD_DEL, 44 CMD_RENAME, 45 CMD_REVERT, 46 CMD_REORDER, 47 /* package cmds */ 48 CMD_SHOW, 49 CMD_CHANGES, 50 CMD_EXPORT, 51 CMD_COMMIT, 52 /* other cmds */ 53 CMD_ADD, 54 CMD_IMPORT, 55 CMD_HELP, 56 }; 57 58 struct uci_type_list { 59 unsigned int idx; 60 const char *name; 61 struct uci_type_list *next; 62 }; 63 64 static struct uci_type_list *type_list = NULL; 65 static char *typestr = NULL; 66 static const char *cur_section_ref = NULL; 67 68 static int uci_cmd(int argc, char **argv); 69 70 static void 71 uci_reset_typelist(void) 72 { 73 struct uci_type_list *type; 74 while (type_list != NULL) { 75 type = type_list; 76 type_list = type_list->next; 77 free(type); 78 } 79 if (typestr) { 80 free(typestr); 81 typestr = NULL; 82 } 83 cur_section_ref = NULL; 84 } 85 86 static char * 87 uci_lookup_section_ref(struct uci_section *s) 88 { 89 struct uci_type_list *ti = type_list; 90 char *ret; 91 int maxlen; 92 93 if (!(flags & CLI_FLAG_SHOW_EXT)) 94 return s->e.name; 95 96 /* look up in section type list */ 97 while (ti) { 98 if (strcmp(ti->name, s->type) == 0) 99 break; 100 ti = ti->next; 101 } 102 if (!ti) { 103 ti = calloc(1, sizeof(struct uci_type_list)); 104 if (!ti) 105 return NULL; 106 ti->next = type_list; 107 type_list = ti; 108 ti->name = s->type; 109 } 110 111 if (s->anonymous) { 112 maxlen = strlen(s->type) + 1 + 2 + 10; 113 if (!typestr) { 114 typestr = malloc(maxlen); 115 if (!typestr) 116 return NULL; 117 } else { 118 void *p = realloc(typestr, maxlen); 119 if (!p) { 120 free(typestr); 121 return NULL; 122 } 123 124 typestr = p; 125 } 126 127 if (typestr) 128 sprintf(typestr, "@%s[%d]", ti->name, ti->idx); 129 130 ret = typestr; 131 } else { 132 ret = s->e.name; 133 } 134 135 ti->idx++; 136 137 return ret; 138 } 139 140 static void uci_usage(void) 141 { 142 fprintf(stderr, 143 "Usage: %s [<options>] <command> [<arguments>]\n\n" 144 "Commands:\n" 145 "\tbatch\n" 146 "\texport [<config>]\n" 147 "\timport [<config>]\n" 148 "\tchanges [<config>]\n" 149 "\tcommit [<config>]\n" 150 "\tadd <config> <section-type>\n" 151 "\tadd_list <config>.<section>.<option>=<string>\n" 152 "\tdel_list <config>.<section>.<option>=<string>\n" 153 "\tshow [<config>[.<section>[.<option>]]]\n" 154 "\tget <config>.<section>[.<option>]\n" 155 "\tset <config>.<section>[.<option>]=<value>\n" 156 "\tdelete <config>[.<section>[[.<option>][=<id>]]]\n" 157 "\trename <config>.<section>[.<option>]=<name>\n" 158 "\trevert <config>[.<section>[.<option>]]\n" 159 "\treorder <config>.<section>=<position>\n" 160 "\n" 161 "Options:\n" 162 "\t-c <path> set the search path for config files (default: "UCI_CONFDIR")\n" 163 "\t-C <path> set the search path for config override files (default: "UCI_CONF2DIR")\n" 164 "\t-d <str> set the delimiter for list values in uci show\n" 165 "\t-f <file> use <file> as input instead of stdin\n" 166 "\t-m when importing, merge data into an existing package\n" 167 "\t-n name unnamed sections on export (default)\n" 168 "\t-N don't name unnamed sections\n" 169 "\t-p <path> add a search path for config change files\n" 170 "\t-P <path> add a search path for config change files and use as default\n" 171 "\t-t <path> set save path for config change files\n" 172 "\t-q quiet mode (don't print error messages)\n" 173 "\t-s force strict mode (stop on parser errors, default)\n" 174 "\t-S disable strict mode\n" 175 "\t-X do not use extended syntax on 'show'\n" 176 "\n", 177 appname 178 ); 179 } 180 181 static void cli_perror(void) 182 { 183 if (flags & CLI_FLAG_QUIET) 184 return; 185 186 uci_perror(ctx, appname); 187 } 188 189 __attribute__((format(printf, 1, 2))) 190 static void cli_error(const char *fmt, ...) 191 { 192 va_list ap; 193 194 if (flags & CLI_FLAG_QUIET) 195 return; 196 197 va_start(ap, fmt); 198 vfprintf(stderr, fmt, ap); 199 va_end(ap); 200 } 201 202 static void uci_print_value(FILE *f, const char *v) 203 { 204 fprintf(f, "'"); 205 while (*v) { 206 if (*v != '\'') 207 fputc(*v, f); 208 else 209 fprintf(f, "'\\''"); 210 v++; 211 } 212 fprintf(f, "'"); 213 } 214 215 static void uci_show_value(struct uci_option *o, bool quote) 216 { 217 struct uci_element *e; 218 bool sep = false; 219 char *space; 220 221 switch(o->type) { 222 case UCI_TYPE_STRING: 223 if (quote) 224 uci_print_value(stdout, o->v.string); 225 else 226 printf("%s", o->v.string); 227 printf("\n"); 228 break; 229 case UCI_TYPE_LIST: 230 uci_foreach_element(&o->v.list, e) { 231 printf("%s", (sep ? delimiter : "")); 232 space = strpbrk(e->name, " \t\r\n"); 233 if (!space && !quote) 234 printf("%s", e->name); 235 else 236 uci_print_value(stdout, e->name); 237 sep = true; 238 } 239 printf("\n"); 240 break; 241 default: 242 printf("<unknown>\n"); 243 break; 244 } 245 } 246 247 static void uci_show_option(struct uci_option *o, bool quote) 248 { 249 printf("%s.%s.%s=", 250 o->section->package->e.name, 251 (cur_section_ref ? cur_section_ref : o->section->e.name), 252 o->e.name); 253 uci_show_value(o, quote); 254 } 255 256 static void uci_show_section(struct uci_section *s) 257 { 258 struct uci_element *e; 259 const char *cname; 260 const char *sname; 261 262 cname = s->package->e.name; 263 sname = (cur_section_ref ? cur_section_ref : s->e.name); 264 printf("%s.%s=%s\n", cname, sname, s->type); 265 uci_foreach_element(&s->options, e) { 266 uci_show_option(uci_to_option(e), true); 267 } 268 } 269 270 static void uci_show_package(struct uci_package *p) 271 { 272 struct uci_element *e; 273 274 uci_reset_typelist(); 275 uci_foreach_element( &p->sections, e) { 276 struct uci_section *s = uci_to_section(e); 277 cur_section_ref = uci_lookup_section_ref(s); 278 uci_show_section(s); 279 } 280 uci_reset_typelist(); 281 } 282 283 static void uci_show_changes(struct uci_package *p) 284 { 285 struct uci_element *e; 286 287 uci_foreach_element(&p->saved_delta, e) { 288 struct uci_delta *h = uci_to_delta(e); 289 char *prefix = ""; 290 char *op = "="; 291 292 switch(h->cmd) { 293 case UCI_CMD_REMOVE: 294 prefix = "-"; 295 break; 296 case UCI_CMD_LIST_ADD: 297 op = "+="; 298 break; 299 case UCI_CMD_LIST_DEL: 300 op = "-="; 301 break; 302 default: 303 break; 304 } 305 printf("%s%s.%s", prefix, p->e.name, h->section); 306 if (e->name) 307 printf(".%s", e->name); 308 if (h->cmd != UCI_CMD_REMOVE) { 309 printf("%s", op); 310 uci_print_value(stdout, h->value); 311 } 312 printf("\n"); 313 } 314 } 315 316 static int package_cmd(int cmd, char *tuple) 317 { 318 struct uci_ptr ptr; 319 int ret = 1; 320 321 if (uci_lookup_ptr(ctx, &ptr, tuple, true) != UCI_OK) { 322 cli_perror(); 323 return 1; 324 } 325 326 switch(cmd) { 327 case CMD_CHANGES: 328 uci_show_changes(ptr.p); 329 break; 330 case CMD_COMMIT: 331 if (flags & CLI_FLAG_NOCOMMIT) { 332 ret = 0; 333 goto out; 334 } 335 if (uci_commit(ctx, &ptr.p, false) != UCI_OK) { 336 cli_perror(); 337 goto out; 338 } 339 break; 340 case CMD_EXPORT: 341 if (uci_export(ctx, stdout, ptr.p, true) != UCI_OK) { 342 goto out; 343 } 344 break; 345 case CMD_SHOW: 346 if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) { 347 ctx->err = UCI_ERR_NOTFOUND; 348 cli_perror(); 349 goto out; 350 } 351 if (ptr.o) 352 uci_show_option(ptr.o, true); 353 else if (ptr.s) 354 uci_show_section(ptr.s); 355 else if (ptr.p) 356 uci_show_package(ptr.p); 357 else 358 goto out; /* should not happen */ 359 break; 360 } 361 362 ret = 0; 363 364 out: 365 if (ptr.p) 366 uci_unload(ctx, ptr.p); 367 return ret; 368 } 369 370 static int uci_do_import(int argc, char **argv) 371 { 372 struct uci_package *package = NULL; 373 char *name = NULL; 374 int ret = UCI_OK; 375 bool merge = false; 376 377 if (argc > 2) 378 return 255; 379 380 if (argc == 2) 381 name = argv[1]; 382 else if (flags & CLI_FLAG_MERGE) 383 /* need a package to merge */ 384 return 255; 385 386 if (flags & CLI_FLAG_MERGE) { 387 if (uci_load(ctx, name, &package) != UCI_OK) 388 package = NULL; 389 else 390 merge = true; 391 } 392 ret = uci_import(ctx, input, name, &package, (name != NULL)); 393 if (ret == UCI_OK) { 394 if (merge) { 395 ret = uci_save(ctx, package); 396 } else { 397 struct uci_element *e; 398 /* loop through all config sections and overwrite existing data */ 399 uci_foreach_element(&ctx->root, e) { 400 struct uci_package *p = uci_to_package(e); 401 ret = uci_commit(ctx, &p, true); 402 } 403 } 404 } 405 406 if (ret != UCI_OK) { 407 cli_perror(); 408 return 1; 409 } 410 411 return 0; 412 } 413 414 static int uci_do_package_cmd(int cmd, int argc, char **argv) 415 { 416 char **configs = NULL; 417 char **p; 418 int ret = 1; 419 420 if (argc > 2) 421 return 255; 422 423 if (argc == 2) 424 return package_cmd(cmd, argv[1]); 425 426 if ((uci_list_configs(ctx, &configs) != UCI_OK) || !configs) { 427 cli_perror(); 428 goto out; 429 } 430 431 for (p = configs; *p; p++) { 432 package_cmd(cmd, *p); 433 } 434 435 ret = 0; 436 out: 437 free(configs); 438 return ret; 439 } 440 441 static int uci_do_add(int argc, char **argv) 442 { 443 struct uci_package *p = NULL; 444 struct uci_section *s = NULL; 445 int ret; 446 447 if (argc != 3) 448 return 255; 449 450 ret = uci_load(ctx, argv[1], &p); 451 if (ret != UCI_OK) 452 goto done; 453 454 ret = uci_add_section(ctx, p, argv[2], &s); 455 if (ret != UCI_OK) 456 goto done; 457 458 ret = uci_save(ctx, p); 459 460 done: 461 if (ret != UCI_OK) 462 cli_perror(); 463 else if (s) 464 fprintf(stdout, "%s\n", s->e.name); 465 466 return ret; 467 } 468 469 static int uci_do_section_cmd(int cmd, int argc, char **argv) 470 { 471 struct uci_ptr ptr; 472 int ret = UCI_OK; 473 int dummy; 474 475 if (argc != 2) 476 return 255; 477 478 if (uci_lookup_ptr(ctx, &ptr, argv[1], true) != UCI_OK) { 479 cli_perror(); 480 return 1; 481 } 482 483 if (ptr.value && (cmd != CMD_SET) && (cmd != CMD_DEL) && 484 (cmd != CMD_ADD_LIST) && (cmd != CMD_DEL_LIST) && 485 (cmd != CMD_RENAME) && (cmd != CMD_REORDER)) 486 return 1; 487 488 switch(cmd) { 489 case CMD_GET: 490 if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) { 491 ctx->err = UCI_ERR_NOTFOUND; 492 cli_perror(); 493 return 1; 494 } 495 if (ptr.o) 496 uci_show_value(ptr.o, false); 497 else if (ptr.s) 498 printf("%s\n", ptr.s->type); 499 break; 500 case CMD_RENAME: 501 ret = uci_rename(ctx, &ptr); 502 break; 503 case CMD_REVERT: 504 ret = uci_revert(ctx, &ptr); 505 break; 506 case CMD_SET: 507 ret = uci_set(ctx, &ptr); 508 break; 509 case CMD_ADD_LIST: 510 ret = uci_add_list(ctx, &ptr); 511 break; 512 case CMD_DEL_LIST: 513 ret = uci_del_list(ctx, &ptr); 514 break; 515 case CMD_REORDER: 516 if (!ptr.s || !ptr.value) { 517 ctx->err = UCI_ERR_NOTFOUND; 518 cli_perror(); 519 return 1; 520 } 521 ret = uci_reorder_section(ctx, ptr.s, strtoul(ptr.value, NULL, 10)); 522 break; 523 case CMD_DEL: 524 if (ptr.value && !sscanf(ptr.value, "%d", &dummy)) 525 return 1; 526 ret = uci_delete(ctx, &ptr); 527 break; 528 } 529 530 /* no save necessary for get */ 531 if ((cmd == CMD_GET) || (cmd == CMD_REVERT)) 532 return 0; 533 534 /* save changes, but don't commit them yet */ 535 if (ret == UCI_OK) 536 ret = uci_save(ctx, ptr.p); 537 538 if (ret != UCI_OK) { 539 cli_perror(); 540 return 1; 541 } 542 543 return 0; 544 } 545 546 static int uci_batch_cmd(void) 547 { 548 char *argv[MAX_ARGS + 2]; 549 char *str = NULL; 550 int ret = 0; 551 int i, j; 552 553 for(i = 0; i <= MAX_ARGS; i++) { 554 if (i == MAX_ARGS) { 555 cli_error("Too many arguments\n"); 556 return 1; 557 } 558 argv[i] = NULL; 559 if (uci_parse_argument(ctx, input, &str, &argv[i]) != UCI_OK) { 560 cli_perror(); 561 i = 0; 562 break; 563 } 564 if (!argv[i][0]) 565 break; 566 argv[i] = strdup(argv[i]); 567 if (!argv[i]) { 568 cli_error("uci: %s", strerror(errno)); 569 return 1; 570 } 571 } 572 argv[i] = NULL; 573 574 if (i > 0) { 575 if (!strcasecmp(argv[0], "exit")) 576 return 254; 577 ret = uci_cmd(i, argv); 578 } else 579 return 0; 580 581 for (j = 0; j < i; j++) { 582 free(argv[j]); 583 } 584 585 return ret; 586 } 587 588 static int uci_batch(void) 589 { 590 int ret = 0; 591 592 flags |= CLI_FLAG_BATCH; 593 while (!feof(input)) { 594 struct uci_element *e, *tmp; 595 596 ret = uci_batch_cmd(); 597 if (ret == 254) 598 return 0; 599 else if (ret == 255) 600 cli_error("Unknown command\n"); 601 602 /* clean up */ 603 uci_foreach_element_safe(&ctx->root, tmp, e) { 604 uci_unload(ctx, uci_to_package(e)); 605 } 606 } 607 flags &= ~CLI_FLAG_BATCH; 608 609 return 0; 610 } 611 612 static int uci_cmd(int argc, char **argv) 613 { 614 int cmd = 0; 615 616 if (!strcasecmp(argv[0], "batch") && !(flags & CLI_FLAG_BATCH)) 617 return uci_batch(); 618 else if (!strcasecmp(argv[0], "show")) 619 cmd = CMD_SHOW; 620 else if (!strcasecmp(argv[0], "changes")) 621 cmd = CMD_CHANGES; 622 else if (!strcasecmp(argv[0], "export")) 623 cmd = CMD_EXPORT; 624 else if (!strcasecmp(argv[0], "commit")) 625 cmd = CMD_COMMIT; 626 else if (!strcasecmp(argv[0], "get")) 627 cmd = CMD_GET; 628 else if (!strcasecmp(argv[0], "set")) 629 cmd = CMD_SET; 630 else if (!strcasecmp(argv[0], "ren") || 631 !strcasecmp(argv[0], "rename")) 632 cmd = CMD_RENAME; 633 else if (!strcasecmp(argv[0], "revert")) 634 cmd = CMD_REVERT; 635 else if (!strcasecmp(argv[0], "reorder")) 636 cmd = CMD_REORDER; 637 else if (!strcasecmp(argv[0], "del") || 638 !strcasecmp(argv[0], "delete")) 639 cmd = CMD_DEL; 640 else if (!strcasecmp(argv[0], "import")) 641 cmd = CMD_IMPORT; 642 else if (!strcasecmp(argv[0], "help")) 643 cmd = CMD_HELP; 644 else if (!strcasecmp(argv[0], "add")) 645 cmd = CMD_ADD; 646 else if (!strcasecmp(argv[0], "add_list")) 647 cmd = CMD_ADD_LIST; 648 else if (!strcasecmp(argv[0], "del_list")) 649 cmd = CMD_DEL_LIST; 650 else 651 cmd = -1; 652 653 switch(cmd) { 654 case CMD_ADD_LIST: 655 case CMD_DEL_LIST: 656 case CMD_GET: 657 case CMD_SET: 658 case CMD_DEL: 659 case CMD_RENAME: 660 case CMD_REVERT: 661 case CMD_REORDER: 662 return uci_do_section_cmd(cmd, argc, argv); 663 case CMD_SHOW: 664 case CMD_EXPORT: 665 case CMD_COMMIT: 666 case CMD_CHANGES: 667 return uci_do_package_cmd(cmd, argc, argv); 668 case CMD_IMPORT: 669 return uci_do_import(argc, argv); 670 case CMD_ADD: 671 return uci_do_add(argc, argv); 672 case CMD_HELP: 673 uci_usage(); 674 return 0; 675 default: 676 return 255; 677 } 678 } 679 680 int main(int argc, char **argv) 681 { 682 int ret; 683 int c; 684 685 flags = CLI_FLAG_SHOW_EXT; 686 appname = argv[0]; 687 input = stdin; 688 ctx = uci_alloc_context(); 689 if (!ctx) { 690 cli_error("Out of memory\n"); 691 return 1; 692 } 693 694 while((c = getopt(argc, argv, "c:C:d:f:LmnNp:P:qsSt:X")) != -1) { 695 switch(c) { 696 case 'c': 697 uci_set_confdir(ctx, optarg); 698 break; 699 case 'C': 700 uci_set_conf2dir(ctx, optarg); 701 break; 702 case 'd': 703 delimiter = optarg; 704 break; 705 case 'f': 706 if (input != stdin) { 707 fclose(input); 708 cli_error("Too many input files.\n"); 709 return 1; 710 } 711 712 input = fopen(optarg, "r"); 713 if (!input) { 714 cli_error("uci: %s", strerror(errno)); 715 return 1; 716 } 717 break; 718 case 'm': 719 flags |= CLI_FLAG_MERGE; 720 break; 721 case 's': 722 ctx->flags |= UCI_FLAG_STRICT; 723 break; 724 case 'S': 725 ctx->flags &= ~UCI_FLAG_STRICT; 726 ctx->flags |= UCI_FLAG_PERROR; 727 break; 728 case 'n': 729 ctx->flags |= UCI_FLAG_EXPORT_NAME; 730 break; 731 case 'N': 732 ctx->flags &= ~UCI_FLAG_EXPORT_NAME; 733 break; 734 case 'p': 735 uci_add_delta_path(ctx, optarg); 736 break; 737 case 'P': 738 uci_set_savedir(ctx, optarg); 739 flags |= CLI_FLAG_NOCOMMIT; 740 break; 741 case 'q': 742 flags |= CLI_FLAG_QUIET; 743 break; 744 case 't': 745 uci_set_savedir(ctx, optarg); 746 break; 747 case 'X': 748 flags &= ~CLI_FLAG_SHOW_EXT; 749 break; 750 default: 751 uci_usage(); 752 return 0; 753 } 754 } 755 if (optind > 1) 756 argv[optind - 1] = argv[0]; 757 argv += optind - 1; 758 argc -= optind - 1; 759 760 if (argc < 2) { 761 uci_usage(); 762 return 0; 763 } 764 765 ret = uci_cmd(argc - 1, argv + 1); 766 if (input != stdin) 767 fclose(input); 768 769 if (ret == 255) 770 uci_usage(); 771 772 uci_free_context(ctx); 773 774 return ret; 775 } 776
This page was automatically generated by LXR 0.3.1. • OpenWrt