• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/rpcd/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 };
1074 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt