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 uc_value_t * 702 uc_uci_pkg_command(uc_vm_t *vm, size_t nargs, enum pkg_cmd cmd) 703 { 704 struct uci_context **c = uc_fn_this("uci.cursor"); 705 uc_value_t *conf = uc_fn_arg(0); 706 struct uci_element *e, *tmp; 707 struct uci_package *p; 708 struct uci_ptr ptr = { 0 }; 709 int rv, res = UCI_OK; 710 711 if (cmd != CMD_REVERT && conf) 712 err_return(UCI_ERR_INVAL); 713 714 if (conf && ucv_type(conf) != UC_STRING) 715 err_return(UCI_ERR_INVAL); 716 717 uci_foreach_element_safe(&(*c)->root, tmp, e) { 718 p = uci_to_package(e); 719 720 if (conf && strcmp(e->name, ucv_string_get(conf))) 721 continue; 722 723 switch (cmd) { 724 case CMD_COMMIT: 725 rv = uci_commit(*c, &p, false); 726 break; 727 728 case CMD_SAVE: 729 rv = uci_save(*c, p); 730 break; 731 732 case CMD_REVERT: 733 ptr.p = p; 734 rv = uci_revert(*c, &ptr); 735 break; 736 737 default: 738 rv = UCI_ERR_INVAL; 739 } 740 741 if (rv != UCI_OK) 742 res = rv; 743 } 744 745 if (res != UCI_OK) 746 err_return(res); 747 748 return ucv_boolean_new(true); 749 } 750 751 static uc_value_t * 752 uc_uci_save(uc_vm_t *vm, size_t nargs) 753 { 754 return uc_uci_pkg_command(vm, nargs, CMD_SAVE); 755 } 756 757 static uc_value_t * 758 uc_uci_commit(uc_vm_t *vm, size_t nargs) 759 { 760 return uc_uci_pkg_command(vm, nargs, CMD_COMMIT); 761 } 762 763 static uc_value_t * 764 uc_uci_revert(uc_vm_t *vm, size_t nargs) 765 { 766 return uc_uci_pkg_command(vm, nargs, CMD_REVERT); 767 } 768 769 static uc_value_t * 770 change_to_uval(uc_vm_t *vm, struct uci_delta *d) 771 { 772 const char *types[] = { 773 [UCI_CMD_REORDER] = "order", 774 [UCI_CMD_REMOVE] = "remove", 775 [UCI_CMD_RENAME] = "rename", 776 [UCI_CMD_ADD] = "add", 777 [UCI_CMD_LIST_ADD] = "list-add", 778 [UCI_CMD_LIST_DEL] = "list-del", 779 [UCI_CMD_CHANGE] = "set", 780 }; 781 782 uc_value_t *a; 783 784 if (!d->section) 785 return NULL; 786 787 a = ucv_array_new(vm); 788 789 if (!a) 790 return NULL; 791 792 ucv_array_push(a, ucv_string_new(types[d->cmd])); 793 ucv_array_push(a, ucv_string_new(d->section)); 794 795 if (d->e.name) 796 ucv_array_push(a, ucv_string_new(d->e.name)); 797 798 if (d->value) { 799 if (d->cmd == UCI_CMD_REORDER) 800 ucv_array_push(a, ucv_int64_new(strtoul(d->value, NULL, 10))); 801 else 802 ucv_array_push(a, ucv_string_new(d->value)); 803 } 804 805 return a; 806 } 807 808 static uc_value_t * 809 changes_to_uval(uc_vm_t *vm, struct uci_context *ctx, const char *package) 810 { 811 uc_value_t *a = NULL, *c; 812 struct uci_package *p = NULL; 813 struct uci_element *e; 814 bool unload = false; 815 816 uci_foreach_element(&ctx->root, e) { 817 if (strcmp(e->name, package)) 818 continue; 819 820 p = uci_to_package(e); 821 } 822 823 if (!p) { 824 unload = true; 825 uci_load(ctx, package, &p); 826 } 827 828 if (!p) 829 return NULL; 830 831 if (!uci_list_empty(&p->delta) || !uci_list_empty(&p->saved_delta)) { 832 a = ucv_array_new(vm); 833 834 if (!a) 835 err_return(UCI_ERR_MEM); 836 837 uci_foreach_element(&p->saved_delta, e) { 838 c = change_to_uval(vm, uci_to_delta(e)); 839 840 if (c) 841 ucv_array_push(a, c); 842 } 843 844 uci_foreach_element(&p->delta, e) { 845 c = change_to_uval(vm, uci_to_delta(e)); 846 847 if (c) 848 ucv_array_push(a, c); 849 } 850 } 851 852 if (unload) 853 uci_unload(ctx, p); 854 855 return a; 856 } 857 858 static uc_value_t * 859 uc_uci_changes(uc_vm_t *vm, size_t nargs) 860 { 861 struct uci_context **c = uc_fn_this("uci.cursor"); 862 uc_value_t *conf = uc_fn_arg(0); 863 uc_value_t *res, *chg; 864 char **configs; 865 int rv, i; 866 867 if (conf && ucv_type(conf) != UC_STRING) 868 err_return(UCI_ERR_INVAL); 869 870 rv = uci_list_configs(*c, &configs); 871 872 if (rv != UCI_OK) 873 err_return(rv); 874 875 res = ucv_object_new(vm); 876 877 for (i = 0; configs[i]; i++) { 878 if (conf && strcmp(configs[i], ucv_string_get(conf))) 879 continue; 880 881 chg = changes_to_uval(vm, *c, configs[i]); 882 883 if (chg) 884 ucv_object_add(res, configs[i], chg); 885 } 886 887 free(configs); 888 889 return res; 890 } 891 892 static uc_value_t * 893 uc_uci_foreach(uc_vm_t *vm, size_t nargs) 894 { 895 struct uci_context **c = uc_fn_this("uci.cursor"); 896 uc_value_t *conf = uc_fn_arg(0); 897 uc_value_t *type = uc_fn_arg(1); 898 uc_value_t *func = uc_fn_arg(2); 899 uc_value_t *rv = NULL; 900 struct uci_package *p = NULL; 901 struct uci_element *e, *tmp; 902 struct uci_section *sc; 903 uc_exception_type_t ex; 904 bool stop = false; 905 bool ret = false; 906 int i = 0; 907 908 if (ucv_type(conf) != UC_STRING || 909 (type && ucv_type(type) != UC_STRING)) 910 err_return(UCI_ERR_INVAL); 911 912 uci_foreach_element(&(*c)->root, e) { 913 if (strcmp(e->name, ucv_string_get(conf))) 914 continue; 915 916 p = uci_to_package(e); 917 break; 918 } 919 920 if (!p && uci_load(*c, ucv_string_get(conf), &p)) 921 err_return((*c)->err); 922 923 uci_foreach_element_safe(&p->sections, tmp, e) { 924 sc = uci_to_section(e); 925 i++; 926 927 if (type && strcmp(sc->type, ucv_string_get(type))) 928 continue; 929 930 uc_value_push(ucv_get(func)); 931 uc_value_push(section_to_uval(vm, sc, i - 1)); 932 933 ex = uc_call(1); 934 935 /* stop on exception in callback */ 936 if (ex) 937 break; 938 939 ret = true; 940 rv = uc_value_pop(); 941 stop = (ucv_type(rv) == UC_BOOLEAN && !ucv_boolean_get(rv)); 942 943 ucv_put(rv); 944 945 if (stop) 946 break; 947 } 948 949 return ucv_boolean_new(ret); 950 } 951 952 static uc_value_t * 953 uc_uci_configs(uc_vm_t *vm, size_t nargs) 954 { 955 struct uci_context **c = uc_fn_this("uci.cursor"); 956 uc_value_t *a; 957 char **configs; 958 int i, rv; 959 960 rv = uci_list_configs(*c, &configs); 961 962 if (rv != UCI_OK) 963 err_return(rv); 964 965 a = ucv_array_new(vm); 966 967 for (i = 0; configs[i]; i++) 968 ucv_array_push(a, ucv_string_new(configs[i])); 969 970 free(configs); 971 972 return a; 973 } 974 975 976 static const uc_function_list_t cursor_fns[] = { 977 { "load", uc_uci_load }, 978 { "unload", uc_uci_unload }, 979 { "get", uc_uci_get }, 980 { "get_all", uc_uci_get_all }, 981 { "get_first", uc_uci_get_first }, 982 { "add", uc_uci_add }, 983 { "set", uc_uci_set }, 984 { "rename", uc_uci_rename }, 985 { "save", uc_uci_save }, 986 { "delete", uc_uci_delete }, 987 { "commit", uc_uci_commit }, 988 { "revert", uc_uci_revert }, 989 { "reorder", uc_uci_reorder }, 990 { "changes", uc_uci_changes }, 991 { "foreach", uc_uci_foreach }, 992 { "configs", uc_uci_configs }, 993 { "error", uc_uci_error }, 994 }; 995 996 static const uc_function_list_t global_fns[] = { 997 { "error", uc_uci_error }, 998 { "cursor", uc_uci_cursor }, 999 }; 1000 1001 1002 static void close_uci(void *ud) { 1003 uci_free_context((struct uci_context *)ud); 1004 } 1005 1006 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 1007 { 1008 uc_function_list_register(scope, global_fns); 1009 1010 cursor_type = uc_type_declare(vm, "uci.cursor", cursor_fns, close_uci); 1011 } 1012
This page was automatically generated by LXR 0.3.1. • OpenWrt