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