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
;
407 rv
= rpc_ucode_validate_call_args(obj
, ubus_method_name
, msg
, &args
);
409 if (rv
!= UBUS_STATUS_OK
)
412 func
= ucv_object_get(
413 ucv_object_get(rpc_ucode_obj_to_signature(obj
), ubus_method_name
, NULL
),
417 if (!ucv_is_callable(func
))
418 return UBUS_STATUS_METHOD_NOT_FOUND
;
420 /* allocate deferred method call context */
421 callctx
= calloc(1, sizeof(*callctx
));
424 return UBUS_STATUS_UNKNOWN_ERROR
;
427 callctx
->script
= script
;
429 ubus_defer_request(ctx
, req
, &callctx
->req
);
431 /* create ucode request type object and set properties */
432 reqobj
= uc_resource_new(script
->requesttype
, callctx
);
433 reqproto
= ucv_object_new(&script
->vm
);
435 ucv_object_add(reqproto
, "args", args
);
436 ucv_object_add(reqproto
, "info",
437 rpc_ucode_gather_call_info(&script
->vm
, ctx
, req
, obj
, ubus_method_name
));
439 ucv_prototype_set(ucv_prototype_get(reqobj
), reqproto
);
441 /* push handler and request object onto stack */
442 uc_vm_stack_push(&script
->vm
, ucv_get(func
));
443 uc_vm_stack_push(&script
->vm
, ucv_get(reqobj
));
445 /* execute request handler function */
446 switch (uc_vm_call(&script
->vm
, false, 1)) {
448 res
= uc_vm_stack_pop(&script
->vm
);
450 /* The handler function invoked a nested aync ubus request and returned it */
451 if (ucv_resource_dataptr(res
, "ubus.deferred")) {
452 /* Install guard timer in case the reply callback is never called */
453 callctx
->timeout
.cb
= rpc_ucode_request_timeout
;
454 uloop_timeout_set(&callctx
->timeout
, request_timeout
);
456 /* Add wrapped request context into registry to prevent GC'ing
457 * until reply or timeout occurred */
459 if (ucv_array_get(script
->pending_replies
, i
) == NULL
) {
460 ucv_array_set(script
->pending_replies
, i
, ucv_get(reqobj
));
466 /* Otherwise, when the function returned an object, treat it as
467 * reply data and conclude deferred request immediately */
468 else if (ucv_type(res
) == UC_OBJECT
) {
469 blob_buf_init(&buf
, 0);
470 rpc_ucode_ucv_object_to_blob(res
, &buf
);
471 ubus_send_reply(ctx
, &callctx
->req
, buf
.head
);
473 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_OK
);
474 callctx
->replied
= true;
477 /* If neither a deferred ubus request, nor a plain object were
478 * returned and if reqobj.reply() hasn't been called, immediately
479 * finish deferred request with UBUS_STATUS_NO_DATA. The */
480 else if (!callctx
->replied
) {
481 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_NO_DATA
);
482 callctx
->replied
= true;
488 /* if the handler function invoked exit(), forward exit status as ubus
489 * return code, map out of range values to UBUS_STATUS_UNKNOWN_ERROR. */
491 rv
= script
->vm
.arg
.s32
;
493 if (rv
< UBUS_STATUS_OK
|| rv
>= __UBUS_STATUS_LAST
)
494 rv
= UBUS_STATUS_UNKNOWN_ERROR
;
496 ubus_complete_deferred_request(ctx
, &callctx
->req
, rv
);
497 callctx
->replied
= true;
500 /* treat other exceptions as unknown error */
502 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_UNKNOWN_ERROR
);
503 callctx
->replied
= true;
507 /* release request object */
510 /* garbage collect */
513 return UBUS_STATUS_OK
;
516 static uc_program_t
*
517 rpc_ucode_script_compile(const char *path
, uc_source_t
*src
)
519 char *syntax_error
= NULL
;
522 prog
= uc_compile(&config
, src
, &syntax_error
);
525 fprintf(stderr
, "Unable to compile ucode script %s: %s\n",
535 rpc_ucode_script_validate(rpc_ucode_script_t
*script
)
537 uc_value_t
*signature
= uc_vm_registry_get(&script
->vm
, "rpcd.ucode.signature");
538 uc_value_t
*args
, *func
;
540 if (ucv_type(signature
) != UC_OBJECT
) {
541 fprintf(stderr
, "Invalid object signature for ucode script %s"
542 " - expected dictionary, got %s\n",
543 script
->path
, ucv_typename(signature
));
548 ucv_object_foreach(signature
, ubus_object_name
, ubus_object_methods
) {
549 if (ucv_type(ubus_object_methods
) != UC_OBJECT
) {
550 fprintf(stderr
, "Invalid method signature for ucode script %s, object %s"
551 " - expected dictionary, got %s\n",
552 script
->path
, ubus_object_name
, ucv_typename(ubus_object_methods
));
557 ucv_object_foreach(ubus_object_methods
, ubus_method_name
, ubus_method_definition
) {
558 func
= ucv_object_get(ubus_method_definition
, "call", NULL
);
559 args
= ucv_object_get(ubus_method_definition
, "args", NULL
);
561 if (ucv_type(ubus_method_definition
) != UC_OBJECT
) {
562 fprintf(stderr
, "Invalid method definition for ucode script %s, object %s, method %s"
563 " - expected dictionary, got %s\n",
564 script
->path
, ubus_object_name
, ubus_method_name
, ucv_typename(ubus_method_definition
));
569 if (!ucv_is_callable(func
)) {
570 fprintf(stderr
, "Invalid method callback for ucode script %s, object %s, method %s"
571 " - expected callable, got %s\n",
572 script
->path
, ubus_object_name
, ubus_method_name
, ucv_typename(func
));
578 if (ucv_type(args
) != UC_OBJECT
) {
579 fprintf(stderr
, "Invalid method argument definition for ucode script %s, "
580 "object %s, method %s - expected dictionary, got %s\n",
581 script
->path
, ubus_object_name
, ubus_method_name
, ucv_typename(args
));
586 ucv_object_foreach(args
, ubus_argument_name
, ubus_argument_typehint
) {
587 switch (ucv_type(ubus_argument_typehint
)) {
597 fprintf(stderr
, "Unsupported argument type for ucode script %s, object %s, "
598 "method %s, argument %s - expected boolean, integer, string, "
599 "array or object, got %s\n",
600 script
->path
, ubus_object_name
, ubus_method_name
, ubus_argument_name
,
601 ucv_typename(ubus_argument_typehint
));
614 rpc_ucode_method_register(struct ubus_method
*method
, const char *ubus_method_name
, uc_value_t
*ubus_method_arguments
)
616 struct blobmsg_policy
*policy
;
617 enum blobmsg_type type
;
619 method
->name
= strdup(ubus_method_name
);
622 fprintf(stderr
, "Unable to allocate ubus method name: %s\n",
628 method
->policy
= calloc(ucv_object_length(ubus_method_arguments
), sizeof(*method
->policy
));
630 if (!method
->policy
) {
631 fprintf(stderr
, "Unable to allocate ubus method argument policy: %s\n",
637 method
->handler
= rpc_ucode_script_call
;
639 ucv_object_foreach(ubus_method_arguments
, ubus_argument_name
, ubus_argument_typehint
) {
640 switch (ucv_type(ubus_argument_typehint
)) {
642 type
= BLOBMSG_TYPE_INT8
;
646 switch (ucv_int64_get(ubus_argument_typehint
)) {
648 type
= BLOBMSG_TYPE_INT8
;
652 type
= BLOBMSG_TYPE_INT16
;
656 type
= BLOBMSG_TYPE_INT64
;
660 type
= BLOBMSG_TYPE_INT32
;
667 type
= BLOBMSG_TYPE_DOUBLE
;
671 type
= BLOBMSG_TYPE_ARRAY
;
675 type
= BLOBMSG_TYPE_TABLE
;
679 type
= BLOBMSG_TYPE_STRING
;
683 policy
= (struct blobmsg_policy
*)&method
->policy
[method
->n_policy
++];
686 policy
->name
= strdup(ubus_argument_name
);
689 fprintf(stderr
, "Unable to allocate ubus method argument name: %s\n",
700 rpc_ucode_script_register(struct ubus_context
*ctx
, rpc_ucode_script_t
*script
)
702 uc_value_t
*signature
= uc_vm_registry_get(&script
->vm
, "rpcd.ucode.signature");
703 const struct blobmsg_policy
*policy
;
704 rpc_ucode_ubus_obj_t
*uuobj
= NULL
;
705 char *tptr
, *tnptr
, *onptr
, *mptr
;
706 struct ubus_method
*method
;
707 struct ubus_object
*obj
;
708 size_t typelen
, namelen
;
712 if (!rpc_ucode_script_validate(script
))
715 ucv_object_foreach(signature
, ubus_object_name
, ubus_object_methods
) {
716 namelen
= strlen(ubus_object_name
);
717 typelen
= strlen("rpcd-plugin-ucode-") + namelen
;
719 uuobj
= calloc_a(sizeof(*uuobj
),
721 &mptr
, ucv_object_length(ubus_object_methods
) * sizeof(struct ubus_method
),
722 &tptr
, sizeof(struct ubus_object_type
),
723 &tnptr
, typelen
+ 1);
726 fprintf(stderr
, "Unable to allocate ubus object signature: %s\n",
732 list_add(&uuobj
->list
, &uuobjs
);
734 uuobj
->script
= script
;
735 uuobj
->signature
= ubus_object_methods
;
737 snprintf(tnptr
, typelen
, "rpcd-plugin-ucode-%s", ubus_object_name
);
739 method
= (struct ubus_method
*)mptr
;
741 obj
= &uuobj
->ubusobj
;
742 obj
->name
= strncpy(onptr
, ubus_object_name
, namelen
);
743 obj
->methods
= method
;
745 obj
->type
= (struct ubus_object_type
*)tptr
;
746 obj
->type
->name
= tnptr
;
747 obj
->type
->methods
= obj
->methods
;
749 ucv_object_foreach(ubus_object_methods
, ubus_method_name
, ubus_method_definition
) {
750 args
= ucv_object_get(ubus_method_definition
, "args", NULL
);
752 if (!rpc_ucode_method_register(&method
[obj
->n_methods
++], ubus_method_name
, args
))
756 obj
->type
= (struct ubus_object_type
*)tptr
;
757 obj
->type
->name
= tnptr
;
758 obj
->type
->methods
= obj
->methods
;
759 obj
->type
->n_methods
= obj
->n_methods
;
761 rv
= ubus_add_object(ctx
, obj
);
763 if (rv
!= UBUS_STATUS_OK
) {
764 fprintf(stderr
, "Unable to register ubus object %s: %s\n",
765 obj
->name
, ubus_strerror(rv
));
773 for (; obj
->n_methods
> 0; method
++, obj
->n_methods
--) {
774 for (policy
= method
->policy
; method
->n_policy
> 0; policy
++, method
->n_policy
--)
775 free((char *)policy
->name
);
777 free((char *)method
->name
);
778 free((char *)method
->policy
);
788 rpc_ucode_request_reply(uc_vm_t
*vm
, size_t nargs
)
790 rpc_ucode_call_ctx_t
**callctx
= uc_fn_this("rpcd.ucode.request");
791 uc_value_t
*reply
= uc_fn_arg(0);
792 uc_value_t
*rcode
= uc_fn_arg(1);
793 int64_t code
= UBUS_STATUS_OK
;
795 if (!callctx
|| !*callctx
) {
796 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
797 "Attempt to invoke reply() on invalid self");
801 else if (reply
&& ucv_type(reply
) != UC_OBJECT
) {
802 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
803 "First argument to reply() must be null or an object");
807 else if (rcode
&& ucv_type(rcode
) != UC_INTEGER
) {
808 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
809 "Second argument to reply() must be null or an integer");
814 if ((*callctx
)->replied
) {
815 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
816 "Reply has already been sent");
822 code
= ucv_int64_get(rcode
);
824 if (errno
== ERANGE
|| code
< 0 || code
> __UBUS_STATUS_LAST
)
825 code
= UBUS_STATUS_UNKNOWN_ERROR
;
828 rpc_ucode_request_finish(*callctx
, code
, reply
);
834 rpc_ucode_request_error(uc_vm_t
*vm
, size_t nargs
)
836 rpc_ucode_call_ctx_t
**callctx
= uc_fn_this("rpcd.ucode.request");
837 uc_value_t
*rcode
= uc_fn_arg(0);
840 if (!callctx
|| !*callctx
) {
841 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
842 "Attempt to invoke error() on invalid self");
846 else if (ucv_type(rcode
) != UC_INTEGER
) {
847 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
848 "First argument to error() must be an integer");
853 if ((*callctx
)->replied
) {
854 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
855 "Reply has already been sent");
861 code
= ucv_int64_get(rcode
);
863 if (errno
== ERANGE
|| code
< 0 || code
> __UBUS_STATUS_LAST
)
864 code
= UBUS_STATUS_UNKNOWN_ERROR
;
866 rpc_ucode_request_finish(*callctx
, code
, NULL
);
871 static const uc_function_list_t rpc_ucode_request_fns
[] = {
872 { "reply", rpc_ucode_request_reply
},
873 { "error", rpc_ucode_request_error
},
877 rpc_ucode_request_gc(void *ud
)
879 rpc_ucode_call_ctx_t
*callctx
= ud
;
881 uloop_timeout_cancel(&callctx
->timeout
);
886 rpc_ucode_init_globals(rpc_ucode_script_t
*script
)
888 uc_vm_t
*vm
= &script
->vm
;
889 uc_value_t
*scope
= uc_vm_scope_get(vm
);
891 #define status_const(name) \
892 ucv_object_add(scope, #name, ucv_uint64_new(name))
894 status_const(UBUS_STATUS_OK
);
895 status_const(UBUS_STATUS_INVALID_COMMAND
);
896 status_const(UBUS_STATUS_INVALID_ARGUMENT
);
897 status_const(UBUS_STATUS_METHOD_NOT_FOUND
);
898 status_const(UBUS_STATUS_NOT_FOUND
);
899 status_const(UBUS_STATUS_NO_DATA
);
900 status_const(UBUS_STATUS_PERMISSION_DENIED
);
901 status_const(UBUS_STATUS_TIMEOUT
);
902 status_const(UBUS_STATUS_NOT_SUPPORTED
);
903 status_const(UBUS_STATUS_UNKNOWN_ERROR
);
904 status_const(UBUS_STATUS_CONNECTION_FAILED
);
908 uc_stdlib_load(scope
);
910 script
->requesttype
= uc_type_declare(vm
, "rpcd.ucode.request",
911 rpc_ucode_request_fns
, rpc_ucode_request_gc
);
914 static rpc_ucode_script_t
*
915 rpc_ucode_script_execute(struct ubus_context
*ctx
, const char *path
, uc_program_t
*prog
)
917 rpc_ucode_script_t
*script
;
918 uc_value_t
*signature
;
919 uc_vm_status_t status
;
923 pathlen
= strlen(path
);
924 script
= calloc_a(sizeof(*script
), &pptr
, pathlen
+ 1);
927 fprintf(stderr
, "Unable to allocate context for ucode script %s: %s\n",
928 path
, strerror(errno
));
930 uc_program_put(prog
);
935 script
->path
= strncpy(pptr
, path
, pathlen
);
937 uc_vm_init(&script
->vm
, &config
);
938 rpc_ucode_init_globals(script
);
940 status
= uc_vm_execute(&script
->vm
, prog
, &signature
);
942 script
->pending_replies
= ucv_array_new(&script
->vm
);
944 uc_vm_registry_set(&script
->vm
, "rpcd.ucode.signature", signature
);
945 uc_vm_registry_set(&script
->vm
, "rpcd.ucode.deferreds", script
->pending_replies
);
947 uc_program_put(prog
);
952 if (rpc_ucode_script_register(ctx
, script
))
955 fprintf(stderr
, "Skipping registration of ucode script %s\n", path
);
959 fprintf(stderr
, "The ucode script %s invoked exit(%" PRId64
")\n",
960 path
, ucv_int64_get(signature
));
964 fprintf(stderr
, "Compilation error while executing ucode script %s\n", path
);
968 fprintf(stderr
, "Runtime error while executing ucode script %s\n", path
);
972 uc_vm_free(&script
->vm
);
979 rpc_ucode_init_script(struct ubus_context
*ctx
, const char *path
)
981 rpc_ucode_script_t
*script
;
985 src
= uc_source_new_file(path
);
988 fprintf(stderr
, "Unable to open ucode script %s: %s\n",
989 path
, strerror(errno
));
991 return UBUS_STATUS_UNKNOWN_ERROR
;
994 prog
= rpc_ucode_script_compile(path
, src
);
997 return UBUS_STATUS_UNKNOWN_ERROR
;
999 script
= rpc_ucode_script_execute(ctx
, path
, prog
);
1002 return UBUS_STATUS_UNKNOWN_ERROR
;
1004 list_add(&script
->list
, &scripts
);
1006 return UBUS_STATUS_OK
;
1010 rpc_ucode_api_init(const struct rpc_daemon_ops
*ops
, struct ubus_context
*ctx
)
1012 char path
[PATH_MAX
];
1018 request_timeout
= *ops
->exec_timeout
;
1020 /* reopen ucode.so with RTLD_GLOBAL in order to export libucode runtime
1021 * symbols for ucode extensions loaded later at runtime */
1022 if (!dlopen(RPC_LIBRARY_DIRECTORY
"/ucode.so", RTLD_LAZY
|RTLD_GLOBAL
)) {
1023 fprintf(stderr
, "Failed to dlopen() ucode.so: %s, dynamic ucode plugins may fail\n",
1027 /* initialize default module search path */
1028 uc_search_path_init(&config
.module_search_path
);
1030 if ((d
= opendir(RPC_UCSCRIPT_DIRECTORY
)) != NULL
) {
1031 while ((e
= readdir(d
)) != NULL
) {
1032 snprintf(path
, sizeof(path
), RPC_UCSCRIPT_DIRECTORY
"/%s", e
->d_name
);
1034 if (stat(path
, &s
) || !S_ISREG(s
.st_mode
))
1037 if (s
.st_mode
& S_IWOTH
) {
1038 fprintf(stderr
, "Ignoring ucode script %s because it is world writable\n",
1044 rv
|= rpc_ucode_init_script(ctx
, path
);
1053 struct rpc_plugin rpc_plugin
= {
1054 .init
= rpc_ucode_api_init