1 /* 2 * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <string.h> 18 #include <uci.h> 19 20 #include "ucode/module.h" 21 22 #define err_return(err) do { last_error = err; return NULL; } while(0) 23 24 static int last_error = 0; 25 static uc_resource_type_t *cursor_type; 26 27 enum pkg_cmd { 28 CMD_SAVE, 29 CMD_COMMIT, 30 CMD_REVERT 31 }; 32 33 static uc_value_t * 34 uc_uci_error(uc_vm_t *vm, size_t nargs) 35 { 36 char buf[sizeof("Unknown error: -9223372036854775808")]; 37 uc_value_t *errmsg; 38 39 const char *errstr[] = { 40 [UCI_ERR_MEM] = "Out of memory", 41 [UCI_ERR_INVAL] = "Invalid argument", 42 [UCI_ERR_NOTFOUND] = "Entry not found", 43 [UCI_ERR_IO] = "I/O error", 44 [UCI_ERR_PARSE] = "Parse error", 45 [UCI_ERR_DUPLICATE] = "Duplicate entry", 46 [UCI_ERR_UNKNOWN] = "Unknown error", 47 }; 48 49 if (last_error == 0) 50 return NULL; 51 52 if (last_error >= 0 && (unsigned)last_error < ARRAY_SIZE(errstr)) { 53 errmsg = ucv_string_new(errstr[last_error]); 54 } 55 else { 56 snprintf(buf, sizeof(buf), "Unknown error: %d", last_error); 57 errmsg = ucv_string_new(buf); 58 } 59 60 last_error = 0; 61 62 return errmsg; 63 } 64 65 66 static uc_value_t * 67 uc_uci_cursor(uc_vm_t *vm, size_t nargs) 68 { 69 uc_value_t *cdir = uc_fn_arg(0); 70 uc_value_t *sdir = uc_fn_arg(1); 71 struct uci_context *c; 72 int rv; 73 74 if ((cdir && ucv_type(cdir) != UC_STRING) || 75 (sdir && ucv_type(sdir) != UC_STRING)) 76 err_return(UCI_ERR_INVAL); 77 78 c = uci_alloc_context(); 79 80 if (!c) 81 err_return(UCI_ERR_MEM); 82 83 if (cdir) { 84 rv = uci_set_confdir(c, ucv_string_get(cdir)); 85 86 if (rv) 87 err_return(rv); 88 } 89 90 if (sdir) { 91 rv = uci_set_savedir(c, ucv_string_get(sdir)); 92 93 if (rv) 94 err_return(rv); 95 } 96 97 return uc_resource_new(cursor_type, c); 98 } 99 100 101 static uc_value_t * 102 uc_uci_load(uc_vm_t *vm, size_t nargs) 103 { 104 struct uci_context **c = uc_fn_this("uci.cursor"); 105 uc_value_t *conf = uc_fn_arg(0); 106 struct uci_element *e; 107 char *s; 108 109 if (!c || !*c) 110 err_return(UCI_ERR_INVAL); 111 112 if (ucv_type(conf) != UC_STRING) 113 err_return(UCI_ERR_INVAL); 114 115 s = ucv_string_get(conf); 116 117 uci_foreach_element(&(*c)->root, e) { 118 if (!strcmp(e->name, s)) { 119 uci_unload(*c, uci_to_package(e)); 120 break; 121 } 122 } 123 124 if (uci_load(*c, s, NULL)) 125 err_return((*c)->err); 126 127 return ucv_boolean_new(true); 128 } 129 130 static uc_value_t * 131 uc_uci_unload(uc_vm_t *vm, size_t nargs) 132 { 133 struct uci_context **c = uc_fn_this("uci.cursor"); 134 uc_value_t *conf = uc_fn_arg(0); 135 struct uci_element *e; 136 137 if (!c || !*c) 138 err_return(UCI_ERR_INVAL); 139 140 if (ucv_type(conf) != UC_STRING) 141 err_return(UCI_ERR_INVAL); 142 143 uci_foreach_element(&(*c)->root, e) { 144 if (!strcmp(e->name, ucv_string_get(conf))) { 145 uci_unload(*c, uci_to_package(e)); 146 147 return ucv_boolean_new(true); 148 } 149 } 150 151 return ucv_boolean_new(false); 152 } 153 154 static int 155 lookup_extended(struct uci_context *ctx, struct uci_ptr *ptr, bool extended) 156 { 157 int rv; 158 struct uci_ptr lookup; 159 160 /* use a copy of the passed ptr since failing lookups will 161 * clobber the state */ 162 lookup = *ptr; 163 lookup.flags |= UCI_LOOKUP_EXTENDED; 164 165 rv = uci_lookup_ptr(ctx, &lookup, NULL, extended); 166 167 /* copy to passed ptr on success */ 168 if (!rv) 169 *ptr = lookup; 170 171 return rv; 172 } 173 174 static int 175 lookup_ptr(struct uci_context *ctx, struct uci_ptr *ptr, bool extended) 176 { 177 if (ptr && !ptr->s && ptr->section && *ptr->section == '@') 178 return lookup_extended(ctx, ptr, extended); 179 180 return uci_lookup_ptr(ctx, ptr, NULL, extended); 181 } 182 183 static uc_value_t * 184 option_to_uval(uc_vm_t *vm, struct uci_option *o) 185 { 186 struct uci_element *e; 187 uc_value_t *arr; 188 189 switch (o->type) { 190 case UCI_TYPE_STRING: 191 return ucv_string_new(o->v.string); 192 193 case UCI_TYPE_LIST: 194 arr = ucv_array_new(vm); 195 196 if (arr) 197 uci_foreach_element(&o->v.list, e) 198 ucv_array_push(arr, ucv_string_new(e->name)); 199 200 return arr; 201 202 default: 203 return NULL; 204 } 205 } 206 207 static uc_value_t * 208 section_to_uval(uc_vm_t *vm, struct uci_section *s, int index) 209 { 210 uc_value_t *so = ucv_object_new(vm); 211 struct uci_element *e; 212 struct uci_option *o; 213 214 if (!so) 215 return NULL; 216 217 ucv_object_add(so, ".anonymous", ucv_boolean_new(s->anonymous)); 218 ucv_object_add(so, ".type", ucv_string_new(s->type)); 219 ucv_object_add(so, ".name", ucv_string_new(s->e.name)); 220 221 if (index >= 0) 222 ucv_object_add(so, ".index", ucv_int64_new(index)); 223 224 uci_foreach_element(&s->options, e) { 225 o = uci_to_option(e); 226 ucv_object_add(so, o->e.name, option_to_uval(vm, o)); 227 } 228 229 return so; 230 } 231 232 static uc_value_t * 233 package_to_uval(uc_vm_t *vm, struct uci_package *p) 234 { 235 uc_value_t *po = ucv_object_new(vm); 236 uc_value_t *so; 237 struct uci_element *e; 238 int i = 0; 239 240 if (!po) 241 return NULL; 242 243 uci_foreach_element(&p->sections, e) { 244 so = section_to_uval(vm, uci_to_section(e), i++); 245 ucv_object_add(po, e->name, so); 246 } 247 248 return po; 249 } 250 251 static uc_value_t * 252 uc_uci_get_any(uc_vm_t *vm, size_t nargs, bool all) 253 { 254 struct uci_context **c = uc_fn_this("uci.cursor"); 255 uc_value_t *conf = uc_fn_arg(0); 256 uc_value_t *sect = uc_fn_arg(1); 257 uc_value_t *opt = uc_fn_arg(2); 258 struct uci_ptr ptr = { 0 }; 259 int rv; 260 261 if (!c || !*c) 262 err_return(UCI_ERR_INVAL); 263 264 if ((ucv_type(conf) != UC_STRING) || 265 (sect && ucv_type(sect) != UC_STRING) || 266 (opt && ucv_type(opt) != UC_STRING)) 267 err_return(UCI_ERR_INVAL); 268 269 if ((!sect && !all) || (opt && all)) 270 err_return(UCI_ERR_INVAL); 271 272 ptr.package = ucv_string_get(conf); 273 ptr.section = sect ? ucv_string_get(sect) : NULL; 274 ptr.option = opt ? ucv_string_get(opt) : NULL; 275 276 rv = lookup_ptr(*c, &ptr, true); 277 278 if (rv != UCI_OK) 279 err_return(rv); 280 281 if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) 282 err_return(UCI_ERR_NOTFOUND); 283 284 if (all) { 285 if (ptr.section) { 286 if (!ptr.s) 287 err_return(UCI_ERR_NOTFOUND); 288 289 return section_to_uval(vm, ptr.s, -1); 290 } 291 292 if (!ptr.p) 293 err_return(UCI_ERR_NOTFOUND); 294 295 return package_to_uval(vm, ptr.p); 296 } 297 298 if (ptr.option) { 299 if (!ptr.o) 300 err_return(UCI_ERR_NOTFOUND); 301 302 return option_to_uval(vm, ptr.o); 303 } 304 305 if (!ptr.s) 306 err_return(UCI_ERR_NOTFOUND); 307 308 return ucv_string_new(ptr.s->type); 309 } 310 311 static uc_value_t * 312 uc_uci_get(uc_vm_t *vm, size_t nargs) 313 { 314 return uc_uci_get_any(vm, nargs, false); 315 } 316 317 static uc_value_t * 318 uc_uci_get_all(uc_vm_t *vm, size_t nargs) 319 { 320 return uc_uci_get_any(vm, nargs, true); 321 } 322 323 static uc_value_t * 324 uc_uci_get_first(uc_vm_t *vm, size_t nargs) 325 { 326 struct uci_context **c = uc_fn_this("uci.cursor"); 327 uc_value_t *conf = uc_fn_arg(0); 328 uc_value_t *type = uc_fn_arg(1); 329 uc_value_t *opt = uc_fn_arg(2); 330 struct uci_package *p = NULL; 331 struct uci_section *sc; 332 struct uci_element *e; 333 struct uci_ptr ptr = { 0 }; 334 int rv; 335 336 if (ucv_type(conf) != UC_STRING || 337 ucv_type(type) != UC_STRING || 338 (opt && ucv_type(opt) != UC_STRING)) 339 err_return(UCI_ERR_INVAL); 340 341 uci_foreach_element(&(*c)->root, e) { 342 if (strcmp(e->name, ucv_string_get(conf))) 343 continue; 344 345 p = uci_to_package(e); 346 break; 347 } 348 349 if (!p && uci_load(*c, ucv_string_get(conf), &p)) 350 err_return((*c)->err); 351 352 uci_foreach_element(&p->sections, e) { 353 sc = uci_to_section(e); 354 355 if (strcmp(sc->type, ucv_string_get(type))) 356 continue; 357 358 if (!opt) 359 return ucv_string_new(sc->e.name); 360 361 ptr.package = ucv_string_get(conf); 362 ptr.section = sc->e.name; 363 ptr.option = ucv_string_get(opt); 364 ptr.p = p; 365 ptr.s = sc; 366 367 rv = lookup_ptr(*c, &ptr, false); 368 369 if (rv != UCI_OK) 370 err_return(rv); 371 372 if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) 373 err_return(UCI_ERR_NOTFOUND); 374 375 return option_to_uval(vm, ptr.o); 376 } 377 378 err_return(UCI_ERR_NOTFOUND); 379 } 380 381 static uc_value_t * 382 uc_uci_add(uc_vm_t *vm, size_t nargs) 383 { 384 struct uci_context **c = uc_fn_this("uci.cursor"); 385 uc_value_t *conf = uc_fn_arg(0); 386 uc_value_t *type = uc_fn_arg(1); 387 struct uci_element *e = NULL; 388 struct uci_package *p = NULL; 389 struct uci_section *sc = NULL; 390 int rv; 391 392 if (ucv_type(conf) != UC_STRING || 393 ucv_type(type) != UC_STRING) 394 err_return(UCI_ERR_INVAL); 395 396 uci_foreach_element(&(*c)->root, e) { 397 if (!strcmp(e->name, ucv_string_get(conf))) { 398 p = uci_to_package(e); 399 break; 400 } 401 } 402 403 if (!p) 404 err_return(UCI_ERR_NOTFOUND); 405 406 rv = uci_add_section(*c, p, ucv_string_get(type), &sc); 407 408 if (rv != UCI_OK) 409 err_return(rv); 410 else if (!sc) 411 err_return(UCI_ERR_NOTFOUND); 412 413 return ucv_string_new(sc->e.name); 414 } 415 416 static bool 417 uval_to_uci(uc_vm_t *vm, uc_value_t *val, const char **p, bool *is_list) 418 { 419 uc_value_t *item; 420 421 *p = NULL; 422 423 if (is_list) 424 *is_list = false; 425 426 switch (ucv_type(val)) { 427 case UC_ARRAY: 428 if (ucv_array_length(val) == 0) 429 return false; 430 431 item = ucv_array_get(val, 0); 432 433 /* don't recurse */ 434 if (ucv_type(item) == UC_ARRAY) 435 return false; 436 437 if (is_list) 438 *is_list = true; 439 440 return uval_to_uci(vm, item, p, NULL); 441 442 case UC_BOOLEAN: 443 *p = xstrdup(ucv_boolean_get(val) ? "1" : ""); 444 445 return true; 446 447 case UC_DOUBLE: 448 case UC_INTEGER: 449 case UC_STRING: 450 *p = ucv_to_string(vm, val); 451 /* fall through */ 452 453 case UC_NULL: 454 return true; 455 456 default: 457 return false; 458 } 459 } 460 461 static uc_value_t * 462 uc_uci_set(uc_vm_t *vm, size_t nargs) 463 { 464 struct uci_context **c = uc_fn_this("uci.cursor"); 465 uc_value_t *conf = uc_fn_arg(0); 466 uc_value_t *sect = uc_fn_arg(1); 467 uc_value_t *opt = NULL, *val = NULL; 468 struct uci_ptr ptr = { 0 }; 469 bool is_list = false; 470 size_t i; 471 int rv; 472 473 if (ucv_type(conf) != UC_STRING || 474 ucv_type(sect) != UC_STRING) 475 err_return(UCI_ERR_INVAL); 476 477 switch (nargs) { 478 /* conf, sect, opt, val */ 479 case 4: 480 opt = uc_fn_arg(2); 481 val = uc_fn_arg(3); 482 483 if (ucv_type(opt) != UC_STRING) 484 err_return(UCI_ERR_INVAL); 485 486 break; 487 488 /* conf, sect, type */ 489 case 3: 490 val = uc_fn_arg(2); 491 492 if (ucv_type(val) != UC_STRING) 493 err_return(UCI_ERR_INVAL); 494 495 break; 496 497 default: 498 err_return(UCI_ERR_INVAL); 499 } 500 501 ptr.package = ucv_string_get(conf); 502 ptr.section = ucv_string_get(sect); 503 ptr.option = opt ? ucv_string_get(opt) : NULL; 504 505 rv = lookup_ptr(*c, &ptr, true); 506 507 if (rv != UCI_OK) 508 err_return(rv); 509 510 if (!ptr.s && ptr.option) 511 err_return(UCI_ERR_NOTFOUND); 512 513 if (!uval_to_uci(vm, val, &ptr.value, &is_list)) 514 err_return(UCI_ERR_INVAL); 515 516 if (is_list) { 517 /* if we got a one-element array, delete existing option (if any) 518 * and iterate array at offset 0 */ 519 if (ucv_array_length(val) == 1) { 520 i = 0; 521 522 free((char *)ptr.value); 523 ptr.value = NULL; 524 525 if (ptr.o) { 526 rv = uci_delete(*c, &ptr); 527 528 if (rv != UCI_OK) 529 err_return(rv); 530 } 531 } 532 /* if we get a multi element array, overwrite existing option (if any) 533 * with first value and iterate remaining array at offset 1 */ 534 else { 535 i = 1; 536 537 rv = uci_set(*c, &ptr); 538 free((char *)ptr.value); 539 540 if (rv != UCI_OK) 541 err_return(rv); 542 } 543 544 for (; i < ucv_array_length(val); i++) { 545 if (!uval_to_uci(vm, ucv_array_get(val, i), &ptr.value, NULL)) 546 continue; 547 548 rv = uci_add_list(*c, &ptr); 549 free((char *)ptr.value); 550 551 if (rv != UCI_OK) 552 err_return(rv); 553 } 554 } 555 else { 556 rv = uci_set(*c, &ptr); 557 free((char *)ptr.value); 558 559 if (rv != UCI_OK) 560 err_return(rv); 561 } 562 563 return ucv_boolean_new(true); 564 } 565 566 static uc_value_t * 567 uc_uci_delete(uc_vm_t *vm, size_t nargs) 568 { 569 struct uci_context **c = uc_fn_this("uci.cursor"); 570 uc_value_t *conf = uc_fn_arg(0); 571 uc_value_t *sect = uc_fn_arg(1); 572 uc_value_t *opt = uc_fn_arg(2); 573 struct uci_ptr ptr = { 0 }; 574 int rv; 575 576 if (ucv_type(conf) != UC_STRING || 577 ucv_type(sect) != UC_STRING || 578 (opt && ucv_type(opt) != UC_STRING)) 579 err_return(UCI_ERR_INVAL); 580 581 ptr.package = ucv_string_get(conf); 582 ptr.section = ucv_string_get(sect); 583 ptr.option = opt ? ucv_string_get(opt) : NULL; 584 585 rv = lookup_ptr(*c, &ptr, true); 586 587 if (rv != UCI_OK) 588 err_return(rv); 589 590 if (opt ? !ptr.o : !ptr.s) 591 err_return(UCI_ERR_NOTFOUND); 592 593 rv = uci_delete(*c, &ptr); 594 595 if (rv != UCI_OK) 596 err_return(rv); 597 598 return ucv_boolean_new(true); 599 } 600 601 static uc_value_t * 602 uc_uci_rename(uc_vm_t *vm, size_t nargs) 603 { 604 struct uci_context **c = uc_fn_this("uci.cursor"); 605 uc_value_t *conf = uc_fn_arg(0); 606 uc_value_t *sect = uc_fn_arg(1); 607 uc_value_t *opt = NULL, *val = NULL; 608 struct uci_ptr ptr = { 0 }; 609 int rv; 610 611 if (ucv_type(conf) != UC_STRING || 612 ucv_type(sect) != UC_STRING) 613 err_return(UCI_ERR_INVAL); 614 615 switch (nargs) { 616 /* conf, sect, opt, val */ 617 case 4: 618 opt = uc_fn_arg(2); 619 val = uc_fn_arg(3); 620 621 if (ucv_type(opt) != UC_STRING || 622 ucv_type(val) != UC_STRING) 623 err_return(UCI_ERR_INVAL); 624 625 break; 626 627 /* conf, sect, type */ 628 case 3: 629 val = uc_fn_arg(2); 630 631 if (ucv_type(val) != UC_STRING) 632 err_return(UCI_ERR_INVAL); 633 634 break; 635 636 default: 637 err_return(UCI_ERR_INVAL); 638 } 639 640 ptr.package = ucv_string_get(conf); 641 ptr.section = ucv_string_get(sect); 642 ptr.option = opt ? ucv_string_get(opt) : NULL; 643 ptr.value = ucv_string_get(val); 644 645 rv = lookup_ptr(*c, &ptr, true); 646 647 if (rv != UCI_OK) 648 err_return(rv); 649 650 if (!ptr.s && ptr.option) 651 err_return(UCI_ERR_NOTFOUND); 652 653 rv = uci_rename(*c, &ptr); 654 655 if (rv != UCI_OK) 656 err_return(rv); 657 658 return ucv_boolean_new(true); 659 } 660 661 static uc_value_t * 662 uc_uci_reorder(uc_vm_t *vm, size_t nargs) 663 { 664 struct uci_context **c = uc_fn_this("uci.cursor"); 665 uc_value_t *conf = uc_fn_arg(0); 666 uc_value_t *sect = uc_fn_arg(1); 667 uc_value_t *val = uc_fn_arg(2); 668 struct uci_ptr ptr = { 0 }; 669 int64_t n; 670 int rv; 671 672 if (ucv_type(conf) != UC_STRING || 673 ucv_type(sect) != UC_STRING || 674 ucv_type(val) != UC_INTEGER) 675 err_return(UCI_ERR_INVAL); 676 677 n = ucv_int64_get(val); 678 679 if (n < 0) 680 err_return(UCI_ERR_INVAL); 681 682 ptr.package = ucv_string_get(conf); 683 ptr.section = ucv_string_get(sect); 684 685 rv = lookup_ptr(*c, &ptr, true); 686 687 if (rv != UCI_OK) 688 err_return(rv); 689 690 if (!ptr.s) 691 err_return(UCI_ERR_NOTFOUND); 692 693 rv = uci_reorder_section(*c, ptr.s, n); 694 695 if (rv != UCI_OK) 696 err_return(rv); 697 698 return ucv_boolean_new(true); 699 } 700 701 static int 702 uc_uci_pkg_command_single(struct uci_context *ctx, enum pkg_cmd cmd, 703 struct uci_package *pkg) 704 { 705 struct uci_ptr ptr = { 0 }; 706 707 switch (cmd) { 708 case CMD_COMMIT: 709 return uci_commit(ctx, &pkg, false); 710 711 case CMD_SAVE: 712 return uci_save(ctx, pkg); 713 714 case CMD_REVERT: 715 ptr.p = pkg; 716 717 return uci_revert(ctx, &ptr); 718 719 default: 720 return UCI_ERR_INVAL; 721 } 722 } 723 724 static uc_value_t * 725 uc_uci_pkg_command(uc_vm_t *vm, size_t nargs, enum pkg_cmd cmd) 726 { 727 struct uci_context **c = uc_fn_this("uci.cursor"); 728 uc_value_t *conf = uc_fn_arg(0); 729 struct uci_package *p; 730 char **configs = NULL; 731 int rv, res = UCI_OK; 732 size_t i; 733 734 if (conf) { 735 if (ucv_type(conf) != UC_STRING) 736 err_return(UCI_ERR_INVAL); 737 738 if (!(p = uci_lookup_package(*c, ucv_string_get(conf)))) 739 err_return(UCI_ERR_NOTFOUND); 740 741 res = uc_uci_pkg_command_single(*c, cmd, p); 742 } 743 else { 744 if (uci_list_configs(*c, &configs)) 745 err_return((*c)->err); 746 747 if (!configs || !configs[0]) 748 err_return(UCI_ERR_NOTFOUND); 749 750 for (i = 0; configs[i]; i++) { 751 if (!(p = uci_lookup_package(*c, configs[i]))) 752 continue; 753 754 rv = uc_uci_pkg_command_single(*c, cmd, p); 755 756 if (rv != UCI_OK) 757 res = rv; 758 } 759 760 free(configs); 761 } 762 763 if (res != UCI_OK) 764 err_return(res); 765 766 return ucv_boolean_new(true); 767 } 768 769 static uc_value_t * 770 uc_uci_save(uc_vm_t *vm, size_t nargs) 771 { 772 return uc_uci_pkg_command(vm, nargs, CMD_SAVE); 773 } 774 775 static uc_value_t * 776 uc_uci_commit(uc_vm_t *vm, size_t nargs) 777 { 778 return uc_uci_pkg_command(vm, nargs, CMD_COMMIT); 779 } 780 781 static uc_value_t * 782 uc_uci_revert(uc_vm_t *vm, size_t nargs) 783 { 784 return uc_uci_pkg_command(vm, nargs, CMD_REVERT); 785 } 786 787 static uc_value_t * 788 change_to_uval(uc_vm_t *vm, struct uci_delta *d) 789 { 790 const char *types[] = { 791 [UCI_CMD_REORDER] = "order", 792 [UCI_CMD_REMOVE] = "remove", 793 [UCI_CMD_RENAME] = "rename", 794 [UCI_CMD_ADD] = "add", 795 [UCI_CMD_LIST_ADD] = "list-add", 796 [UCI_CMD_LIST_DEL] = "list-del", 797 [UCI_CMD_CHANGE] = "set", 798 }; 799 800 uc_value_t *a; 801 802 if (!d->section) 803 return NULL; 804 805 a = ucv_array_new(vm); 806 807 if (!a) 808 return NULL; 809 810 ucv_array_push(a, ucv_string_new(types[d->cmd])); 811 ucv_array_push(a, ucv_string_new(d->section)); 812 813 if (d->e.name) 814 ucv_array_push(a, ucv_string_new(d->e.name)); 815 816 if (d->value) { 817 if (d->cmd == UCI_CMD_REORDER) 818 ucv_array_push(a, ucv_int64_new(strtoul(d->value, NULL, 10))); 819 else 820 ucv_array_push(a, ucv_string_new(d->value)); 821 } 822 823 return a; 824 } 825 826 static uc_value_t * 827 changes_to_uval(uc_vm_t *vm, struct uci_context *ctx, const char *package) 828 { 829 uc_value_t *a = NULL, *c; 830 struct uci_package *p = NULL; 831 struct uci_element *e; 832 bool unload = false; 833 834 uci_foreach_element(&ctx->root, e) { 835 if (strcmp(e->name, package)) 836 continue; 837 838 p = uci_to_package(e); 839 } 840 841 if (!p) { 842 unload = true; 843 uci_load(ctx, package, &p); 844 } 845 846 if (!p) 847 return NULL; 848 849 if (!uci_list_empty(&p->delta) || !uci_list_empty(&p->saved_delta)) { 850 a = ucv_array_new(vm); 851 852 if (!a) 853 err_return(UCI_ERR_MEM); 854 855 uci_foreach_element(&p->saved_delta, e) { 856 c = change_to_uval(vm, uci_to_delta(e)); 857 858 if (c) 859 ucv_array_push(a, c); 860 } 861 862 uci_foreach_element(&p->delta, e) { 863 c = change_to_uval(vm, uci_to_delta(e)); 864 865 if (c) 866 ucv_array_push(a, c); 867 } 868 } 869 870 if (unload) 871 uci_unload(ctx, p); 872 873 return a; 874 } 875 876 static uc_value_t * 877 uc_uci_changes(uc_vm_t *vm, size_t nargs) 878 { 879 struct uci_context **c = uc_fn_this("uci.cursor"); 880 uc_value_t *conf = uc_fn_arg(0); 881 uc_value_t *res, *chg; 882 char **configs; 883 int rv, i; 884 885 if (conf && ucv_type(conf) != UC_STRING) 886 err_return(UCI_ERR_INVAL); 887 888 rv = uci_list_configs(*c, &configs); 889 890 if (rv != UCI_OK) 891 err_return(rv); 892 893 res = ucv_object_new(vm); 894 895 for (i = 0; configs[i]; i++) { 896 if (conf && strcmp(configs[i], ucv_string_get(conf))) 897 continue; 898 899 chg = changes_to_uval(vm, *c, configs[i]); 900 901 if (chg) 902 ucv_object_add(res, configs[i], chg); 903 } 904 905 free(configs); 906 907 return res; 908 } 909 910 static uc_value_t * 911 uc_uci_foreach(uc_vm_t *vm, size_t nargs) 912 { 913 struct uci_context **c = uc_fn_this("uci.cursor"); 914 uc_value_t *conf = uc_fn_arg(0); 915 uc_value_t *type = uc_fn_arg(1); 916 uc_value_t *func = uc_fn_arg(2); 917 uc_value_t *rv = NULL; 918 struct uci_package *p = NULL; 919 struct uci_element *e, *tmp; 920 struct uci_section *sc; 921 uc_exception_type_t ex; 922 bool stop = false; 923 bool ret = false; 924 int i = 0; 925 926 if (ucv_type(conf) != UC_STRING || 927 (type && ucv_type(type) != UC_STRING)) 928 err_return(UCI_ERR_INVAL); 929 930 uci_foreach_element(&(*c)->root, e) { 931 if (strcmp(e->name, ucv_string_get(conf))) 932 continue; 933 934 p = uci_to_package(e); 935 break; 936 } 937 938 if (!p && uci_load(*c, ucv_string_get(conf), &p)) 939 err_return((*c)->err); 940 941 uci_foreach_element_safe(&p->sections, tmp, e) { 942 sc = uci_to_section(e); 943 i++; 944 945 if (type && strcmp(sc->type, ucv_string_get(type))) 946 continue; 947 948 uc_value_push(ucv_get(func)); 949 uc_value_push(section_to_uval(vm, sc, i - 1)); 950 951 ex = uc_call(1); 952 953 /* stop on exception in callback */ 954 if (ex) 955 break; 956 957 ret = true; 958 rv = uc_value_pop(); 959 stop = (ucv_type(rv) == UC_BOOLEAN && !ucv_boolean_get(rv)); 960 961 ucv_put(rv); 962 963 if (stop) 964 break; 965 } 966 967 return ucv_boolean_new(ret); 968 } 969 970 static uc_value_t * 971 uc_uci_configs(uc_vm_t *vm, size_t nargs) 972 { 973 struct uci_context **c = uc_fn_this("uci.cursor"); 974 uc_value_t *a; 975 char **configs; 976 int i, rv; 977 978 rv = uci_list_configs(*c, &configs); 979 980 if (rv != UCI_OK) 981 err_return(rv); 982 983 a = ucv_array_new(vm); 984 985 for (i = 0; configs[i]; i++) 986 ucv_array_push(a, ucv_string_new(configs[i])); 987 988 free(configs); 989 990 return a; 991 } 992 993 994 static const uc_function_list_t cursor_fns[] = { 995 { "load", uc_uci_load }, 996 { "unload", uc_uci_unload }, 997 { "get", uc_uci_get }, 998 { "get_all", uc_uci_get_all }, 999 { "get_first", uc_uci_get_first }, 1000 { "add", uc_uci_add }, 1001 { "set", uc_uci_set }, 1002 { "rename", uc_uci_rename }, 1003 { "save", uc_uci_save }, 1004 { "delete", uc_uci_delete }, 1005 { "commit", uc_uci_commit }, 1006 { "revert", uc_uci_revert }, 1007 { "reorder", uc_uci_reorder }, 1008 { "changes", uc_uci_changes }, 1009 { "foreach", uc_uci_foreach }, 1010 { "configs", uc_uci_configs }, 1011 { "error", uc_uci_error }, 1012 }; 1013 1014 static const uc_function_list_t global_fns[] = { 1015 { "error", uc_uci_error }, 1016 { "cursor", uc_uci_cursor }, 1017 }; 1018 1019 1020 static void close_uci(void *ud) { 1021 uci_free_context((struct uci_context *)ud); 1022 } 1023 1024 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 1025 { 1026 uc_function_list_register(scope, global_fns); 1027 1028 cursor_type = uc_type_declare(vm, "uci.cursor", cursor_fns, close_uci); 1029 } 1030
This page was automatically generated by LXR 0.3.1. • OpenWrt