1 /* 2 * rpcd - UBUS RPC server - ucode plugin 3 * 4 * Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/stat.h> 21 #include <dirent.h> 22 #include <limits.h> 23 24 #include <libubox/blobmsg.h> 25 #include <libubox/blobmsg_json.h> 26 27 #include <libubus.h> 28 29 #include <ucode/compiler.h> 30 #include <ucode/lib.h> 31 #include <ucode/vm.h> 32 33 #include <rpcd/plugin.h> 34 35 #define RPC_UCSCRIPT_DIRECTORY INSTALL_PREFIX "/share/rpcd/ucode" 36 37 static struct blob_buf buf; 38 static int request_timeout; 39 40 /* 41 * Track script instances and registered ubus objects in these lists. 42 * 43 * This is primarily done to make Valgrind happy and to mark the 44 * related memory as reachable. Since we don't have a teardown 45 * mechanism in rpcd plugins we can't orderly free the related 46 * ubus object and ucode VM memory anyway. 47 */ 48 static LIST_HEAD(scripts); 49 static LIST_HEAD(uuobjs); 50 51 typedef struct { 52 struct list_head list; 53 uc_vm_t vm; 54 uc_resource_type_t *requesttype; 55 uc_value_t *pending_replies; 56 char *path; 57 } rpc_ucode_script_t; 58 59 typedef struct { 60 struct list_head list; 61 rpc_ucode_script_t *script; 62 uc_value_t *signature; 63 struct ubus_object ubusobj; 64 } rpc_ucode_ubus_obj_t; 65 66 typedef struct { 67 struct ubus_context *ubus; 68 struct ubus_request_data req; 69 struct uloop_timeout timeout; 70 rpc_ucode_script_t *script; 71 uc_value_t *func; 72 uc_value_t *args; 73 uc_value_t *info; 74 bool replied; 75 } rpc_ucode_call_ctx_t; 76 77 static uc_parse_config_t config = { 78 .strict_declarations = false, 79 .lstrip_blocks = true, 80 .trim_blocks = true 81 }; 82 83 84 static rpc_ucode_script_t * 85 rpc_ucode_obj_to_script(struct ubus_object *obj) 86 { 87 rpc_ucode_ubus_obj_t *uo = container_of(obj, rpc_ucode_ubus_obj_t, ubusobj); 88 89 return uo->script; 90 } 91 92 static uc_value_t * 93 rpc_ucode_obj_to_signature(struct ubus_object *obj) 94 { 95 rpc_ucode_ubus_obj_t *uo = container_of(obj, rpc_ucode_ubus_obj_t, ubusobj); 96 97 return uo->signature; 98 } 99 100 static void 101 rpc_ucode_ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob); 102 103 static void 104 rpc_ucode_ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob); 105 106 static void 107 rpc_ucode_ucv_to_blob(const char *name, uc_value_t *val, struct blob_buf *blob) 108 { 109 int64_t n; 110 void *c; 111 112 switch (ucv_type(val)) { 113 case UC_NULL: 114 blobmsg_add_field(blob, BLOBMSG_TYPE_UNSPEC, name, NULL, 0); 115 break; 116 117 case UC_BOOLEAN: 118 blobmsg_add_u8(blob, name, ucv_boolean_get(val)); 119 break; 120 121 case UC_INTEGER: 122 n = ucv_int64_get(val); 123 124 if (errno == ERANGE) 125 blobmsg_add_u64(blob, name, ucv_uint64_get(val)); 126 else if (n >= INT32_MIN && n <= INT32_MAX) 127 blobmsg_add_u32(blob, name, n); 128 else 129 blobmsg_add_u64(blob, name, n); 130 131 break; 132 133 case UC_DOUBLE: 134 blobmsg_add_double(blob, name, ucv_double_get(val)); 135 break; 136 137 case UC_STRING: 138 blobmsg_add_string(blob, name, ucv_string_get(val)); 139 break; 140 141 case UC_ARRAY: 142 c = blobmsg_open_array(blob, name); 143 rpc_ucode_ucv_array_to_blob(val, blob); 144 blobmsg_close_array(blob, c); 145 break; 146 147 case UC_OBJECT: 148 c = blobmsg_open_table(blob, name); 149 rpc_ucode_ucv_object_to_blob(val, blob); 150 blobmsg_close_table(blob, c); 151 break; 152 153 default: 154 break; 155 } 156 } 157 158 static void 159 rpc_ucode_ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob) 160 { 161 size_t i; 162 163 for (i = 0; i < ucv_array_length(val); i++) 164 rpc_ucode_ucv_to_blob(NULL, ucv_array_get(val, i), blob); 165 } 166 167 static void 168 rpc_ucode_ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob) 169 { 170 ucv_object_foreach(val, k, v) 171 rpc_ucode_ucv_to_blob(k, v, blob); 172 } 173 174 static uc_value_t * 175 rpc_ucode_blob_to_ucv(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name); 176 177 static uc_value_t * 178 rpc_ucode_blob_array_to_ucv(uc_vm_t *vm, struct blob_attr *attr, size_t len, bool table) 179 { 180 uc_value_t *o = table ? ucv_object_new(vm) : ucv_array_new(vm); 181 uc_value_t *v; 182 struct blob_attr *pos; 183 size_t rem = len; 184 const char *name; 185 186 if (!o) 187 return NULL; 188 189 __blob_for_each_attr(pos, attr, rem) { 190 name = NULL; 191 v = rpc_ucode_blob_to_ucv(vm, pos, table, &name); 192 193 if (table && name) 194 ucv_object_add(o, name, v); 195 else if (!table) 196 ucv_array_push(o, v); 197 else 198 ucv_put(v); 199 } 200 201 return o; 202 } 203 204 static uc_value_t * 205 rpc_ucode_blob_to_ucv(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name) 206 { 207 void *data; 208 int len; 209 210 if (!blobmsg_check_attr(attr, false)) 211 return NULL; 212 213 if (table && blobmsg_name(attr)[0]) 214 *name = blobmsg_name(attr); 215 216 data = blobmsg_data(attr); 217 len = blobmsg_data_len(attr); 218 219 switch (blob_id(attr)) { 220 case BLOBMSG_TYPE_BOOL: 221 return ucv_boolean_new(*(uint8_t *)data); 222 223 case BLOBMSG_TYPE_INT16: 224 return ucv_int64_new((int16_t)be16_to_cpu(*(uint16_t *)data)); 225 226 case BLOBMSG_TYPE_INT32: 227 return ucv_int64_new((int32_t)be32_to_cpu(*(uint32_t *)data)); 228 229 case BLOBMSG_TYPE_INT64: 230 return ucv_int64_new((int64_t)be64_to_cpu(*(uint64_t *)data)); 231 232 case BLOBMSG_TYPE_DOUBLE: 233 ; 234 union { 235 double d; 236 uint64_t u64; 237 } v; 238 239 v.u64 = be64_to_cpu(*(uint64_t *)data); 240 241 return ucv_double_new(v.d); 242 243 case BLOBMSG_TYPE_STRING: 244 return ucv_string_new(data); 245 246 case BLOBMSG_TYPE_ARRAY: 247 return rpc_ucode_blob_array_to_ucv(vm, data, len, false); 248 249 case BLOBMSG_TYPE_TABLE: 250 return rpc_ucode_blob_array_to_ucv(vm, data, len, true); 251 252 default: 253 return NULL; 254 } 255 } 256 257 static int 258 rpc_ucode_validate_call_args(struct ubus_object *obj, const char *ubus_method_name, struct blob_attr *msg, uc_value_t **res) 259 { 260 rpc_ucode_script_t *script = rpc_ucode_obj_to_script(obj); 261 const struct ubus_method *method = NULL; 262 const struct blobmsg_hdr *hdr; 263 struct blob_attr *attr; 264 bool found; 265 size_t i; 266 int len; 267 268 for (i = 0; i < obj->n_methods; i++) { 269 if (!strcmp(obj->methods[i].name, ubus_method_name)) { 270 method = &obj->methods[i]; 271 break; 272 } 273 } 274 275 if (!method) 276 return UBUS_STATUS_METHOD_NOT_FOUND; 277 278 len = blob_len(msg); 279 280 __blob_for_each_attr(attr, blob_data(msg), len) { 281 if (!blobmsg_check_attr_len(attr, false, len)) 282 return UBUS_STATUS_INVALID_ARGUMENT; 283 284 if (!blob_is_extended(attr)) 285 return UBUS_STATUS_INVALID_ARGUMENT; 286 287 hdr = blob_data(attr); 288 found = false; 289 290 for (i = 0; i < method->n_policy; i++) { 291 if (blobmsg_namelen(hdr) != strlen(method->policy[i].name)) 292 continue; 293 294 if (strcmp(method->policy[i].name, (char *)hdr->name)) 295 continue; 296 297 /* named argument found but wrong type */ 298 if (blob_id(attr) != method->policy[i].type) 299 goto inval; 300 301 found = true; 302 break; 303 } 304 305 /* named argument not found in policy */ 306 if (!found) 307 goto inval; 308 } 309 310 *res = rpc_ucode_blob_array_to_ucv(&script->vm, blob_data(msg), blob_len(msg), true); 311 312 return UBUS_STATUS_OK; 313 314 inval: 315 *res = NULL; 316 317 return UBUS_STATUS_INVALID_ARGUMENT; 318 } 319 320 static uc_value_t * 321 rpc_ucode_gather_call_info(uc_vm_t *vm, 322 struct ubus_context *ctx, struct ubus_request_data *req, 323 struct ubus_object *obj, const char *ubus_method_name) 324 { 325 uc_value_t *info, *o; 326 327 info = ucv_object_new(vm); 328 329 o = ucv_object_new(vm); 330 331 ucv_object_add(o, "user", ucv_string_new(req->acl.user)); 332 ucv_object_add(o, "group", ucv_string_new(req->acl.group)); 333 ucv_object_add(o, "object", ucv_string_new(req->acl.object)); 334 335 ucv_object_add(info, "acl", o); 336 337 o = ucv_object_new(vm); 338 339 ucv_object_add(o, "id", ucv_uint64_new(obj->id)); 340 ucv_object_add(o, "name", ucv_string_new(obj->name)); 341 342 if (obj->path) 343 ucv_object_add(o, "path", ucv_string_new(obj->path)); 344 345 ucv_object_add(info, "object", o); 346 347 ucv_object_add(info, "method", ucv_string_new(ubus_method_name)); 348 349 return info; 350 } 351 352 static void 353 rpc_ucode_request_finish(rpc_ucode_call_ctx_t *callctx, int code, uc_value_t *reply) 354 { 355 rpc_ucode_script_t *script = callctx->script; 356 uc_resource_t *r; 357 size_t i; 358 359 if (callctx->replied) 360 return; 361 362 if (reply) { 363 blob_buf_init(&buf, 0); 364 rpc_ucode_ucv_object_to_blob(reply, &buf); 365 ubus_send_reply(callctx->ubus, &callctx->req, buf.head); 366 } 367 368 ubus_complete_deferred_request(callctx->ubus, &callctx->req, code); 369 370 callctx->replied = true; 371 372 for (i = 0; i < ucv_array_length(script->pending_replies); i++) { 373 r = (uc_resource_t *)ucv_array_get(script->pending_replies, i); 374 375 if (r && r->data == callctx) { 376 ucv_array_set(script->pending_replies, i, NULL); 377 break; 378 } 379 } 380 } 381 382 static void 383 rpc_ucode_request_timeout(struct uloop_timeout *timeout) 384 { 385 rpc_ucode_call_ctx_t *callctx = container_of(timeout, rpc_ucode_call_ctx_t, timeout); 386 387 rpc_ucode_request_finish(callctx, UBUS_STATUS_TIMEOUT, NULL); 388 } 389 390 static int 391 rpc_ucode_script_call(struct ubus_context *ctx, struct ubus_object *obj, 392 struct ubus_request_data *req, const char *ubus_method_name, 393 struct blob_attr *msg) 394 { 395 rpc_ucode_script_t *script = rpc_ucode_obj_to_script(obj); 396 uc_value_t *func, *args = NULL, *reqobj, *reqproto, *res; 397 rpc_ucode_call_ctx_t *callctx; 398 size_t i; 399 int rv; 400 401 rv = rpc_ucode_validate_call_args(obj, ubus_method_name, msg, &args); 402 403 if (rv != UBUS_STATUS_OK) 404 return rv; 405 406 func = ucv_object_get( 407 ucv_object_get(rpc_ucode_obj_to_signature(obj), ubus_method_name, NULL), 408 "call", NULL 409 ); 410 411 if (!ucv_is_callable(func)) 412 return UBUS_STATUS_METHOD_NOT_FOUND; 413 414 /* allocate deferred method call context */ 415 callctx = calloc(1, sizeof(*callctx)); 416 417 if (!callctx) 418 return UBUS_STATUS_UNKNOWN_ERROR; 419 420 callctx->ubus = ctx; 421 callctx->script = script; 422 423 ubus_defer_request(ctx, req, &callctx->req); 424 425 /* create ucode request type object and set properties */ 426 reqobj = uc_resource_new(script->requesttype, callctx); 427 reqproto = ucv_object_new(&script->vm); 428 429 ucv_object_add(reqproto, "args", args); 430 ucv_object_add(reqproto, "info", 431 rpc_ucode_gather_call_info(&script->vm, ctx, req, obj, ubus_method_name)); 432 433 ucv_prototype_set(ucv_prototype_get(reqobj), reqproto); 434 435 /* push handler and request object onto stack */ 436 uc_vm_stack_push(&script->vm, ucv_get(func)); 437 uc_vm_stack_push(&script->vm, ucv_get(reqobj)); 438 439 /* execute request handler function */ 440 switch (uc_vm_call(&script->vm, false, 1)) { 441 case EXCEPTION_NONE: 442 res = uc_vm_stack_pop(&script->vm); 443 444 /* The handler function invoked a nested aync ubus request and returned it */ 445 if (ucv_resource_dataptr(res, "ubus.deferred")) { 446 /* Install guard timer in case the reply callback is never called */ 447 callctx->timeout.cb = rpc_ucode_request_timeout; 448 uloop_timeout_set(&callctx->timeout, request_timeout); 449 450 /* Add wrapped request context into registry to prevent GC'ing 451 * until reply or timeout occurred */ 452 for (i = 0;; i++) { 453 if (ucv_array_get(script->pending_replies, i) == NULL) { 454 ucv_array_set(script->pending_replies, i, ucv_get(reqobj)); 455 break; 456 } 457 } 458 } 459 460 /* Otherwise, when the function returned an object, treat it as 461 * reply data and conclude deferred request immediately */ 462 else if (ucv_type(res) == UC_OBJECT) { 463 blob_buf_init(&buf, 0); 464 rpc_ucode_ucv_object_to_blob(res, &buf); 465 ubus_send_reply(ctx, &callctx->req, buf.head); 466 467 ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_OK); 468 callctx->replied = true; 469 } 470 471 /* If neither a deferred ubus request, nor a plain object were 472 * returned and if reqobj.reply() hasn't been called, immediately 473 * finish deferred request with UBUS_STATUS_NO_DATA. The */ 474 else if (!callctx->replied) { 475 ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_NO_DATA); 476 callctx->replied = true; 477 } 478 479 ucv_put(res); 480 break; 481 482 /* if the handler function invoked exit(), forward exit status as ubus 483 * return code, map out of range values to UBUS_STATUS_UNKNOWN_ERROR. */ 484 case EXCEPTION_EXIT: 485 rv = script->vm.arg.s32; 486 487 if (rv < UBUS_STATUS_OK || rv >= __UBUS_STATUS_LAST) 488 rv = UBUS_STATUS_UNKNOWN_ERROR; 489 490 ubus_complete_deferred_request(ctx, &callctx->req, rv); 491 callctx->replied = true; 492 break; 493 494 /* treat other exceptions as unknown error */ 495 default: 496 ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_UNKNOWN_ERROR); 497 callctx->replied = true; 498 break; 499 } 500 501 /* release request object */ 502 ucv_put(reqobj); 503 504 /* garbage collect */ 505 ucv_gc(&script->vm); 506 507 return UBUS_STATUS_OK; 508 } 509 510 static uc_program_t * 511 rpc_ucode_script_compile(const char *path, uc_source_t *src) 512 { 513 char *syntax_error = NULL; 514 uc_program_t *prog; 515 516 prog = uc_compile(&config, src, &syntax_error); 517 518 if (!prog) 519 fprintf(stderr, "Unable to compile ucode script %s: %s\n", 520 path, syntax_error); 521 522 uc_source_put(src); 523 free(syntax_error); 524 525 return prog; 526 } 527 528 static bool 529 rpc_ucode_script_validate(rpc_ucode_script_t *script) 530 { 531 uc_value_t *signature = uc_vm_registry_get(&script->vm, "rpcd.ucode.signature"); 532 uc_value_t *args, *func; 533 534 if (ucv_type(signature) != UC_OBJECT) { 535 fprintf(stderr, "Invalid object signature for ucode script %s" 536 " - expected dictionary, got %s\n", 537 script->path, ucv_typename(signature)); 538 539 return false; 540 } 541 542 ucv_object_foreach(signature, ubus_object_name, ubus_object_methods) { 543 if (ucv_type(ubus_object_methods) != UC_OBJECT) { 544 fprintf(stderr, "Invalid method signature for ucode script %s, object %s" 545 " - expected dictionary, got %s\n", 546 script->path, ubus_object_name, ucv_typename(ubus_object_methods)); 547 548 return false; 549 } 550 551 ucv_object_foreach(ubus_object_methods, ubus_method_name, ubus_method_definition) { 552 func = ucv_object_get(ubus_method_definition, "call", NULL); 553 args = ucv_object_get(ubus_method_definition, "args", NULL); 554 555 if (ucv_type(ubus_method_definition) != UC_OBJECT) { 556 fprintf(stderr, "Invalid method definition for ucode script %s, object %s, method %s" 557 " - expected dictionary, got %s\n", 558 script->path, ubus_object_name, ubus_method_name, ucv_typename(ubus_method_definition)); 559 560 return false; 561 } 562 563 if (!ucv_is_callable(func)) { 564 fprintf(stderr, "Invalid method callback for ucode script %s, object %s, method %s" 565 " - expected callable, got %s\n", 566 script->path, ubus_object_name, ubus_method_name, ucv_typename(func)); 567 568 return false; 569 } 570 571 if (args) { 572 if (ucv_type(args) != UC_OBJECT) { 573 fprintf(stderr, "Invalid method argument definition for ucode script %s, " 574 "object %s, method %s - expected dictionary, got %s\n", 575 script->path, ubus_object_name, ubus_method_name, ucv_typename(args)); 576 577 return false; 578 } 579 580 ucv_object_foreach(args, ubus_argument_name, ubus_argument_typehint) { 581 switch (ucv_type(ubus_argument_typehint)) { 582 case UC_BOOLEAN: 583 case UC_INTEGER: 584 case UC_DOUBLE: 585 case UC_STRING: 586 case UC_ARRAY: 587 case UC_OBJECT: 588 continue; 589 590 default: 591 fprintf(stderr, "Unsupported argument type for ucode script %s, object %s, " 592 "method %s, argument %s - expected boolean, integer, string, " 593 "array or object, got %s\n", 594 script->path, ubus_object_name, ubus_method_name, ubus_argument_name, 595 ucv_typename(ubus_argument_typehint)); 596 597 return false; 598 } 599 } 600 } 601 } 602 } 603 604 return true; 605 } 606 607 static bool 608 rpc_ucode_method_register(struct ubus_method *method, const char *ubus_method_name, uc_value_t *ubus_method_arguments) 609 { 610 struct blobmsg_policy *policy; 611 enum blobmsg_type type; 612 613 method->name = strdup(ubus_method_name); 614 615 if (!method->name) { 616 fprintf(stderr, "Unable to allocate ubus method name: %s\n", 617 strerror(errno)); 618 619 return false; 620 } 621 622 method->policy = calloc(ucv_object_length(ubus_method_arguments), sizeof(*method->policy)); 623 624 if (!method->policy) { 625 fprintf(stderr, "Unable to allocate ubus method argument policy: %s\n", 626 strerror(errno)); 627 628 return false; 629 } 630 631 method->handler = rpc_ucode_script_call; 632 633 ucv_object_foreach(ubus_method_arguments, ubus_argument_name, ubus_argument_typehint) { 634 switch (ucv_type(ubus_argument_typehint)) { 635 case UC_BOOLEAN: 636 type = BLOBMSG_TYPE_INT8; 637 break; 638 639 case UC_INTEGER: 640 switch (ucv_int64_get(ubus_argument_typehint)) { 641 case 8: 642 type = BLOBMSG_TYPE_INT8; 643 break; 644 645 case 16: 646 type = BLOBMSG_TYPE_INT16; 647 break; 648 649 case 64: 650 type = BLOBMSG_TYPE_INT64; 651 break; 652 653 default: 654 type = BLOBMSG_TYPE_INT32; 655 break; 656 } 657 658 break; 659 660 case UC_DOUBLE: 661 type = BLOBMSG_TYPE_DOUBLE; 662 break; 663 664 case UC_ARRAY: 665 type = BLOBMSG_TYPE_ARRAY; 666 break; 667 668 case UC_OBJECT: 669 type = BLOBMSG_TYPE_TABLE; 670 break; 671 672 default: 673 type = BLOBMSG_TYPE_STRING; 674 break; 675 } 676 677 policy = (struct blobmsg_policy *)&method->policy[method->n_policy++]; 678 679 policy->type = type; 680 policy->name = strdup(ubus_argument_name); 681 682 if (!policy->name) { 683 fprintf(stderr, "Unable to allocate ubus method argument name: %s\n", 684 strerror(errno)); 685 686 return false; 687 } 688 } 689 690 return true; 691 } 692 693 static bool 694 rpc_ucode_script_register(struct ubus_context *ctx, rpc_ucode_script_t *script) 695 { 696 uc_value_t *signature = uc_vm_registry_get(&script->vm, "rpcd.ucode.signature"); 697 const struct blobmsg_policy *policy; 698 rpc_ucode_ubus_obj_t *uuobj = NULL; 699 char *tptr, *tnptr, *onptr, *mptr; 700 struct ubus_method *method; 701 struct ubus_object *obj; 702 size_t typelen, namelen; 703 uc_value_t *args; 704 int rv; 705 706 if (!rpc_ucode_script_validate(script)) 707 return false; 708 709 ucv_object_foreach(signature, ubus_object_name, ubus_object_methods) { 710 namelen = strlen(ubus_object_name); 711 typelen = strlen("rpcd-plugin-ucode-") + namelen; 712 713 uuobj = calloc_a(sizeof(*uuobj), 714 &onptr, namelen + 1, 715 &mptr, ucv_object_length(ubus_object_methods) * sizeof(struct ubus_method), 716 &tptr, sizeof(struct ubus_object_type), 717 &tnptr, typelen + 1); 718 719 if (!uuobj) { 720 fprintf(stderr, "Unable to allocate ubus object signature: %s\n", 721 strerror(errno)); 722 723 continue; 724 } 725 726 list_add(&uuobj->list, &uuobjs); 727 728 uuobj->script = script; 729 uuobj->signature = ubus_object_methods; 730 731 snprintf(tnptr, typelen, "rpcd-plugin-ucode-%s", ubus_object_name); 732 733 method = (struct ubus_method *)mptr; 734 735 obj = &uuobj->ubusobj; 736 obj->name = strncpy(onptr, ubus_object_name, namelen); 737 obj->methods = method; 738 739 obj->type = (struct ubus_object_type *)tptr; 740 obj->type->name = tnptr; 741 obj->type->methods = obj->methods; 742 743 ucv_object_foreach(ubus_object_methods, ubus_method_name, ubus_method_definition) { 744 args = ucv_object_get(ubus_method_definition, "args", NULL); 745 746 if (!rpc_ucode_method_register(&method[obj->n_methods++], ubus_method_name, args)) 747 goto free; 748 } 749 750 obj->type = (struct ubus_object_type *)tptr; 751 obj->type->name = tnptr; 752 obj->type->methods = obj->methods; 753 obj->type->n_methods = obj->n_methods; 754 755 rv = ubus_add_object(ctx, obj); 756 757 if (rv != UBUS_STATUS_OK) { 758 fprintf(stderr, "Unable to register ubus object %s: %s\n", 759 obj->name, ubus_strerror(rv)); 760 761 goto free; 762 } 763 764 continue; 765 766 free: 767 for (; obj->n_methods > 0; method++, obj->n_methods--) { 768 for (policy = method->policy; method->n_policy > 0; policy++, method->n_policy--) 769 free((char *)policy->name); 770 771 free((char *)method->name); 772 free((char *)method->policy); 773 } 774 775 free(uuobj); 776 } 777 778 return true; 779 } 780 781 static uc_value_t * 782 rpc_ucode_request_reply(uc_vm_t *vm, size_t nargs) 783 { 784 rpc_ucode_call_ctx_t **callctx = uc_fn_this("rpcd.ucode.request"); 785 uc_value_t *reply = uc_fn_arg(0); 786 uc_value_t *rcode = uc_fn_arg(1); 787 int64_t code = UBUS_STATUS_OK; 788 789 if (!callctx || !*callctx) { 790 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, 791 "Attempt to invoke reply() on invalid self"); 792 793 return NULL; 794 } 795 else if (reply && ucv_type(reply) != UC_OBJECT) { 796 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, 797 "First argument to reply() must be null or an object"); 798 799 return NULL; 800 } 801 else if (rcode && ucv_type(rcode) != UC_INTEGER) { 802 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, 803 "Second argument to reply() must be null or an integer"); 804 805 return NULL; 806 } 807 808 if ((*callctx)->replied) { 809 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, 810 "Reply has already been sent"); 811 812 return NULL; 813 } 814 815 if (rcode) { 816 code = ucv_int64_get(rcode); 817 818 if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST) 819 code = UBUS_STATUS_UNKNOWN_ERROR; 820 } 821 822 rpc_ucode_request_finish(*callctx, code, reply); 823 824 return NULL; 825 } 826 827 static uc_value_t * 828 rpc_ucode_request_error(uc_vm_t *vm, size_t nargs) 829 { 830 rpc_ucode_call_ctx_t **callctx = uc_fn_this("rpcd.ucode.request"); 831 uc_value_t *rcode = uc_fn_arg(0); 832 int64_t code; 833 834 if (!callctx || !*callctx) { 835 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, 836 "Attempt to invoke error() on invalid self"); 837 838 return NULL; 839 } 840 else if (ucv_type(rcode) != UC_INTEGER) { 841 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, 842 "First argument to error() must be an integer"); 843 844 return NULL; 845 } 846 847 if ((*callctx)->replied) { 848 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, 849 "Reply has already been sent"); 850 851 return NULL; 852 } 853 854 855 code = ucv_int64_get(rcode); 856 857 if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST) 858 code = UBUS_STATUS_UNKNOWN_ERROR; 859 860 rpc_ucode_request_finish(*callctx, code, NULL); 861 862 return NULL; 863 } 864 865 static const uc_function_list_t rpc_ucode_request_fns[] = { 866 { "reply", rpc_ucode_request_reply }, 867 { "error", rpc_ucode_request_error }, 868 }; 869 870 static void 871 rpc_ucode_request_gc(void *ud) 872 { 873 rpc_ucode_call_ctx_t *callctx = ud; 874 875 uloop_timeout_cancel(&callctx->timeout); 876 free(callctx); 877 } 878 879 static void 880 rpc_ucode_init_globals(rpc_ucode_script_t *script) 881 { 882 uc_vm_t *vm = &script->vm; 883 uc_value_t *scope = uc_vm_scope_get(vm); 884 885 #define status_const(name) \ 886 ucv_object_add(scope, #name, ucv_uint64_new(name)) 887 888 status_const(UBUS_STATUS_OK); 889 status_const(UBUS_STATUS_INVALID_COMMAND); 890 status_const(UBUS_STATUS_INVALID_ARGUMENT); 891 status_const(UBUS_STATUS_METHOD_NOT_FOUND); 892 status_const(UBUS_STATUS_NOT_FOUND); 893 status_const(UBUS_STATUS_NO_DATA); 894 status_const(UBUS_STATUS_PERMISSION_DENIED); 895 status_const(UBUS_STATUS_TIMEOUT); 896 status_const(UBUS_STATUS_NOT_SUPPORTED); 897 status_const(UBUS_STATUS_UNKNOWN_ERROR); 898 status_const(UBUS_STATUS_CONNECTION_FAILED); 899 900 #undef status_const 901 902 uc_stdlib_load(scope); 903 904 script->requesttype = uc_type_declare(vm, "rpcd.ucode.request", 905 rpc_ucode_request_fns, rpc_ucode_request_gc); 906 } 907 908 static rpc_ucode_script_t * 909 rpc_ucode_script_execute(struct ubus_context *ctx, const char *path, uc_program_t *prog) 910 { 911 rpc_ucode_script_t *script; 912 uc_value_t *signature; 913 uc_vm_status_t status; 914 size_t pathlen; 915 char *pptr; 916 917 pathlen = strlen(path); 918 script = calloc_a(sizeof(*script), &pptr, pathlen + 1); 919 920 if (!script) { 921 fprintf(stderr, "Unable to allocate context for ucode script %s: %s\n", 922 path, strerror(errno)); 923 924 uc_program_put(prog); 925 926 return NULL; 927 } 928 929 script->path = strncpy(pptr, path, pathlen); 930 931 uc_vm_init(&script->vm, &config); 932 rpc_ucode_init_globals(script); 933 934 status = uc_vm_execute(&script->vm, prog, &signature); 935 936 script->pending_replies = ucv_array_new(&script->vm); 937 938 uc_vm_registry_set(&script->vm, "rpcd.ucode.signature", signature); 939 uc_vm_registry_set(&script->vm, "rpcd.ucode.deferreds", script->pending_replies); 940 941 uc_program_put(prog); 942 ucv_gc(&script->vm); 943 944 switch (status) { 945 case STATUS_OK: 946 if (rpc_ucode_script_register(ctx, script)) 947 return script; 948 949 fprintf(stderr, "Skipping registration of ucode script %s\n", path); 950 break; 951 952 case STATUS_EXIT: 953 fprintf(stderr, "The ucode script %s invoked exit(%" PRId64 ")\n", 954 path, ucv_int64_get(signature)); 955 break; 956 957 case ERROR_COMPILE: 958 fprintf(stderr, "Compilation error while executing ucode script %s\n", path); 959 break; 960 961 case ERROR_RUNTIME: 962 fprintf(stderr, "Runtime error while executing ucode script %s\n", path); 963 break; 964 } 965 966 uc_vm_free(&script->vm); 967 free(script); 968 969 return NULL; 970 } 971 972 static int 973 rpc_ucode_init_script(struct ubus_context *ctx, const char *path) 974 { 975 rpc_ucode_script_t *script; 976 uc_program_t *prog; 977 uc_source_t *src; 978 979 src = uc_source_new_file(path); 980 981 if (!src) { 982 fprintf(stderr, "Unable to open ucode script %s: %s\n", 983 path, strerror(errno)); 984 985 return UBUS_STATUS_UNKNOWN_ERROR; 986 } 987 988 prog = rpc_ucode_script_compile(path, src); 989 990 if (!prog) 991 return UBUS_STATUS_UNKNOWN_ERROR; 992 993 script = rpc_ucode_script_execute(ctx, path, prog); 994 995 if (!script) 996 return UBUS_STATUS_UNKNOWN_ERROR; 997 998 list_add(&script->list, &scripts); 999 1000 return UBUS_STATUS_OK; 1001 } 1002 1003 static int 1004 rpc_ucode_api_init(const struct rpc_daemon_ops *ops, struct ubus_context *ctx) 1005 { 1006 char path[PATH_MAX]; 1007 struct dirent *e; 1008 struct stat s; 1009 int rv = 0; 1010 DIR *d; 1011 1012 request_timeout = *ops->exec_timeout; 1013 1014 /* reopen ucode.so with RTLD_GLOBAL in order to export libucode runtime 1015 * symbols for ucode extensions loaded later at runtime */ 1016 if (!dlopen(RPC_LIBRARY_DIRECTORY "/ucode.so", RTLD_LAZY|RTLD_GLOBAL)) { 1017 fprintf(stderr, "Failed to dlopen() ucode.so: %s, dynamic ucode plugins may fail\n", 1018 dlerror()); 1019 } 1020 if ((d = opendir(RPC_UCSCRIPT_DIRECTORY)) != NULL) { 1021 while ((e = readdir(d)) != NULL) { 1022 snprintf(path, sizeof(path), RPC_UCSCRIPT_DIRECTORY "/%s", e->d_name); 1023 1024 if (stat(path, &s) || !S_ISREG(s.st_mode)) 1025 continue; 1026 1027 if (s.st_mode & S_IWOTH) { 1028 fprintf(stderr, "Ignoring ucode script %s because it is world writable\n", 1029 path); 1030 1031 continue; 1032 } 1033 1034 rv |= rpc_ucode_init_script(ctx, path); 1035 } 1036 1037 closedir(d); 1038 } 1039 1040 return rv; 1041 } 1042 1043 struct rpc_plugin rpc_plugin = { 1044 .init = rpc_ucode_api_init 1045 }; 1046
This page was automatically generated by LXR 0.3.1. • OpenWrt