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 */
311 *res
= rpc_ucode_blob_array_to_ucv(&script
->vm
, blob_data(msg
), blob_len(msg
), true);
313 return UBUS_STATUS_OK
;
318 return UBUS_STATUS_INVALID_ARGUMENT
;
322 rpc_ucode_gather_call_info(uc_vm_t
*vm
,
323 struct ubus_context
*ctx
, struct ubus_request_data
*req
,
324 struct ubus_object
*obj
, const char *ubus_method_name
)
326 uc_value_t
*info
, *o
;
328 info
= ucv_object_new(vm
);
330 o
= ucv_object_new(vm
);
332 ucv_object_add(o
, "user", ucv_string_new(req
->acl
.user
));
333 ucv_object_add(o
, "group", ucv_string_new(req
->acl
.group
));
334 ucv_object_add(o
, "object", ucv_string_new(req
->acl
.object
));
336 ucv_object_add(info
, "acl", o
);
338 o
= ucv_object_new(vm
);
340 ucv_object_add(o
, "id", ucv_uint64_new(obj
->id
));
341 ucv_object_add(o
, "name", ucv_string_new(obj
->name
));
344 ucv_object_add(o
, "path", ucv_string_new(obj
->path
));
346 ucv_object_add(info
, "object", o
);
348 ucv_object_add(info
, "method", ucv_string_new(ubus_method_name
));
354 rpc_ucode_request_finish(rpc_ucode_call_ctx_t
*callctx
, int code
, uc_value_t
*reply
)
356 rpc_ucode_script_t
*script
= callctx
->script
;
360 if (callctx
->replied
)
364 blob_buf_init(&buf
, 0);
365 rpc_ucode_ucv_object_to_blob(reply
, &buf
);
366 ubus_send_reply(callctx
->ubus
, &callctx
->req
, buf
.head
);
369 ubus_complete_deferred_request(callctx
->ubus
, &callctx
->req
, code
);
371 callctx
->replied
= true;
373 for (i
= 0; i
< ucv_array_length(script
->pending_replies
); i
++) {
374 r
= (uc_resource_t
*)ucv_array_get(script
->pending_replies
, i
);
376 if (r
&& r
->data
== callctx
) {
377 ucv_array_set(script
->pending_replies
, i
, NULL
);
384 rpc_ucode_request_timeout(struct uloop_timeout
*timeout
)
386 rpc_ucode_call_ctx_t
*callctx
= container_of(timeout
, rpc_ucode_call_ctx_t
, timeout
);
388 rpc_ucode_request_finish(callctx
, UBUS_STATUS_TIMEOUT
, NULL
);
392 rpc_ucode_script_call(struct ubus_context
*ctx
, struct ubus_object
*obj
,
393 struct ubus_request_data
*req
, const char *ubus_method_name
,
394 struct blob_attr
*msg
)
396 rpc_ucode_script_t
*script
= rpc_ucode_obj_to_script(obj
);
397 uc_value_t
*func
, *args
= NULL
, *reqobj
, *reqproto
, *res
;
398 rpc_ucode_call_ctx_t
*callctx
;
402 rv
= rpc_ucode_validate_call_args(obj
, ubus_method_name
, msg
, &args
);
404 if (rv
!= UBUS_STATUS_OK
)
407 func
= ucv_object_get(
408 ucv_object_get(rpc_ucode_obj_to_signature(obj
), ubus_method_name
, NULL
),
412 if (!ucv_is_callable(func
))
413 return UBUS_STATUS_METHOD_NOT_FOUND
;
415 /* allocate deferred method call context */
416 callctx
= calloc(1, sizeof(*callctx
));
419 return UBUS_STATUS_UNKNOWN_ERROR
;
422 callctx
->script
= script
;
424 ubus_defer_request(ctx
, req
, &callctx
->req
);
426 /* create ucode request type object and set properties */
427 reqobj
= uc_resource_new(script
->requesttype
, callctx
);
428 reqproto
= ucv_object_new(&script
->vm
);
430 ucv_object_add(reqproto
, "args", args
);
431 ucv_object_add(reqproto
, "info",
432 rpc_ucode_gather_call_info(&script
->vm
, ctx
, req
, obj
, ubus_method_name
));
434 ucv_prototype_set(ucv_prototype_get(reqobj
), reqproto
);
436 /* push handler and request object onto stack */
437 uc_vm_stack_push(&script
->vm
, ucv_get(func
));
438 uc_vm_stack_push(&script
->vm
, ucv_get(reqobj
));
440 /* execute request handler function */
441 switch (uc_vm_call(&script
->vm
, false, 1)) {
443 res
= uc_vm_stack_pop(&script
->vm
);
445 /* The handler function invoked a nested aync ubus request and returned it */
446 if (ucv_resource_dataptr(res
, "ubus.deferred")) {
447 /* Install guard timer in case the reply callback is never called */
448 callctx
->timeout
.cb
= rpc_ucode_request_timeout
;
449 uloop_timeout_set(&callctx
->timeout
, request_timeout
);
451 /* Add wrapped request context into registry to prevent GC'ing
452 * until reply or timeout occurred */
454 if (ucv_array_get(script
->pending_replies
, i
) == NULL
) {
455 ucv_array_set(script
->pending_replies
, i
, ucv_get(reqobj
));
461 /* Otherwise, when the function returned an object, treat it as
462 * reply data and conclude deferred request immediately */
463 else if (ucv_type(res
) == UC_OBJECT
) {
464 blob_buf_init(&buf
, 0);
465 rpc_ucode_ucv_object_to_blob(res
, &buf
);
466 ubus_send_reply(ctx
, &callctx
->req
, buf
.head
);
468 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_OK
);
469 callctx
->replied
= true;
472 /* If neither a deferred ubus request, nor a plain object were
473 * returned and if reqobj.reply() hasn't been called, immediately
474 * finish deferred request with UBUS_STATUS_NO_DATA. The */
475 else if (!callctx
->replied
) {
476 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_NO_DATA
);
477 callctx
->replied
= true;
483 /* if the handler function invoked exit(), forward exit status as ubus
484 * return code, map out of range values to UBUS_STATUS_UNKNOWN_ERROR. */
486 rv
= script
->vm
.arg
.s32
;
488 if (rv
< UBUS_STATUS_OK
|| rv
>= __UBUS_STATUS_LAST
)
489 rv
= UBUS_STATUS_UNKNOWN_ERROR
;
491 ubus_complete_deferred_request(ctx
, &callctx
->req
, rv
);
492 callctx
->replied
= true;
495 /* treat other exceptions as unknown error */
497 ubus_complete_deferred_request(ctx
, &callctx
->req
, UBUS_STATUS_UNKNOWN_ERROR
);
498 callctx
->replied
= true;
502 /* release request object */
505 /* garbage collect */
508 return UBUS_STATUS_OK
;
511 static uc_program_t
*
512 rpc_ucode_script_compile(const char *path
, uc_source_t
*src
)
514 char *syntax_error
= NULL
;
517 prog
= uc_compile(&config
, src
, &syntax_error
);
520 fprintf(stderr
, "Unable to compile ucode script %s: %s\n",
530 rpc_ucode_script_validate(rpc_ucode_script_t
*script
)
532 uc_value_t
*signature
= uc_vm_registry_get(&script
->vm
, "rpcd.ucode.signature");
533 uc_value_t
*args
, *func
;
535 if (ucv_type(signature
) != UC_OBJECT
) {
536 fprintf(stderr
, "Invalid object signature for ucode script %s"
537 " - expected dictionary, got %s\n",
538 script
->path
, ucv_typename(signature
));
543 ucv_object_foreach(signature
, ubus_object_name
, ubus_object_methods
) {
544 if (ucv_type(ubus_object_methods
) != UC_OBJECT
) {
545 fprintf(stderr
, "Invalid method signature for ucode script %s, object %s"
546 " - expected dictionary, got %s\n",
547 script
->path
, ubus_object_name
, ucv_typename(ubus_object_methods
));
552 ucv_object_foreach(ubus_object_methods
, ubus_method_name
, ubus_method_definition
) {
553 func
= ucv_object_get(ubus_method_definition
, "call", NULL
);
554 args
= ucv_object_get(ubus_method_definition
, "args", NULL
);
556 if (ucv_type(ubus_method_definition
) != UC_OBJECT
) {
557 fprintf(stderr
, "Invalid method definition for ucode script %s, object %s, method %s"
558 " - expected dictionary, got %s\n",
559 script
->path
, ubus_object_name
, ubus_method_name
, ucv_typename(ubus_method_definition
));
564 if (!ucv_is_callable(func
)) {
565 fprintf(stderr
, "Invalid method callback for ucode script %s, object %s, method %s"
566 " - expected callable, got %s\n",
567 script
->path
, ubus_object_name
, ubus_method_name
, ucv_typename(func
));
573 if (ucv_type(args
) != UC_OBJECT
) {
574 fprintf(stderr
, "Invalid method argument definition for ucode script %s, "
575 "object %s, method %s - expected dictionary, got %s\n",
576 script
->path
, ubus_object_name
, ubus_method_name
, ucv_typename(args
));
581 ucv_object_foreach(args
, ubus_argument_name
, ubus_argument_typehint
) {
582 switch (ucv_type(ubus_argument_typehint
)) {
592 fprintf(stderr
, "Unsupported argument type for ucode script %s, object %s, "
593 "method %s, argument %s - expected boolean, integer, string, "
594 "array or object, got %s\n",
595 script
->path
, ubus_object_name
, ubus_method_name
, ubus_argument_name
,
596 ucv_typename(ubus_argument_typehint
));
609 rpc_ucode_method_register(struct ubus_method
*method
, const char *ubus_method_name
, uc_value_t
*ubus_method_arguments
)
611 struct blobmsg_policy
*policy
;
612 enum blobmsg_type type
;
614 method
->name
= strdup(ubus_method_name
);
617 fprintf(stderr
, "Unable to allocate ubus method name: %s\n",
623 method
->policy
= calloc(ucv_object_length(ubus_method_arguments
), sizeof(*method
->policy
));
625 if (!method
->policy
) {
626 fprintf(stderr
, "Unable to allocate ubus method argument policy: %s\n",
632 method
->handler
= rpc_ucode_script_call
;
634 ucv_object_foreach(ubus_method_arguments
, ubus_argument_name
, ubus_argument_typehint
) {
635 switch (ucv_type(ubus_argument_typehint
)) {
637 type
= BLOBMSG_TYPE_INT8
;
641 switch (ucv_int64_get(ubus_argument_typehint
)) {
643 type
= BLOBMSG_TYPE_INT8
;
647 type
= BLOBMSG_TYPE_INT16
;
651 type
= BLOBMSG_TYPE_INT64
;
655 type
= BLOBMSG_TYPE_INT32
;
662 type
= BLOBMSG_TYPE_DOUBLE
;
666 type
= BLOBMSG_TYPE_ARRAY
;
670 type
= BLOBMSG_TYPE_TABLE
;
674 type
= BLOBMSG_TYPE_STRING
;
678 policy
= (struct blobmsg_policy
*)&method
->policy
[method
->n_policy
++];
681 policy
->name
= strdup(ubus_argument_name
);
684 fprintf(stderr
, "Unable to allocate ubus method argument name: %s\n",
695 rpc_ucode_script_register(struct ubus_context
*ctx
, rpc_ucode_script_t
*script
)
697 uc_value_t
*signature
= uc_vm_registry_get(&script
->vm
, "rpcd.ucode.signature");
698 const struct blobmsg_policy
*policy
;
699 rpc_ucode_ubus_obj_t
*uuobj
= NULL
;
700 char *tptr
, *tnptr
, *onptr
, *mptr
;
701 struct ubus_method
*method
;
702 struct ubus_object
*obj
;
703 size_t typelen
, namelen
;
707 if (!rpc_ucode_script_validate(script
))
710 ucv_object_foreach(signature
, ubus_object_name
, ubus_object_methods
) {
711 namelen
= strlen(ubus_object_name
);
712 typelen
= strlen("rpcd-plugin-ucode-") + namelen
;
714 uuobj
= calloc_a(sizeof(*uuobj
),
716 &mptr
, ucv_object_length(ubus_object_methods
) * sizeof(struct ubus_method
),
717 &tptr
, sizeof(struct ubus_object_type
),
718 &tnptr
, typelen
+ 1);
721 fprintf(stderr
, "Unable to allocate ubus object signature: %s\n",
727 list_add(&uuobj
->list
, &uuobjs
);
729 uuobj
->script
= script
;
730 uuobj
->signature
= ubus_object_methods
;
732 snprintf(tnptr
, typelen
, "rpcd-plugin-ucode-%s", ubus_object_name
);
734 method
= (struct ubus_method
*)mptr
;
736 obj
= &uuobj
->ubusobj
;
737 obj
->name
= strncpy(onptr
, ubus_object_name
, namelen
);
738 obj
->methods
= method
;
740 obj
->type
= (struct ubus_object_type
*)tptr
;
741 obj
->type
->name
= tnptr
;
742 obj
->type
->methods
= obj
->methods
;
744 ucv_object_foreach(ubus_object_methods
, ubus_method_name
, ubus_method_definition
) {
745 args
= ucv_object_get(ubus_method_definition
, "args", NULL
);
747 if (!rpc_ucode_method_register(&method
[obj
->n_methods
++], ubus_method_name
, args
))
751 obj
->type
= (struct ubus_object_type
*)tptr
;
752 obj
->type
->name
= tnptr
;
753 obj
->type
->methods
= obj
->methods
;
754 obj
->type
->n_methods
= obj
->n_methods
;
756 rv
= ubus_add_object(ctx
, obj
);
758 if (rv
!= UBUS_STATUS_OK
) {
759 fprintf(stderr
, "Unable to register ubus object %s: %s\n",
760 obj
->name
, ubus_strerror(rv
));
768 for (; obj
->n_methods
> 0; method
++, obj
->n_methods
--) {
769 for (policy
= method
->policy
; method
->n_policy
> 0; policy
++, method
->n_policy
--)
770 free((char *)policy
->name
);
772 free((char *)method
->name
);
773 free((char *)method
->policy
);
783 rpc_ucode_request_reply(uc_vm_t
*vm
, size_t nargs
)
785 rpc_ucode_call_ctx_t
**callctx
= uc_fn_this("rpcd.ucode.request");
786 uc_value_t
*reply
= uc_fn_arg(0);
787 uc_value_t
*rcode
= uc_fn_arg(1);
788 int64_t code
= UBUS_STATUS_OK
;
790 if (!callctx
|| !*callctx
) {
791 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
792 "Attempt to invoke reply() on invalid self");
796 else if (reply
&& ucv_type(reply
) != UC_OBJECT
) {
797 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
798 "First argument to reply() must be null or an object");
802 else if (rcode
&& ucv_type(rcode
) != UC_INTEGER
) {
803 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
804 "Second argument to reply() must be null or an integer");
809 if ((*callctx
)->replied
) {
810 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
811 "Reply has already been sent");
817 code
= ucv_int64_get(rcode
);
819 if (errno
== ERANGE
|| code
< 0 || code
> __UBUS_STATUS_LAST
)
820 code
= UBUS_STATUS_UNKNOWN_ERROR
;
823 rpc_ucode_request_finish(*callctx
, code
, reply
);
829 rpc_ucode_request_error(uc_vm_t
*vm
, size_t nargs
)
831 rpc_ucode_call_ctx_t
**callctx
= uc_fn_this("rpcd.ucode.request");
832 uc_value_t
*rcode
= uc_fn_arg(0);
835 if (!callctx
|| !*callctx
) {
836 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
837 "Attempt to invoke error() on invalid self");
841 else if (ucv_type(rcode
) != UC_INTEGER
) {
842 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
843 "First argument to error() must be an integer");
848 if ((*callctx
)->replied
) {
849 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
850 "Reply has already been sent");
856 code
= ucv_int64_get(rcode
);
858 if (errno
== ERANGE
|| code
< 0 || code
> __UBUS_STATUS_LAST
)
859 code
= UBUS_STATUS_UNKNOWN_ERROR
;
861 rpc_ucode_request_finish(*callctx
, code
, NULL
);
866 static const uc_function_list_t rpc_ucode_request_fns
[] = {
867 { "reply", rpc_ucode_request_reply
},
868 { "error", rpc_ucode_request_error
},
872 rpc_ucode_request_gc(void *ud
)
874 rpc_ucode_call_ctx_t
*callctx
= ud
;
876 uloop_timeout_cancel(&callctx
->timeout
);
881 rpc_ucode_init_globals(rpc_ucode_script_t
*script
)
883 uc_vm_t
*vm
= &script
->vm
;
884 uc_value_t
*scope
= uc_vm_scope_get(vm
);
886 #define status_const(name) \
887 ucv_object_add(scope, #name, ucv_uint64_new(name))
889 status_const(UBUS_STATUS_OK
);
890 status_const(UBUS_STATUS_INVALID_COMMAND
);
891 status_const(UBUS_STATUS_INVALID_ARGUMENT
);
892 status_const(UBUS_STATUS_METHOD_NOT_FOUND
);
893 status_const(UBUS_STATUS_NOT_FOUND
);
894 status_const(UBUS_STATUS_NO_DATA
);
895 status_const(UBUS_STATUS_PERMISSION_DENIED
);
896 status_const(UBUS_STATUS_TIMEOUT
);
897 status_const(UBUS_STATUS_NOT_SUPPORTED
);
898 status_const(UBUS_STATUS_UNKNOWN_ERROR
);
899 status_const(UBUS_STATUS_CONNECTION_FAILED
);
903 uc_stdlib_load(scope
);
905 script
->requesttype
= uc_type_declare(vm
, "rpcd.ucode.request",
906 rpc_ucode_request_fns
, rpc_ucode_request_gc
);
909 static rpc_ucode_script_t
*
910 rpc_ucode_script_execute(struct ubus_context
*ctx
, const char *path
, uc_program_t
*prog
)
912 rpc_ucode_script_t
*script
;
913 uc_value_t
*signature
;
914 uc_vm_status_t status
;
918 pathlen
= strlen(path
);
919 script
= calloc_a(sizeof(*script
), &pptr
, pathlen
+ 1);
922 fprintf(stderr
, "Unable to allocate context for ucode script %s: %s\n",
923 path
, strerror(errno
));
925 uc_program_put(prog
);
930 script
->path
= strncpy(pptr
, path
, pathlen
);
932 uc_vm_init(&script
->vm
, &config
);
933 rpc_ucode_init_globals(script
);
935 status
= uc_vm_execute(&script
->vm
, prog
, &signature
);
937 script
->pending_replies
= ucv_array_new(&script
->vm
);
939 uc_vm_registry_set(&script
->vm
, "rpcd.ucode.signature", signature
);
940 uc_vm_registry_set(&script
->vm
, "rpcd.ucode.deferreds", script
->pending_replies
);
942 uc_program_put(prog
);
947 if (rpc_ucode_script_register(ctx
, script
))
950 fprintf(stderr
, "Skipping registration of ucode script %s\n", path
);
954 fprintf(stderr
, "The ucode script %s invoked exit(%" PRId64
")\n",
955 path
, ucv_int64_get(signature
));
959 fprintf(stderr
, "Compilation error while executing ucode script %s\n", path
);
963 fprintf(stderr
, "Runtime error while executing ucode script %s\n", path
);
967 uc_vm_free(&script
->vm
);
974 rpc_ucode_init_script(struct ubus_context
*ctx
, const char *path
)
976 rpc_ucode_script_t
*script
;
980 src
= uc_source_new_file(path
);
983 fprintf(stderr
, "Unable to open ucode script %s: %s\n",
984 path
, strerror(errno
));
986 return UBUS_STATUS_UNKNOWN_ERROR
;
989 prog
= rpc_ucode_script_compile(path
, src
);
992 return UBUS_STATUS_UNKNOWN_ERROR
;
994 script
= rpc_ucode_script_execute(ctx
, path
, prog
);
997 return UBUS_STATUS_UNKNOWN_ERROR
;
999 list_add(&script
->list
, &scripts
);
1001 return UBUS_STATUS_OK
;
1005 rpc_ucode_api_init(const struct rpc_daemon_ops
*ops
, struct ubus_context
*ctx
)
1007 char path
[PATH_MAX
];
1013 request_timeout
= *ops
->exec_timeout
;
1015 /* reopen ucode.so with RTLD_GLOBAL in order to export libucode runtime
1016 * symbols for ucode extensions loaded later at runtime */
1017 if (!dlopen(RPC_LIBRARY_DIRECTORY
"/ucode.so", RTLD_LAZY
|RTLD_GLOBAL
)) {
1018 fprintf(stderr
, "Failed to dlopen() ucode.so: %s, dynamic ucode plugins may fail\n",
1022 /* initialize default module search path */
1023 uc_search_path_init(&config
.module_search_path
);
1025 if ((d
= opendir(RPC_UCSCRIPT_DIRECTORY
)) != NULL
) {
1026 while ((e
= readdir(d
)) != NULL
) {
1027 snprintf(path
, sizeof(path
), RPC_UCSCRIPT_DIRECTORY
"/%s", e
->d_name
);
1029 if (stat(path
, &s
) || !S_ISREG(s
.st_mode
))
1032 if (s
.st_mode
& S_IWOTH
) {
1033 fprintf(stderr
, "Ignoring ucode script %s because it is world writable\n",
1039 rv
|= rpc_ucode_init_script(ctx
, path
);
1048 struct rpc_plugin rpc_plugin
= {
1049 .init
= rpc_ucode_api_init