ucode: add ucode interpreter plugin
[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 };
82
83
84 static rpc_ucode_script_t *
85 rpc_ucode_obj_to_script(struct ubus_object *obj)
86 {
87 rpc_ucode_ubus_obj_t *uo = container_of(obj, rpc_ucode_ubus_obj_t, ubusobj);
88
89 return uo->script;
90 }
91
92 static uc_value_t *
93 rpc_ucode_obj_to_signature(struct ubus_object *obj)
94 {
95 rpc_ucode_ubus_obj_t *uo = container_of(obj, rpc_ucode_ubus_obj_t, ubusobj);
96
97 return uo->signature;
98 }
99
100 static void
101 rpc_ucode_ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob);
102
103 static void
104 rpc_ucode_ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob);
105
106 static void
107 rpc_ucode_ucv_to_blob(const char *name, uc_value_t *val, struct blob_buf *blob)
108 {
109 int64_t n;
110 void *c;
111
112 switch (ucv_type(val)) {
113 case UC_NULL:
114 blobmsg_add_field(blob, BLOBMSG_TYPE_UNSPEC, name, NULL, 0);
115 break;
116
117 case UC_BOOLEAN:
118 blobmsg_add_u8(blob, name, ucv_boolean_get(val));
119 break;
120
121 case UC_INTEGER:
122 n = ucv_int64_get(val);
123
124 if (errno == ERANGE)
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);
128 else
129 blobmsg_add_u64(blob, name, n);
130
131 break;
132
133 case UC_DOUBLE:
134 blobmsg_add_double(blob, name, ucv_double_get(val));
135 break;
136
137 case UC_STRING:
138 blobmsg_add_string(blob, name, ucv_string_get(val));
139 break;
140
141 case UC_ARRAY:
142 c = blobmsg_open_array(blob, name);
143 rpc_ucode_ucv_array_to_blob(val, blob);
144 blobmsg_close_array(blob, c);
145 break;
146
147 case UC_OBJECT:
148 c = blobmsg_open_table(blob, name);
149 rpc_ucode_ucv_object_to_blob(val, blob);
150 blobmsg_close_table(blob, c);
151 break;
152
153 default:
154 break;
155 }
156 }
157
158 static void
159 rpc_ucode_ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob)
160 {
161 size_t i;
162
163 for (i = 0; i < ucv_array_length(val); i++)
164 rpc_ucode_ucv_to_blob(NULL, ucv_array_get(val, i), blob);
165 }
166
167 static void
168 rpc_ucode_ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob)
169 {
170 ucv_object_foreach(val, k, v)
171 rpc_ucode_ucv_to_blob(k, v, blob);
172 }
173
174 static uc_value_t *
175 rpc_ucode_blob_to_ucv(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name);
176
177 static uc_value_t *
178 rpc_ucode_blob_array_to_ucv(uc_vm_t *vm, struct blob_attr *attr, size_t len, bool table)
179 {
180 uc_value_t *o = table ? ucv_object_new(vm) : ucv_array_new(vm);
181 uc_value_t *v;
182 struct blob_attr *pos;
183 size_t rem = len;
184 const char *name;
185
186 if (!o)
187 return NULL;
188
189 __blob_for_each_attr(pos, attr, rem) {
190 name = NULL;
191 v = rpc_ucode_blob_to_ucv(vm, pos, table, &name);
192
193 if (table && name)
194 ucv_object_add(o, name, v);
195 else if (!table)
196 ucv_array_push(o, v);
197 else
198 ucv_put(v);
199 }
200
201 return o;
202 }
203
204 static uc_value_t *
205 rpc_ucode_blob_to_ucv(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name)
206 {
207 void *data;
208 int len;
209
210 if (!blobmsg_check_attr(attr, false))
211 return NULL;
212
213 if (table && blobmsg_name(attr)[0])
214 *name = blobmsg_name(attr);
215
216 data = blobmsg_data(attr);
217 len = blobmsg_data_len(attr);
218
219 switch (blob_id(attr)) {
220 case BLOBMSG_TYPE_BOOL:
221 return ucv_boolean_new(*(uint8_t *)data);
222
223 case BLOBMSG_TYPE_INT16:
224 return ucv_int64_new((int16_t)be16_to_cpu(*(uint16_t *)data));
225
226 case BLOBMSG_TYPE_INT32:
227 return ucv_int64_new((int32_t)be32_to_cpu(*(uint32_t *)data));
228
229 case BLOBMSG_TYPE_INT64:
230 return ucv_int64_new((int64_t)be64_to_cpu(*(uint64_t *)data));
231
232 case BLOBMSG_TYPE_DOUBLE:
233 ;
234 union {
235 double d;
236 uint64_t u64;
237 } v;
238
239 v.u64 = be64_to_cpu(*(uint64_t *)data);
240
241 return ucv_double_new(v.d);
242
243 case BLOBMSG_TYPE_STRING:
244 return ucv_string_new(data);
245
246 case BLOBMSG_TYPE_ARRAY:
247 return rpc_ucode_blob_array_to_ucv(vm, data, len, false);
248
249 case BLOBMSG_TYPE_TABLE:
250 return rpc_ucode_blob_array_to_ucv(vm, data, len, true);
251
252 default:
253 return NULL;
254 }
255 }
256
257 static int
258 rpc_ucode_validate_call_args(struct ubus_object *obj, const char *ubus_method_name, struct blob_attr *msg, uc_value_t **res)
259 {
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;
264 bool found;
265 size_t i;
266 int len;
267
268 for (i = 0; i < obj->n_methods; i++) {
269 if (!strcmp(obj->methods[i].name, ubus_method_name)) {
270 method = &obj->methods[i];
271 break;
272 }
273 }
274
275 if (!method)
276 return UBUS_STATUS_METHOD_NOT_FOUND;
277
278 len = blob_len(msg);
279
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;
283
284 if (!blob_is_extended(attr))
285 return UBUS_STATUS_INVALID_ARGUMENT;
286
287 hdr = blob_data(attr);
288 found = false;
289
290 for (i = 0; i < method->n_policy; i++) {
291 if (blobmsg_namelen(hdr) != strlen(method->policy[i].name))
292 continue;
293
294 if (strcmp(method->policy[i].name, (char *)hdr->name))
295 continue;
296
297 /* named argument found but wrong type */
298 if (blob_id(attr) != method->policy[i].type)
299 goto inval;
300
301 found = true;
302 break;
303 }
304
305 /* named argument not found in policy */
306 if (!found)
307 goto inval;
308 }
309
310 *res = rpc_ucode_blob_array_to_ucv(&script->vm, blob_data(msg), blob_len(msg), true);
311
312 return UBUS_STATUS_OK;
313
314 inval:
315 *res = NULL;
316
317 return UBUS_STATUS_INVALID_ARGUMENT;
318 }
319
320 static uc_value_t *
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)
324 {
325 uc_value_t *info, *o;
326
327 info = ucv_object_new(vm);
328
329 o = ucv_object_new(vm);
330
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));
334
335 ucv_object_add(info, "acl", o);
336
337 o = ucv_object_new(vm);
338
339 ucv_object_add(o, "id", ucv_uint64_new(obj->id));
340 ucv_object_add(o, "name", ucv_string_new(obj->name));
341
342 if (obj->path)
343 ucv_object_add(o, "path", ucv_string_new(obj->path));
344
345 ucv_object_add(info, "object", o);
346
347 ucv_object_add(info, "method", ucv_string_new(ubus_method_name));
348
349 return info;
350 }
351
352 static void
353 rpc_ucode_request_finish(rpc_ucode_call_ctx_t *callctx, int code, uc_value_t *reply)
354 {
355 rpc_ucode_script_t *script = callctx->script;
356 uc_resource_t *r;
357 size_t i;
358
359 if (callctx->replied)
360 return;
361
362 if (reply) {
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);
366 }
367
368 ubus_complete_deferred_request(callctx->ubus, &callctx->req, code);
369
370 callctx->replied = true;
371
372 for (i = 0; i < ucv_array_length(script->pending_replies); i++) {
373 r = (uc_resource_t *)ucv_array_get(script->pending_replies, i);
374
375 if (r && r->data == callctx) {
376 ucv_array_set(script->pending_replies, i, NULL);
377 break;
378 }
379 }
380 }
381
382 static void
383 rpc_ucode_request_timeout(struct uloop_timeout *timeout)
384 {
385 rpc_ucode_call_ctx_t *callctx = container_of(timeout, rpc_ucode_call_ctx_t, timeout);
386
387 rpc_ucode_request_finish(callctx, UBUS_STATUS_TIMEOUT, NULL);
388 }
389
390 static int
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)
394 {
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;
398 size_t i;
399 int rv;
400
401 rv = rpc_ucode_validate_call_args(obj, ubus_method_name, msg, &args);
402
403 if (rv != UBUS_STATUS_OK)
404 return rv;
405
406 func = ucv_object_get(
407 ucv_object_get(rpc_ucode_obj_to_signature(obj), ubus_method_name, NULL),
408 "call", NULL
409 );
410
411 if (!ucv_is_callable(func))
412 return UBUS_STATUS_METHOD_NOT_FOUND;
413
414 /* allocate deferred method call context */
415 callctx = calloc(1, sizeof(*callctx));
416
417 if (!callctx)
418 return UBUS_STATUS_UNKNOWN_ERROR;
419
420 callctx->ubus = ctx;
421 callctx->script = script;
422
423 ubus_defer_request(ctx, req, &callctx->req);
424
425 /* create ucode request type object and set properties */
426 reqobj = uc_resource_new(script->requesttype, callctx);
427 reqproto = ucv_object_new(&script->vm);
428
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));
432
433 ucv_prototype_set(ucv_prototype_get(reqobj), reqproto);
434
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));
438
439 /* execute request handler function */
440 switch (uc_vm_call(&script->vm, false, 1)) {
441 case EXCEPTION_NONE:
442 res = uc_vm_stack_pop(&script->vm);
443
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);
449
450 /* Add wrapped request context into registry to prevent GC'ing
451 * until reply or timeout occurred */
452 for (i = 0;; i++) {
453 if (ucv_array_get(script->pending_replies, i) == NULL) {
454 ucv_array_set(script->pending_replies, i, ucv_get(reqobj));
455 break;
456 }
457 }
458 }
459
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);
466
467 ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_OK);
468 callctx->replied = true;
469 }
470
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;
477 }
478
479 ucv_put(res);
480 break;
481
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. */
484 case EXCEPTION_EXIT:
485 rv = script->vm.arg.s32;
486
487 if (rv < UBUS_STATUS_OK || rv >= __UBUS_STATUS_LAST)
488 rv = UBUS_STATUS_UNKNOWN_ERROR;
489
490 ubus_complete_deferred_request(ctx, &callctx->req, rv);
491 callctx->replied = true;
492 break;
493
494 /* treat other exceptions as unknown error */
495 default:
496 ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_UNKNOWN_ERROR);
497 callctx->replied = true;
498 break;
499 }
500
501 /* release request object */
502 ucv_put(reqobj);
503
504 /* garbage collect */
505 ucv_gc(&script->vm);
506
507 return UBUS_STATUS_OK;
508 }
509
510 static uc_function_t *
511 rpc_ucode_script_compile(const char *path, uc_source_t *src)
512 {
513 char *syntax_error = NULL;
514 uc_function_t *progfunc;
515
516 progfunc = uc_compile(&config, src, &syntax_error);
517
518 if (!progfunc)
519 fprintf(stderr, "Unable to compile ucode script %s: %s\n",
520 path, syntax_error);
521
522 uc_source_put(src);
523 free(syntax_error);
524
525 return progfunc;
526 }
527
528 static bool
529 rpc_ucode_script_validate(rpc_ucode_script_t *script)
530 {
531 uc_value_t *signature = uc_vm_registry_get(&script->vm, "rpcd.ucode.signature");
532 uc_value_t *args, *func;
533
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));
538
539 return false;
540 }
541
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));
547
548 return false;
549 }
550
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);
554
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));
559
560 return false;
561 }
562
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));
567
568 return false;
569 }
570
571 if (args) {
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));
576
577 return false;
578 }
579
580 ucv_object_foreach(args, ubus_argument_name, ubus_argument_typehint) {
581 switch (ucv_type(ubus_argument_typehint)) {
582 case UC_BOOLEAN:
583 case UC_INTEGER:
584 case UC_DOUBLE:
585 case UC_STRING:
586 case UC_ARRAY:
587 case UC_OBJECT:
588 continue;
589
590 default:
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));
596
597 return false;
598 }
599 }
600 }
601 }
602 }
603
604 return true;
605 }
606
607 static bool
608 rpc_ucode_method_register(struct ubus_method *method, const char *ubus_method_name, uc_value_t *ubus_method_arguments)
609 {
610 struct blobmsg_policy *policy;
611 enum blobmsg_type type;
612
613 method->name = strdup(ubus_method_name);
614
615 if (!method->name) {
616 fprintf(stderr, "Unable to allocate ubus method name: %s\n",
617 strerror(errno));
618
619 return false;
620 }
621
622 method->policy = calloc(ucv_object_length(ubus_method_arguments), sizeof(*method->policy));
623
624 if (!method->policy) {
625 fprintf(stderr, "Unable to allocate ubus method argument policy: %s\n",
626 strerror(errno));
627
628 return false;
629 }
630
631 method->handler = rpc_ucode_script_call;
632
633 ucv_object_foreach(ubus_method_arguments, ubus_argument_name, ubus_argument_typehint) {
634 switch (ucv_type(ubus_argument_typehint)) {
635 case UC_BOOLEAN:
636 type = BLOBMSG_TYPE_INT8;
637 break;
638
639 case UC_INTEGER:
640 switch (ucv_int64_get(ubus_argument_typehint)) {
641 case 8:
642 type = BLOBMSG_TYPE_INT8;
643 break;
644
645 case 16:
646 type = BLOBMSG_TYPE_INT16;
647 break;
648
649 case 64:
650 type = BLOBMSG_TYPE_INT64;
651 break;
652
653 default:
654 type = BLOBMSG_TYPE_INT32;
655 break;
656 }
657
658 break;
659
660 case UC_DOUBLE:
661 type = BLOBMSG_TYPE_DOUBLE;
662 break;
663
664 case UC_ARRAY:
665 type = BLOBMSG_TYPE_ARRAY;
666 break;
667
668 case UC_OBJECT:
669 type = BLOBMSG_TYPE_TABLE;
670 break;
671
672 default:
673 type = BLOBMSG_TYPE_STRING;
674 break;
675 }
676
677 policy = (struct blobmsg_policy *)&method->policy[method->n_policy++];
678
679 policy->type = type;
680 policy->name = strdup(ubus_argument_name);
681
682 if (!policy->name) {
683 fprintf(stderr, "Unable to allocate ubus method argument name: %s\n",
684 strerror(errno));
685
686 return false;
687 }
688 }
689
690 return true;
691 }
692
693 static bool
694 rpc_ucode_script_register(struct ubus_context *ctx, rpc_ucode_script_t *script)
695 {
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;
703 uc_value_t *args;
704 int rv;
705
706 if (!rpc_ucode_script_validate(script))
707 return false;
708
709 ucv_object_foreach(signature, ubus_object_name, ubus_object_methods) {
710 namelen = strlen(ubus_object_name);
711 typelen = strlen("rpcd-plugin-ucode-") + namelen;
712
713 uuobj = calloc_a(sizeof(*uuobj),
714 &onptr, namelen + 1,
715 &mptr, ucv_object_length(ubus_object_methods) * sizeof(struct ubus_method),
716 &tptr, sizeof(struct ubus_object_type),
717 &tnptr, typelen + 1);
718
719 if (!uuobj) {
720 fprintf(stderr, "Unable to allocate ubus object signature: %s\n",
721 strerror(errno));
722
723 continue;
724 }
725
726 list_add(&uuobj->list, &uuobjs);
727
728 uuobj->script = script;
729 uuobj->signature = ubus_object_methods;
730
731 snprintf(tnptr, typelen, "rpcd-plugin-ucode-%s", ubus_object_name);
732
733 method = (struct ubus_method *)mptr;
734
735 obj = &uuobj->ubusobj;
736 obj->name = strncpy(onptr, ubus_object_name, namelen);
737 obj->methods = method;
738
739 obj->type = (struct ubus_object_type *)tptr;
740 obj->type->name = tnptr;
741 obj->type->methods = obj->methods;
742
743 ucv_object_foreach(ubus_object_methods, ubus_method_name, ubus_method_definition) {
744 args = ucv_object_get(ubus_method_definition, "args", NULL);
745
746 if (!rpc_ucode_method_register(&method[obj->n_methods++], ubus_method_name, args))
747 goto free;
748 }
749
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;
754
755 rv = ubus_add_object(ctx, obj);
756
757 if (rv != UBUS_STATUS_OK) {
758 fprintf(stderr, "Unable to register ubus object %s: %s\n",
759 obj->name, ubus_strerror(rv));
760
761 goto free;
762 }
763
764 continue;
765
766 free:
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);
770
771 free((char *)method->name);
772 free((char *)method->policy);
773 }
774
775 free(uuobj);
776 }
777
778 return true;
779 }
780
781 static uc_value_t *
782 rpc_ucode_request_reply(uc_vm_t *vm, size_t nargs)
783 {
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;
788
789 if (!callctx || !*callctx) {
790 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
791 "Attempt to invoke reply() on invalid self");
792
793 return NULL;
794 }
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");
798
799 return NULL;
800 }
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");
804
805 return NULL;
806 }
807
808 if ((*callctx)->replied) {
809 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
810 "Reply has already been sent");
811
812 return NULL;
813 }
814
815 if (rcode) {
816 code = ucv_int64_get(rcode);
817
818 if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST)
819 code = UBUS_STATUS_UNKNOWN_ERROR;
820 }
821
822 rpc_ucode_request_finish(*callctx, code, reply);
823
824 return NULL;
825 }
826
827 static uc_value_t *
828 rpc_ucode_request_error(uc_vm_t *vm, size_t nargs)
829 {
830 rpc_ucode_call_ctx_t **callctx = uc_fn_this("rpcd.ucode.request");
831 uc_value_t *rcode = uc_fn_arg(0);
832 int64_t code;
833
834 if (!callctx || !*callctx) {
835 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
836 "Attempt to invoke error() on invalid self");
837
838 return NULL;
839 }
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");
843
844 return NULL;
845 }
846
847 if ((*callctx)->replied) {
848 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
849 "Reply has already been sent");
850
851 return NULL;
852 }
853
854
855 code = ucv_int64_get(rcode);
856
857 if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST)
858 code = UBUS_STATUS_UNKNOWN_ERROR;
859
860 rpc_ucode_request_finish(*callctx, code, NULL);
861
862 return NULL;
863 }
864
865 static const uc_function_list_t rpc_ucode_request_fns[] = {
866 { "reply", rpc_ucode_request_reply },
867 { "error", rpc_ucode_request_error },
868 };
869
870 static void
871 rpc_ucode_request_gc(void *ud)
872 {
873 rpc_ucode_call_ctx_t *callctx = ud;
874
875 uloop_timeout_cancel(&callctx->timeout);
876 free(callctx);
877 }
878
879 static void
880 rpc_ucode_init_globals(rpc_ucode_script_t *script)
881 {
882 uc_vm_t *vm = &script->vm;
883 uc_value_t *scope = uc_vm_scope_get(vm);
884
885 #define status_const(name) \
886 ucv_object_add(scope, #name, ucv_uint64_new(name))
887
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);
899
900 #undef status_const
901
902 uc_stdlib_load(scope);
903
904 script->requesttype = uc_type_declare(vm, "rpcd.ucode.request",
905 rpc_ucode_request_fns, rpc_ucode_request_gc);
906 }
907
908 static rpc_ucode_script_t *
909 rpc_ucode_script_execute(struct ubus_context *ctx, const char *path, uc_function_t *func)
910 {
911 rpc_ucode_script_t *script;
912 uc_value_t *signature;
913 uc_vm_status_t status;
914 size_t pathlen;
915 char *pptr;
916
917 pathlen = strlen(path);
918 script = calloc_a(sizeof(*script), &pptr, pathlen + 1);
919
920 if (!script) {
921 fprintf(stderr, "Unable to allocate context for ucode script %s: %s\n",
922 path, strerror(errno));
923
924 ucv_put(&func->header);
925
926 return NULL;
927 }
928
929 script->path = strncpy(pptr, path, pathlen);
930
931 uc_vm_init(&script->vm, &config);
932 rpc_ucode_init_globals(script);
933
934 status = uc_vm_execute(&script->vm, func, &signature);
935
936 script->pending_replies = ucv_array_new(&script->vm);
937
938 uc_vm_registry_set(&script->vm, "rpcd.ucode.signature", signature);
939 uc_vm_registry_set(&script->vm, "rpcd.ucode.deferreds", script->pending_replies);
940
941 ucv_gc(&script->vm);
942
943 switch (status) {
944 case STATUS_OK:
945 if (rpc_ucode_script_register(ctx, script))
946 return script;
947
948 fprintf(stderr, "Skipping registration of ucode script %s\n", path);
949 break;
950
951 case STATUS_EXIT:
952 fprintf(stderr, "The ucode script %s invoked exit(%" PRId64 ")\n",
953 path, ucv_int64_get(signature));
954 break;
955
956 case ERROR_COMPILE:
957 fprintf(stderr, "Compilation error while executing ucode script %s\n", path);
958 break;
959
960 case ERROR_RUNTIME:
961 fprintf(stderr, "Runtime error while executing ucode script %s\n", path);
962 break;
963 }
964
965 uc_vm_free(&script->vm);
966 free(script);
967
968 return NULL;
969 }
970
971 static int
972 rpc_ucode_init_script(struct ubus_context *ctx, const char *path)
973 {
974 rpc_ucode_script_t *script;
975 uc_function_t *progfunc;
976 uc_source_t *src;
977
978 src = uc_source_new_file(path);
979
980 if (!src) {
981 fprintf(stderr, "Unable to open ucode script %s: %s\n",
982 path, strerror(errno));
983
984 return UBUS_STATUS_UNKNOWN_ERROR;
985 }
986
987 progfunc = rpc_ucode_script_compile(path, src);
988
989 if (!progfunc)
990 return UBUS_STATUS_UNKNOWN_ERROR;
991
992 script = rpc_ucode_script_execute(ctx, path, progfunc);
993
994 if (!script)
995 return UBUS_STATUS_UNKNOWN_ERROR;
996
997 list_add(&script->list, &scripts);
998
999 return UBUS_STATUS_OK;
1000 }
1001
1002 static int
1003 rpc_ucode_api_init(const struct rpc_daemon_ops *ops, struct ubus_context *ctx)
1004 {
1005 char path[PATH_MAX];
1006 struct dirent *e;
1007 struct stat s;
1008 int rv = 0;
1009 DIR *d;
1010
1011 request_timeout = *ops->exec_timeout;
1012
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",
1017 dlerror());
1018 }
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);
1022
1023 if (stat(path, &s) || !S_ISREG(s.st_mode))
1024 continue;
1025
1026 if (s.st_mode & S_IWOTH) {
1027 fprintf(stderr, "Ignoring ucode script %s because it is world writable\n",
1028 path);
1029
1030 continue;
1031 }
1032
1033 rv |= rpc_ucode_init_script(ctx, path);
1034 }
1035
1036 closedir(d);
1037 }
1038
1039 return rv;
1040 }
1041
1042 struct rpc_plugin rpc_plugin = {
1043 .init = rpc_ucode_api_init
1044 };