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