• 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 };
 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_program_t *
511 rpc_ucode_script_compile(const char *path, uc_source_t *src)
512 {
513         char *syntax_error = NULL;
514         uc_program_t *prog;
515 
516         prog = uc_compile(&config, src, &syntax_error);
517 
518         if (!prog)
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 prog;
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_program_t *prog)
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                 uc_program_put(prog);
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, prog, &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         uc_program_put(prog);
942         ucv_gc(&script->vm);
943 
944         switch (status) {
945         case STATUS_OK:
946                 if (rpc_ucode_script_register(ctx, script))
947                         return script;
948 
949                 fprintf(stderr, "Skipping registration of ucode script %s\n", path);
950                 break;
951 
952         case STATUS_EXIT:
953                 fprintf(stderr, "The ucode script %s invoked exit(%" PRId64 ")\n",
954                         path, ucv_int64_get(signature));
955                 break;
956 
957         case ERROR_COMPILE:
958                 fprintf(stderr, "Compilation error while executing ucode script %s\n", path);
959                 break;
960 
961         case ERROR_RUNTIME:
962                 fprintf(stderr, "Runtime error while executing ucode script %s\n", path);
963                 break;
964         }
965 
966         uc_vm_free(&script->vm);
967         free(script);
968 
969         return NULL;
970 }
971 
972 static int
973 rpc_ucode_init_script(struct ubus_context *ctx, const char *path)
974 {
975         rpc_ucode_script_t *script;
976         uc_program_t *prog;
977         uc_source_t *src;
978 
979         src = uc_source_new_file(path);
980 
981         if (!src) {
982                 fprintf(stderr, "Unable to open ucode script %s: %s\n",
983                         path, strerror(errno));
984 
985                 return UBUS_STATUS_UNKNOWN_ERROR;
986         }
987 
988         prog = rpc_ucode_script_compile(path, src);
989 
990         if (!prog)
991                 return UBUS_STATUS_UNKNOWN_ERROR;
992 
993         script = rpc_ucode_script_execute(ctx, path, prog);
994 
995         if (!script)
996                 return UBUS_STATUS_UNKNOWN_ERROR;
997 
998         list_add(&script->list, &scripts);
999 
1000         return UBUS_STATUS_OK;
1001 }
1002 
1003 static int
1004 rpc_ucode_api_init(const struct rpc_daemon_ops *ops, struct ubus_context *ctx)
1005 {
1006         char path[PATH_MAX];
1007         struct dirent *e;
1008         struct stat s;
1009         int rv = 0;
1010         DIR *d;
1011 
1012         request_timeout = *ops->exec_timeout;
1013 
1014         /* reopen ucode.so with RTLD_GLOBAL in order to export libucode runtime
1015          * symbols for ucode extensions loaded later at runtime */
1016         if (!dlopen(RPC_LIBRARY_DIRECTORY "/ucode.so", RTLD_LAZY|RTLD_GLOBAL)) {
1017                 fprintf(stderr, "Failed to dlopen() ucode.so: %s, dynamic ucode plugins may fail\n",
1018                         dlerror());
1019         }
1020         if ((d = opendir(RPC_UCSCRIPT_DIRECTORY)) != NULL) {
1021                 while ((e = readdir(d)) != NULL) {
1022                         snprintf(path, sizeof(path), RPC_UCSCRIPT_DIRECTORY "/%s", e->d_name);
1023 
1024                         if (stat(path, &s) || !S_ISREG(s.st_mode))
1025                                 continue;
1026 
1027                         if (s.st_mode & S_IWOTH) {
1028                                 fprintf(stderr, "Ignoring ucode script %s because it is world writable\n",
1029                                         path);
1030 
1031                                 continue;
1032                         }
1033 
1034                         rv |= rpc_ucode_init_script(ctx, path);
1035                 }
1036 
1037                 closedir(d);
1038         }
1039 
1040         return rv;
1041 }
1042 
1043 struct rpc_plugin rpc_plugin = {
1044         .init = rpc_ucode_api_init
1045 };
1046 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt