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