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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt