2 * rpcd - UBUS RPC server - ucode plugin
4 * Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io>
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.
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.
19 #include <sys/types.h>
24 #include <libubox/blobmsg.h>
25 #include <libubox/blobmsg_json.h>
29 #include <ucode/compiler.h>
30 #include <ucode/lib.h>
33 #include <rpcd/plugin.h>
35 #define RPC_UCSCRIPT_DIRECTORY INSTALL_PREFIX "/share/rpcd/ucode"
37 static struct blob_buf buf
;
38 static int request_timeout
;
41 * Track script instances and registered ubus objects in these lists.
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.
48 static LIST_HEAD(scripts
);
49 static LIST_HEAD(uuobjs
);
52 struct list_head list
;
54 uc_resource_type_t
*requesttype
;
55 uc_value_t
*pending_replies
;
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
;
67 struct ubus_context
*ubus
;
68 struct ubus_request_data req
;
69 struct uloop_timeout timeout
;
70 rpc_ucode_script_t
*script
;
75 } rpc_ucode_call_ctx_t
;
77 static uc_parse_config_t config
= {
78 .strict_declarations
= false,
79 .lstrip_blocks
= true,
85 static rpc_ucode_script_t
*
86 rpc_ucode_obj_to_script(struct ubus_object
*obj
)
88 rpc_ucode_ubus_obj_t
*uo
= container_of(obj
, rpc_ucode_ubus_obj_t
, ubusobj
);
94 rpc_ucode_obj_to_signature(struct ubus_object
*obj
)
96 rpc_ucode_ubus_obj_t
*uo
= container_of(obj
, rpc_ucode_ubus_obj_t
, ubusobj
);
102 rpc_ucode_ucv_array_to_blob(uc_value_t
*val
, struct blob_buf
*blob
);
105 rpc_ucode_ucv_object_to_blob(uc_value_t
*val
, struct blob_buf
*blob
);
108 rpc_ucode_ucv_to_blob(const char *name
, uc_value_t
*val
, struct blob_buf
*blob
)
113 switch (ucv_type(val
)) {
115 blobmsg_add_field(blob
, BLOBMSG_TYPE_UNSPEC
, name
, NULL
, 0);
119 blobmsg_add_u8(blob
, name
, ucv_boolean_get(val
));
123 n
= ucv_int64_get(val
);
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
);
130 blobmsg_add_u64(blob
, name
, n
);
135 blobmsg_add_double(blob
, name
, ucv_double_get(val
));
139 blobmsg_add_string(blob
, name
, ucv_string_get(val
));
143 c
= blobmsg_open_array(blob
, name
);
144 rpc_ucode_ucv_array_to_blob(val
, blob
);
145 blobmsg_close_array(blob
, c
);
149 c
= blobmsg_open_table(blob
, name
);
150 rpc_ucode_ucv_object_to_blob(val
, blob
);
151 blobmsg_close_table(blob
, c
);
160 rpc_ucode_ucv_array_to_blob(uc_value_t
*val
, struct blob_buf
*blob
)
164 for (i
= 0; i
< ucv_array_length(val
); i
++)
165 rpc_ucode_ucv_to_blob(NULL
, ucv_array_get(val
, i
), blob
);
169 rpc_ucode_ucv_object_to_blob(uc_value_t
*val
, struct blob_buf
*blob
)
171 ucv_object_foreach(val
, k
, v
)
172 rpc_ucode_ucv_to_blob(k
, v
, blob
);
176 rpc_ucode_blob_to_ucv(uc_vm_t
*vm
, struct blob_attr
*attr
, bool table
, const char **name
);
179 rpc_ucode_blob_array_to_ucv(uc_vm_t
*vm
, struct blob_attr
*attr
, size_t len
, bool table
)
181 uc_value_t
*o
= table
? ucv_object_new(vm
) : ucv_array_new(vm
);
183 struct blob_attr
*pos
;
190 __blob_for_each_attr(pos
, attr
, rem
) {
192 v
= rpc_ucode_blob_to_ucv(vm
, pos
, table
, &name
);
195 ucv_object_add(o
, name
, v
);
197 ucv_array_push(o
, v
);
206 rpc_ucode_blob_to_ucv(uc_vm_t
*vm
, struct blob_attr
*attr
, bool table
, const char **name
)
211 if (!blobmsg_check_attr(attr
, false))
214 if (table
&& blobmsg_name(attr
)[0])
215 *name
= blobmsg_name(attr
);
217 data
= blobmsg_data(attr
);
218 len
= blobmsg_data_len(attr
);
220 switch (blob_id(attr
)) {
221 case BLOBMSG_TYPE_BOOL
:
222 return ucv_boolean_new(*(uint8_t *)data
);
224 case BLOBMSG_TYPE_INT16
:
225 return ucv_int64_new((int16_t)be16_to_cpu(*(uint16_t *)data
));
227 case BLOBMSG_TYPE_INT32
:
228 return ucv_int64_new((int32_t)be32_to_cpu(*(uint32_t *)data
));
230 case BLOBMSG_TYPE_INT64
:
231 return ucv_int64_new((int64_t)be64_to_cpu(*(uint64_t *)data
));
233 case BLOBMSG_TYPE_DOUBLE
:
240 v
.u64
= be64_to_cpu(*(uint64_t *)data
);
242 return ucv_double_new(v
.d
);
244 case BLOBMSG_TYPE_STRING
:
245 return ucv_string_new(data
);
247 case BLOBMSG_TYPE_ARRAY
:
248 return rpc_ucode_blob_array_to_ucv(vm
, data
, len
, false);
250 case BLOBMSG_TYPE_TABLE
:
251 return rpc_ucode_blob_array_to_ucv(vm
, data
, len
, true);
259 rpc_ucode_validate_call_args(struct ubus_object
*obj
, const char *ubus_method_name
, struct blob_attr
*msg
, uc_value_t
**res
)
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
;
269 for (i
= 0; i
< obj
->n_methods
; i
++) {
270 if (!strcmp(obj
->methods
[i
].name
, ubus_method_name
)) {
271 method
= &obj
->methods
[i
];
277 return UBUS_STATUS_METHOD_NOT_FOUND
;
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
;
285 if (!blob_is_extended(attr
))
286 return UBUS_STATUS_INVALID_ARGUMENT
;
288 hdr
= blob_data(attr
);
291 for (i
= 0; i
< method
->n_policy
; i
++) {
292 if (blobmsg_namelen(hdr
) != strlen(method
->policy
[i
].name
))
295 if (strcmp(method
->policy
[i
].name
, (char *)hdr
->name
))
298 /* named argument found but wrong type */
299 if (blob_id(attr
) != method
->policy
[i
].type
)
306 /* named argument not found in policy */
308 /* allow special ubus_rpc_session argument */
309 if (!strcmp("ubus_rpc_session", (char *)hdr
->name
) && blob_id(attr
) == BLOBMSG_TYPE_STRING
)
316 *res
= rpc_ucode_blob_array_to_ucv(&script
->vm
, blob_data(msg
), blob_len(msg
), true);
318 return UBUS_STATUS_OK
;
323 return UBUS_STATUS_INVALID_ARGUMENT
;
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
)
331 uc_value_t
*info
, *o
;
333 info
= ucv_object_new(vm
);
335 o
= ucv_object_new(vm
);
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
));
341 ucv_object_add(info
, "acl", o
);
343 o
= ucv_object_new(vm
);
345 ucv_object_add(o
, "id", ucv_uint64_new(obj
->id
));
346 ucv_object_add(o
, "name", ucv_string_new(obj
->name
));
349 ucv_object_add(o
, "path", ucv_string_new(obj
->path
));
351 ucv_object_add(info
, "object", o
);
353 ucv_object_add(info
, "method", ucv_string_new(ubus_method_name
));
359 rpc_ucode_request_finish(rpc_ucode_call_ctx_t
*callctx
, int code
, uc_value_t
*reply
)
361 rpc_ucode_script_t
*script
= callctx
->script
;
365 if (callctx
->replied
)
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
);
374 ubus_complete_deferred_request(callctx
->ubus
, &callctx
->req
, code
);
376 callctx
->replied
= true;
378 for (i
= 0; i
< ucv_array_length(script
->pending_replies
); i
++) {
379 r
= (uc_resource_t
*)ucv_array_get(script
->pending_replies
, i
);
381 if (r
&& r
->data
== callctx
) {
382 ucv_array_set(script
->pending_replies
, i
, NULL
);
389 rpc_ucode_request_timeout(struct uloop_timeout
*timeout
)
391 rpc_ucode_call_ctx_t
*callctx
= container_of(timeout
, rpc_ucode_call_ctx_t
, timeout
);
393 rpc_ucode_request_finish(callctx
, UBUS_STATUS_TIMEOUT
, NULL
);
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
)
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
;
408 rv
= rpc_ucode_validate_call_args(obj
, ubus_method_name
, msg
, &args
);
410 if (rv
!= UBUS_STATUS_OK
)
413 func
= ucv_object_get(
414 ucv_object_get(rpc_ucode_obj_to_signature(obj
), ubus_method_name
, NULL
),
418 if (!ucv_is_callable(func
))
419 return UBUS_STATUS_METHOD_NOT_FOUND
;
421 /* allocate deferred method call context */
422 callctx
= calloc(1, sizeof(*callctx
));
425 return UBUS_STATUS_UNKNOWN_ERROR
;
428 callctx
->script
= script
;
430 ubus_defer_request(ctx
, req
, &callctx
->req
);
432 /* create ucode request type object and set properties */
433 reqobj
= uc_resource_new(script
->requesttype
, callctx
);
434 reqproto
= ucv_object_new(&script
->vm
);
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
));
440 ucv_prototype_set(ucv_prototype_get(reqobj
), reqproto
);
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
));
446 /* execute request handler function */
447 switch (uc_vm_call(&script
->vm
, false, 1)) {
449 res
= uc_vm_stack_pop(&script
->vm
);
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
);
457 /* Add wrapped request context into registry to prevent GC'ing
458 * until reply or timeout occurred */
460 if (ucv_array_get(script
->pending_replies
, i
) == NULL
) {
461 ucv_array_set(script
->pending_replies
, i
, ucv_get(reqobj
));
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
);
474 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_OK
);
475 callctx
->replied
= true;
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;
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. */
492 rv
= script
->vm
.arg
.s32
;
494 if (rv
< UBUS_STATUS_OK
|| rv
>= __UBUS_STATUS_LAST
)
495 rv
= UBUS_STATUS_UNKNOWN_ERROR
;
497 ubus_complete_deferred_request(ctx
, &callctx
->req
, rv
);
498 callctx
->replied
= true;
501 /* treat other exceptions as unknown error */
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";
511 res
= ucv_object_get(
512 ucv_array_get(script
->vm
.exception
.stacktrace
, 0),
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
));
520 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_UNKNOWN_ERROR
);
521 callctx
->replied
= true;
525 /* release request object */
528 /* garbage collect */
531 return UBUS_STATUS_OK
;
534 static uc_program_t
*
535 rpc_ucode_script_compile(const char *path
, uc_source_t
*src
)
537 char *syntax_error
= NULL
;
540 prog
= uc_compile(&config
, src
, &syntax_error
);
543 fprintf(stderr
, "Unable to compile ucode script %s: %s\n",
553 rpc_ucode_script_validate(rpc_ucode_script_t
*script
)
555 uc_value_t
*signature
= uc_vm_registry_get(&script
->vm
, "rpcd.ucode.signature");
556 uc_value_t
*args
, *func
;
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
));
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
));
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
);
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
));
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
));
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
));
604 ucv_object_foreach(args
, ubus_argument_name
, ubus_argument_typehint
) {
605 switch (ucv_type(ubus_argument_typehint
)) {
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
));
632 rpc_ucode_method_register(struct ubus_method
*method
, const char *ubus_method_name
, uc_value_t
*ubus_method_arguments
)
634 struct blobmsg_policy
*policy
;
635 enum blobmsg_type type
;
637 method
->name
= strdup(ubus_method_name
);
640 fprintf(stderr
, "Unable to allocate ubus method name: %s\n",
646 method
->policy
= calloc(ucv_object_length(ubus_method_arguments
), sizeof(*method
->policy
));
648 if (!method
->policy
) {
649 fprintf(stderr
, "Unable to allocate ubus method argument policy: %s\n",
655 method
->handler
= rpc_ucode_script_call
;
657 ucv_object_foreach(ubus_method_arguments
, ubus_argument_name
, ubus_argument_typehint
) {
658 switch (ucv_type(ubus_argument_typehint
)) {
660 type
= BLOBMSG_TYPE_INT8
;
664 switch (ucv_int64_get(ubus_argument_typehint
)) {
666 type
= BLOBMSG_TYPE_INT8
;
670 type
= BLOBMSG_TYPE_INT16
;
674 type
= BLOBMSG_TYPE_INT64
;
678 type
= BLOBMSG_TYPE_INT32
;
685 type
= BLOBMSG_TYPE_DOUBLE
;
689 type
= BLOBMSG_TYPE_ARRAY
;
693 type
= BLOBMSG_TYPE_TABLE
;
697 type
= BLOBMSG_TYPE_STRING
;
701 policy
= (struct blobmsg_policy
*)&method
->policy
[method
->n_policy
++];
704 policy
->name
= strdup(ubus_argument_name
);
707 fprintf(stderr
, "Unable to allocate ubus method argument name: %s\n",
718 rpc_ucode_script_register(struct ubus_context
*ctx
, rpc_ucode_script_t
*script
)
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
;
730 if (!rpc_ucode_script_validate(script
))
733 ucv_object_foreach(signature
, ubus_object_name
, ubus_object_methods
) {
734 namelen
= strlen(ubus_object_name
);
735 typelen
= strlen("rpcd-plugin-ucode-") + namelen
;
737 uuobj
= calloc_a(sizeof(*uuobj
),
739 &mptr
, ucv_object_length(ubus_object_methods
) * sizeof(struct ubus_method
),
740 &tptr
, sizeof(struct ubus_object_type
),
741 &tnptr
, typelen
+ 1);
744 fprintf(stderr
, "Unable to allocate ubus object signature: %s\n",
750 list_add(&uuobj
->list
, &uuobjs
);
752 uuobj
->script
= script
;
753 uuobj
->signature
= ubus_object_methods
;
755 snprintf(tnptr
, typelen
, "rpcd-plugin-ucode-%s", ubus_object_name
);
757 method
= (struct ubus_method
*)mptr
;
759 obj
= &uuobj
->ubusobj
;
760 obj
->name
= strncpy(onptr
, ubus_object_name
, namelen
);
761 obj
->methods
= method
;
763 obj
->type
= (struct ubus_object_type
*)tptr
;
764 obj
->type
->name
= tnptr
;
765 obj
->type
->methods
= obj
->methods
;
767 ucv_object_foreach(ubus_object_methods
, ubus_method_name
, ubus_method_definition
) {
768 args
= ucv_object_get(ubus_method_definition
, "args", NULL
);
770 if (!rpc_ucode_method_register(&method
[obj
->n_methods
++], ubus_method_name
, args
))
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
;
779 rv
= ubus_add_object(ctx
, obj
);
781 if (rv
!= UBUS_STATUS_OK
) {
782 fprintf(stderr
, "Unable to register ubus object %s: %s\n",
783 obj
->name
, ubus_strerror(rv
));
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
);
795 free((char *)method
->name
);
796 free((char *)method
->policy
);
806 rpc_ucode_request_reply(uc_vm_t
*vm
, size_t nargs
)
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
;
813 if (!callctx
|| !*callctx
) {
814 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
815 "Attempt to invoke reply() on invalid self");
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");
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");
832 if ((*callctx
)->replied
) {
833 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
834 "Reply has already been sent");
840 code
= ucv_int64_get(rcode
);
842 if (errno
== ERANGE
|| code
< 0 || code
> __UBUS_STATUS_LAST
)
843 code
= UBUS_STATUS_UNKNOWN_ERROR
;
846 rpc_ucode_request_finish(*callctx
, code
, reply
);
852 rpc_ucode_request_error(uc_vm_t
*vm
, size_t nargs
)
854 rpc_ucode_call_ctx_t
**callctx
= uc_fn_this("rpcd.ucode.request");
855 uc_value_t
*rcode
= uc_fn_arg(0);
858 if (!callctx
|| !*callctx
) {
859 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
860 "Attempt to invoke error() on invalid self");
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");
871 if ((*callctx
)->replied
) {
872 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
873 "Reply has already been sent");
879 code
= ucv_int64_get(rcode
);
881 if (errno
== ERANGE
|| code
< 0 || code
> __UBUS_STATUS_LAST
)
882 code
= UBUS_STATUS_UNKNOWN_ERROR
;
884 rpc_ucode_request_finish(*callctx
, code
, NULL
);
889 static const uc_function_list_t rpc_ucode_request_fns
[] = {
890 { "reply", rpc_ucode_request_reply
},
891 { "error", rpc_ucode_request_error
},
895 rpc_ucode_request_gc(void *ud
)
897 rpc_ucode_call_ctx_t
*callctx
= ud
;
899 uloop_timeout_cancel(&callctx
->timeout
);
904 rpc_ucode_init_globals(rpc_ucode_script_t
*script
)
906 uc_vm_t
*vm
= &script
->vm
;
907 uc_value_t
*scope
= uc_vm_scope_get(vm
);
909 #define status_const(name) \
910 ucv_object_add(scope, #name, ucv_uint64_new(name))
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
);
926 uc_stdlib_load(scope
);
928 script
->requesttype
= uc_type_declare(vm
, "rpcd.ucode.request",
929 rpc_ucode_request_fns
, rpc_ucode_request_gc
);
932 static rpc_ucode_script_t
*
933 rpc_ucode_script_execute(struct ubus_context
*ctx
, const char *path
, uc_program_t
*prog
)
935 rpc_ucode_script_t
*script
;
936 uc_value_t
*signature
;
937 uc_vm_status_t status
;
941 pathlen
= strlen(path
);
942 script
= calloc_a(sizeof(*script
), &pptr
, pathlen
+ 1);
945 fprintf(stderr
, "Unable to allocate context for ucode script %s: %s\n",
946 path
, strerror(errno
));
948 uc_program_put(prog
);
953 script
->path
= strncpy(pptr
, path
, pathlen
);
955 uc_vm_init(&script
->vm
, &config
);
956 rpc_ucode_init_globals(script
);
958 status
= uc_vm_execute(&script
->vm
, prog
, &signature
);
960 script
->pending_replies
= ucv_array_new(&script
->vm
);
962 uc_vm_registry_set(&script
->vm
, "rpcd.ucode.signature", signature
);
963 uc_vm_registry_set(&script
->vm
, "rpcd.ucode.deferreds", script
->pending_replies
);
965 uc_program_put(prog
);
970 if (rpc_ucode_script_register(ctx
, script
))
973 fprintf(stderr
, "Skipping registration of ucode script %s\n", path
);
977 fprintf(stderr
, "The ucode script %s invoked exit(%" PRId64
")\n",
978 path
, ucv_int64_get(signature
));
982 fprintf(stderr
, "Compilation error while executing ucode script %s\n", path
);
986 fprintf(stderr
, "Runtime error while executing ucode script %s\n", path
);
990 uc_vm_free(&script
->vm
);
997 rpc_ucode_init_script(struct ubus_context
*ctx
, const char *path
)
999 rpc_ucode_script_t
*script
;
1003 src
= uc_source_new_file(path
);
1006 fprintf(stderr
, "Unable to open ucode script %s: %s\n",
1007 path
, strerror(errno
));
1009 return UBUS_STATUS_UNKNOWN_ERROR
;
1012 prog
= rpc_ucode_script_compile(path
, src
);
1015 return UBUS_STATUS_UNKNOWN_ERROR
;
1017 script
= rpc_ucode_script_execute(ctx
, path
, prog
);
1020 return UBUS_STATUS_UNKNOWN_ERROR
;
1022 list_add(&script
->list
, &scripts
);
1024 return UBUS_STATUS_OK
;
1028 rpc_ucode_api_init(const struct rpc_daemon_ops
*ops
, struct ubus_context
*ctx
)
1030 char path
[PATH_MAX
];
1036 request_timeout
= *ops
->exec_timeout
;
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",
1045 /* initialize default module search path */
1046 uc_search_path_init(&config
.module_search_path
);
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
);
1052 if (stat(path
, &s
) || !S_ISREG(s
.st_mode
))
1055 if (s
.st_mode
& S_IWOTH
) {
1056 fprintf(stderr
, "Ignoring ucode script %s because it is world writable\n",
1062 rv
|= rpc_ucode_init_script(ctx
, path
);
1071 struct rpc_plugin rpc_plugin
= {
1072 .init
= rpc_ucode_api_init