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,
84 static rpc_ucode_script_t
*
85 rpc_ucode_obj_to_script(struct ubus_object
*obj
)
87 rpc_ucode_ubus_obj_t
*uo
= container_of(obj
, rpc_ucode_ubus_obj_t
, ubusobj
);
93 rpc_ucode_obj_to_signature(struct ubus_object
*obj
)
95 rpc_ucode_ubus_obj_t
*uo
= container_of(obj
, rpc_ucode_ubus_obj_t
, ubusobj
);
101 rpc_ucode_ucv_array_to_blob(uc_value_t
*val
, struct blob_buf
*blob
);
104 rpc_ucode_ucv_object_to_blob(uc_value_t
*val
, struct blob_buf
*blob
);
107 rpc_ucode_ucv_to_blob(const char *name
, uc_value_t
*val
, struct blob_buf
*blob
)
112 switch (ucv_type(val
)) {
114 blobmsg_add_field(blob
, BLOBMSG_TYPE_UNSPEC
, name
, NULL
, 0);
118 blobmsg_add_u8(blob
, name
, ucv_boolean_get(val
));
122 n
= ucv_int64_get(val
);
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
);
129 blobmsg_add_u64(blob
, name
, n
);
134 blobmsg_add_double(blob
, name
, ucv_double_get(val
));
138 blobmsg_add_string(blob
, name
, ucv_string_get(val
));
142 c
= blobmsg_open_array(blob
, name
);
143 rpc_ucode_ucv_array_to_blob(val
, blob
);
144 blobmsg_close_array(blob
, c
);
148 c
= blobmsg_open_table(blob
, name
);
149 rpc_ucode_ucv_object_to_blob(val
, blob
);
150 blobmsg_close_table(blob
, c
);
159 rpc_ucode_ucv_array_to_blob(uc_value_t
*val
, struct blob_buf
*blob
)
163 for (i
= 0; i
< ucv_array_length(val
); i
++)
164 rpc_ucode_ucv_to_blob(NULL
, ucv_array_get(val
, i
), blob
);
168 rpc_ucode_ucv_object_to_blob(uc_value_t
*val
, struct blob_buf
*blob
)
170 ucv_object_foreach(val
, k
, v
)
171 rpc_ucode_ucv_to_blob(k
, v
, blob
);
175 rpc_ucode_blob_to_ucv(uc_vm_t
*vm
, struct blob_attr
*attr
, bool table
, const char **name
);
178 rpc_ucode_blob_array_to_ucv(uc_vm_t
*vm
, struct blob_attr
*attr
, size_t len
, bool table
)
180 uc_value_t
*o
= table
? ucv_object_new(vm
) : ucv_array_new(vm
);
182 struct blob_attr
*pos
;
189 __blob_for_each_attr(pos
, attr
, rem
) {
191 v
= rpc_ucode_blob_to_ucv(vm
, pos
, table
, &name
);
194 ucv_object_add(o
, name
, v
);
196 ucv_array_push(o
, v
);
205 rpc_ucode_blob_to_ucv(uc_vm_t
*vm
, struct blob_attr
*attr
, bool table
, const char **name
)
210 if (!blobmsg_check_attr(attr
, false))
213 if (table
&& blobmsg_name(attr
)[0])
214 *name
= blobmsg_name(attr
);
216 data
= blobmsg_data(attr
);
217 len
= blobmsg_data_len(attr
);
219 switch (blob_id(attr
)) {
220 case BLOBMSG_TYPE_BOOL
:
221 return ucv_boolean_new(*(uint8_t *)data
);
223 case BLOBMSG_TYPE_INT16
:
224 return ucv_int64_new((int16_t)be16_to_cpu(*(uint16_t *)data
));
226 case BLOBMSG_TYPE_INT32
:
227 return ucv_int64_new((int32_t)be32_to_cpu(*(uint32_t *)data
));
229 case BLOBMSG_TYPE_INT64
:
230 return ucv_int64_new((int64_t)be64_to_cpu(*(uint64_t *)data
));
232 case BLOBMSG_TYPE_DOUBLE
:
239 v
.u64
= be64_to_cpu(*(uint64_t *)data
);
241 return ucv_double_new(v
.d
);
243 case BLOBMSG_TYPE_STRING
:
244 return ucv_string_new(data
);
246 case BLOBMSG_TYPE_ARRAY
:
247 return rpc_ucode_blob_array_to_ucv(vm
, data
, len
, false);
249 case BLOBMSG_TYPE_TABLE
:
250 return rpc_ucode_blob_array_to_ucv(vm
, data
, len
, true);
258 rpc_ucode_validate_call_args(struct ubus_object
*obj
, const char *ubus_method_name
, struct blob_attr
*msg
, uc_value_t
**res
)
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
;
268 for (i
= 0; i
< obj
->n_methods
; i
++) {
269 if (!strcmp(obj
->methods
[i
].name
, ubus_method_name
)) {
270 method
= &obj
->methods
[i
];
276 return UBUS_STATUS_METHOD_NOT_FOUND
;
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
;
284 if (!blob_is_extended(attr
))
285 return UBUS_STATUS_INVALID_ARGUMENT
;
287 hdr
= blob_data(attr
);
290 for (i
= 0; i
< method
->n_policy
; i
++) {
291 if (blobmsg_namelen(hdr
) != strlen(method
->policy
[i
].name
))
294 if (strcmp(method
->policy
[i
].name
, (char *)hdr
->name
))
297 /* named argument found but wrong type */
298 if (blob_id(attr
) != method
->policy
[i
].type
)
305 /* named argument not found in policy */
310 *res
= rpc_ucode_blob_array_to_ucv(&script
->vm
, blob_data(msg
), blob_len(msg
), true);
312 return UBUS_STATUS_OK
;
317 return UBUS_STATUS_INVALID_ARGUMENT
;
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
)
325 uc_value_t
*info
, *o
;
327 info
= ucv_object_new(vm
);
329 o
= ucv_object_new(vm
);
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
));
335 ucv_object_add(info
, "acl", o
);
337 o
= ucv_object_new(vm
);
339 ucv_object_add(o
, "id", ucv_uint64_new(obj
->id
));
340 ucv_object_add(o
, "name", ucv_string_new(obj
->name
));
343 ucv_object_add(o
, "path", ucv_string_new(obj
->path
));
345 ucv_object_add(info
, "object", o
);
347 ucv_object_add(info
, "method", ucv_string_new(ubus_method_name
));
353 rpc_ucode_request_finish(rpc_ucode_call_ctx_t
*callctx
, int code
, uc_value_t
*reply
)
355 rpc_ucode_script_t
*script
= callctx
->script
;
359 if (callctx
->replied
)
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
);
368 ubus_complete_deferred_request(callctx
->ubus
, &callctx
->req
, code
);
370 callctx
->replied
= true;
372 for (i
= 0; i
< ucv_array_length(script
->pending_replies
); i
++) {
373 r
= (uc_resource_t
*)ucv_array_get(script
->pending_replies
, i
);
375 if (r
&& r
->data
== callctx
) {
376 ucv_array_set(script
->pending_replies
, i
, NULL
);
383 rpc_ucode_request_timeout(struct uloop_timeout
*timeout
)
385 rpc_ucode_call_ctx_t
*callctx
= container_of(timeout
, rpc_ucode_call_ctx_t
, timeout
);
387 rpc_ucode_request_finish(callctx
, UBUS_STATUS_TIMEOUT
, NULL
);
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
)
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
;
401 rv
= rpc_ucode_validate_call_args(obj
, ubus_method_name
, msg
, &args
);
403 if (rv
!= UBUS_STATUS_OK
)
406 func
= ucv_object_get(
407 ucv_object_get(rpc_ucode_obj_to_signature(obj
), ubus_method_name
, NULL
),
411 if (!ucv_is_callable(func
))
412 return UBUS_STATUS_METHOD_NOT_FOUND
;
414 /* allocate deferred method call context */
415 callctx
= calloc(1, sizeof(*callctx
));
418 return UBUS_STATUS_UNKNOWN_ERROR
;
421 callctx
->script
= script
;
423 ubus_defer_request(ctx
, req
, &callctx
->req
);
425 /* create ucode request type object and set properties */
426 reqobj
= uc_resource_new(script
->requesttype
, callctx
);
427 reqproto
= ucv_object_new(&script
->vm
);
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
));
433 ucv_prototype_set(ucv_prototype_get(reqobj
), reqproto
);
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
));
439 /* execute request handler function */
440 switch (uc_vm_call(&script
->vm
, false, 1)) {
442 res
= uc_vm_stack_pop(&script
->vm
);
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
);
450 /* Add wrapped request context into registry to prevent GC'ing
451 * until reply or timeout occurred */
453 if (ucv_array_get(script
->pending_replies
, i
) == NULL
) {
454 ucv_array_set(script
->pending_replies
, i
, ucv_get(reqobj
));
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
);
467 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_OK
);
468 callctx
->replied
= true;
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;
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. */
485 rv
= script
->vm
.arg
.s32
;
487 if (rv
< UBUS_STATUS_OK
|| rv
>= __UBUS_STATUS_LAST
)
488 rv
= UBUS_STATUS_UNKNOWN_ERROR
;
490 ubus_complete_deferred_request(ctx
, &callctx
->req
, rv
);
491 callctx
->replied
= true;
494 /* treat other exceptions as unknown error */
496 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_UNKNOWN_ERROR
);
497 callctx
->replied
= true;
501 /* release request object */
504 /* garbage collect */
507 return UBUS_STATUS_OK
;
510 static uc_function_t
*
511 rpc_ucode_script_compile(const char *path
, uc_source_t
*src
)
513 char *syntax_error
= NULL
;
514 uc_function_t
*progfunc
;
516 progfunc
= uc_compile(&config
, src
, &syntax_error
);
519 fprintf(stderr
, "Unable to compile ucode script %s: %s\n",
529 rpc_ucode_script_validate(rpc_ucode_script_t
*script
)
531 uc_value_t
*signature
= uc_vm_registry_get(&script
->vm
, "rpcd.ucode.signature");
532 uc_value_t
*args
, *func
;
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
));
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
));
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
);
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
));
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
));
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
));
580 ucv_object_foreach(args
, ubus_argument_name
, ubus_argument_typehint
) {
581 switch (ucv_type(ubus_argument_typehint
)) {
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
));
608 rpc_ucode_method_register(struct ubus_method
*method
, const char *ubus_method_name
, uc_value_t
*ubus_method_arguments
)
610 struct blobmsg_policy
*policy
;
611 enum blobmsg_type type
;
613 method
->name
= strdup(ubus_method_name
);
616 fprintf(stderr
, "Unable to allocate ubus method name: %s\n",
622 method
->policy
= calloc(ucv_object_length(ubus_method_arguments
), sizeof(*method
->policy
));
624 if (!method
->policy
) {
625 fprintf(stderr
, "Unable to allocate ubus method argument policy: %s\n",
631 method
->handler
= rpc_ucode_script_call
;
633 ucv_object_foreach(ubus_method_arguments
, ubus_argument_name
, ubus_argument_typehint
) {
634 switch (ucv_type(ubus_argument_typehint
)) {
636 type
= BLOBMSG_TYPE_INT8
;
640 switch (ucv_int64_get(ubus_argument_typehint
)) {
642 type
= BLOBMSG_TYPE_INT8
;
646 type
= BLOBMSG_TYPE_INT16
;
650 type
= BLOBMSG_TYPE_INT64
;
654 type
= BLOBMSG_TYPE_INT32
;
661 type
= BLOBMSG_TYPE_DOUBLE
;
665 type
= BLOBMSG_TYPE_ARRAY
;
669 type
= BLOBMSG_TYPE_TABLE
;
673 type
= BLOBMSG_TYPE_STRING
;
677 policy
= (struct blobmsg_policy
*)&method
->policy
[method
->n_policy
++];
680 policy
->name
= strdup(ubus_argument_name
);
683 fprintf(stderr
, "Unable to allocate ubus method argument name: %s\n",
694 rpc_ucode_script_register(struct ubus_context
*ctx
, rpc_ucode_script_t
*script
)
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
;
706 if (!rpc_ucode_script_validate(script
))
709 ucv_object_foreach(signature
, ubus_object_name
, ubus_object_methods
) {
710 namelen
= strlen(ubus_object_name
);
711 typelen
= strlen("rpcd-plugin-ucode-") + namelen
;
713 uuobj
= calloc_a(sizeof(*uuobj
),
715 &mptr
, ucv_object_length(ubus_object_methods
) * sizeof(struct ubus_method
),
716 &tptr
, sizeof(struct ubus_object_type
),
717 &tnptr
, typelen
+ 1);
720 fprintf(stderr
, "Unable to allocate ubus object signature: %s\n",
726 list_add(&uuobj
->list
, &uuobjs
);
728 uuobj
->script
= script
;
729 uuobj
->signature
= ubus_object_methods
;
731 snprintf(tnptr
, typelen
, "rpcd-plugin-ucode-%s", ubus_object_name
);
733 method
= (struct ubus_method
*)mptr
;
735 obj
= &uuobj
->ubusobj
;
736 obj
->name
= strncpy(onptr
, ubus_object_name
, namelen
);
737 obj
->methods
= method
;
739 obj
->type
= (struct ubus_object_type
*)tptr
;
740 obj
->type
->name
= tnptr
;
741 obj
->type
->methods
= obj
->methods
;
743 ucv_object_foreach(ubus_object_methods
, ubus_method_name
, ubus_method_definition
) {
744 args
= ucv_object_get(ubus_method_definition
, "args", NULL
);
746 if (!rpc_ucode_method_register(&method
[obj
->n_methods
++], ubus_method_name
, args
))
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
;
755 rv
= ubus_add_object(ctx
, obj
);
757 if (rv
!= UBUS_STATUS_OK
) {
758 fprintf(stderr
, "Unable to register ubus object %s: %s\n",
759 obj
->name
, ubus_strerror(rv
));
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
);
771 free((char *)method
->name
);
772 free((char *)method
->policy
);
782 rpc_ucode_request_reply(uc_vm_t
*vm
, size_t nargs
)
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
;
789 if (!callctx
|| !*callctx
) {
790 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
791 "Attempt to invoke reply() on invalid self");
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");
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");
808 if ((*callctx
)->replied
) {
809 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
810 "Reply has already been sent");
816 code
= ucv_int64_get(rcode
);
818 if (errno
== ERANGE
|| code
< 0 || code
> __UBUS_STATUS_LAST
)
819 code
= UBUS_STATUS_UNKNOWN_ERROR
;
822 rpc_ucode_request_finish(*callctx
, code
, reply
);
828 rpc_ucode_request_error(uc_vm_t
*vm
, size_t nargs
)
830 rpc_ucode_call_ctx_t
**callctx
= uc_fn_this("rpcd.ucode.request");
831 uc_value_t
*rcode
= uc_fn_arg(0);
834 if (!callctx
|| !*callctx
) {
835 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
836 "Attempt to invoke error() on invalid self");
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");
847 if ((*callctx
)->replied
) {
848 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
849 "Reply has already been sent");
855 code
= ucv_int64_get(rcode
);
857 if (errno
== ERANGE
|| code
< 0 || code
> __UBUS_STATUS_LAST
)
858 code
= UBUS_STATUS_UNKNOWN_ERROR
;
860 rpc_ucode_request_finish(*callctx
, code
, NULL
);
865 static const uc_function_list_t rpc_ucode_request_fns
[] = {
866 { "reply", rpc_ucode_request_reply
},
867 { "error", rpc_ucode_request_error
},
871 rpc_ucode_request_gc(void *ud
)
873 rpc_ucode_call_ctx_t
*callctx
= ud
;
875 uloop_timeout_cancel(&callctx
->timeout
);
880 rpc_ucode_init_globals(rpc_ucode_script_t
*script
)
882 uc_vm_t
*vm
= &script
->vm
;
883 uc_value_t
*scope
= uc_vm_scope_get(vm
);
885 #define status_const(name) \
886 ucv_object_add(scope, #name, ucv_uint64_new(name))
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
);
902 uc_stdlib_load(scope
);
904 script
->requesttype
= uc_type_declare(vm
, "rpcd.ucode.request",
905 rpc_ucode_request_fns
, rpc_ucode_request_gc
);
908 static rpc_ucode_script_t
*
909 rpc_ucode_script_execute(struct ubus_context
*ctx
, const char *path
, uc_function_t
*func
)
911 rpc_ucode_script_t
*script
;
912 uc_value_t
*signature
;
913 uc_vm_status_t status
;
917 pathlen
= strlen(path
);
918 script
= calloc_a(sizeof(*script
), &pptr
, pathlen
+ 1);
921 fprintf(stderr
, "Unable to allocate context for ucode script %s: %s\n",
922 path
, strerror(errno
));
924 ucv_put(&func
->header
);
929 script
->path
= strncpy(pptr
, path
, pathlen
);
931 uc_vm_init(&script
->vm
, &config
);
932 rpc_ucode_init_globals(script
);
934 status
= uc_vm_execute(&script
->vm
, func
, &signature
);
936 script
->pending_replies
= ucv_array_new(&script
->vm
);
938 uc_vm_registry_set(&script
->vm
, "rpcd.ucode.signature", signature
);
939 uc_vm_registry_set(&script
->vm
, "rpcd.ucode.deferreds", script
->pending_replies
);
945 if (rpc_ucode_script_register(ctx
, script
))
948 fprintf(stderr
, "Skipping registration of ucode script %s\n", path
);
952 fprintf(stderr
, "The ucode script %s invoked exit(%" PRId64
")\n",
953 path
, ucv_int64_get(signature
));
957 fprintf(stderr
, "Compilation error while executing ucode script %s\n", path
);
961 fprintf(stderr
, "Runtime error while executing ucode script %s\n", path
);
965 uc_vm_free(&script
->vm
);
972 rpc_ucode_init_script(struct ubus_context
*ctx
, const char *path
)
974 rpc_ucode_script_t
*script
;
975 uc_function_t
*progfunc
;
978 src
= uc_source_new_file(path
);
981 fprintf(stderr
, "Unable to open ucode script %s: %s\n",
982 path
, strerror(errno
));
984 return UBUS_STATUS_UNKNOWN_ERROR
;
987 progfunc
= rpc_ucode_script_compile(path
, src
);
990 return UBUS_STATUS_UNKNOWN_ERROR
;
992 script
= rpc_ucode_script_execute(ctx
, path
, progfunc
);
995 return UBUS_STATUS_UNKNOWN_ERROR
;
997 list_add(&script
->list
, &scripts
);
999 return UBUS_STATUS_OK
;
1003 rpc_ucode_api_init(const struct rpc_daemon_ops
*ops
, struct ubus_context
*ctx
)
1005 char path
[PATH_MAX
];
1011 request_timeout
= *ops
->exec_timeout
;
1013 /* reopen ucode.so with RTLD_GLOBAL in order to export libucode runtime
1014 * symbols for ucode extensions loaded later at runtime */
1015 if (!dlopen(RPC_LIBRARY_DIRECTORY
"/ucode.so", RTLD_LAZY
|RTLD_GLOBAL
)) {
1016 fprintf(stderr
, "Failed to dlopen() ucode.so: %s, dynamic ucode plugins may fail\n",
1019 if ((d
= opendir(RPC_UCSCRIPT_DIRECTORY
)) != NULL
) {
1020 while ((e
= readdir(d
)) != NULL
) {
1021 snprintf(path
, sizeof(path
), RPC_UCSCRIPT_DIRECTORY
"/%s", e
->d_name
);
1023 if (stat(path
, &s
) || !S_ISREG(s
.st_mode
))
1026 if (s
.st_mode
& S_IWOTH
) {
1027 fprintf(stderr
, "Ignoring ucode script %s because it is world writable\n",
1033 rv
|= rpc_ucode_init_script(ctx
, path
);
1042 struct rpc_plugin rpc_plugin
= {
1043 .init
= rpc_ucode_api_init