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

Sources/ucode/lib/ubus.c

  1 /*
  2  * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or distribute this software for any
  5  * purpose with or without fee is hereby granted, provided that the above
  6  * copyright notice and this permission notice appear in all copies.
  7  *
  8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15  */
 16 
 17 #include <unistd.h>
 18 #include <libubus.h>
 19 #include <libubox/blobmsg.h>
 20 #include <libubox/blobmsg_json.h>
 21 
 22 #include "ucode/module.h"
 23 
 24 #define err_return(err) do { last_error = err; return NULL; } while(0)
 25 
 26 static enum ubus_msg_status last_error = 0;
 27 static uc_resource_type_t *defer_type;
 28 static uc_resource_type_t *conn_type;
 29 
 30 static uc_value_t *cb_registry;
 31 static uint64_t n_cb_active;
 32 static bool have_own_uloop;
 33 
 34 typedef struct {
 35         int timeout;
 36         struct blob_buf buf;
 37         struct ubus_context *ctx;
 38 } ubus_connection;
 39 
 40 typedef struct {
 41         struct ubus_context *context;
 42         struct ubus_request request;
 43         struct uloop_timeout timeout;
 44         bool complete;
 45         uc_vm_t *vm;
 46         uc_value_t *callback;
 47         uc_value_t *response;
 48 } ubus_deferred;
 49 
 50 static uc_value_t *
 51 uc_ubus_error(uc_vm_t *vm, size_t nargs)
 52 {
 53         uc_value_t *errmsg;
 54 
 55         if (last_error == 0)
 56                 return NULL;
 57 
 58         errmsg = ucv_string_new(ubus_strerror(last_error));
 59         last_error = 0;
 60 
 61         return errmsg;
 62 }
 63 
 64 static uc_value_t *
 65 uc_blob_to_json(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name);
 66 
 67 static uc_value_t *
 68 uc_blob_array_to_json(uc_vm_t *vm, struct blob_attr *attr, size_t len, bool table)
 69 {
 70         uc_value_t *o = table ? ucv_object_new(vm) : ucv_array_new(vm);
 71         uc_value_t *v;
 72         struct blob_attr *pos;
 73         size_t rem = len;
 74         const char *name;
 75 
 76         if (!o)
 77                 return NULL;
 78 
 79         __blob_for_each_attr(pos, attr, rem) {
 80                 name = NULL;
 81                 v = uc_blob_to_json(vm, pos, table, &name);
 82 
 83                 if (table && name)
 84                         ucv_object_add(o, name, v);
 85                 else if (!table)
 86                         ucv_array_push(o, v);
 87                 else
 88                         ucv_put(v);
 89         }
 90 
 91         return o;
 92 }
 93 
 94 static uc_value_t *
 95 uc_blob_to_json(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name)
 96 {
 97         void *data;
 98         int len;
 99 
100         if (!blobmsg_check_attr(attr, false))
101                 return NULL;
102 
103         if (table && blobmsg_name(attr)[0])
104                 *name = blobmsg_name(attr);
105 
106         data = blobmsg_data(attr);
107         len = blobmsg_data_len(attr);
108 
109         switch (blob_id(attr)) {
110         case BLOBMSG_TYPE_BOOL:
111                 return ucv_boolean_new(*(uint8_t *)data);
112 
113         case BLOBMSG_TYPE_INT16:
114                 return ucv_int64_new((int16_t)be16_to_cpu(*(uint16_t *)data));
115 
116         case BLOBMSG_TYPE_INT32:
117                 return ucv_int64_new((int32_t)be32_to_cpu(*(uint32_t *)data));
118 
119         case BLOBMSG_TYPE_INT64:
120                 return ucv_int64_new((int64_t)be64_to_cpu(*(uint64_t *)data));
121 
122         case BLOBMSG_TYPE_DOUBLE:
123                 ;
124                 union {
125                         double d;
126                         uint64_t u64;
127                 } v;
128 
129                 v.u64 = be64_to_cpu(*(uint64_t *)data);
130 
131                 return ucv_double_new(v.d);
132 
133         case BLOBMSG_TYPE_STRING:
134                 return ucv_string_new(data);
135 
136         case BLOBMSG_TYPE_ARRAY:
137                 return uc_blob_array_to_json(vm, data, len, false);
138 
139         case BLOBMSG_TYPE_TABLE:
140                 return uc_blob_array_to_json(vm, data, len, true);
141 
142         default:
143                 return NULL;
144         }
145 }
146 
147 
148 static uc_value_t *
149 uc_ubus_connect(uc_vm_t *vm, size_t nargs)
150 {
151         uc_value_t *socket = uc_fn_arg(0);
152         uc_value_t *timeout = uc_fn_arg(1);
153         uc_value_t *co;
154         ubus_connection *c;
155 
156         if ((socket && ucv_type(socket) != UC_STRING) ||
157             (timeout && ucv_type(timeout) != UC_INTEGER))
158                 err_return(UBUS_STATUS_INVALID_ARGUMENT);
159 
160         c = calloc(1, sizeof(*c));
161 
162         if (!c)
163                 err_return(UBUS_STATUS_UNKNOWN_ERROR);
164 
165         c->ctx = ubus_connect(socket ? ucv_string_get(socket) : NULL);
166         c->timeout = timeout ? ucv_int64_get(timeout) : 30;
167 
168         if (!c->ctx) {
169                 free(c);
170                 err_return(UBUS_STATUS_UNKNOWN_ERROR);
171         }
172 
173         if (c->timeout < 0)
174                 c->timeout = 30;
175 
176         co = ucv_object_new(vm);
177 
178         if (!co) {
179                 ubus_free(c->ctx);
180                 free(c);
181                 err_return(ENOMEM);
182         }
183 
184         ubus_add_uloop(c->ctx);
185 
186         return uc_resource_new(conn_type, c);
187 }
188 
189 static void
190 uc_ubus_signatures_cb(struct ubus_context *c, struct ubus_object_data *o, void *p)
191 {
192         uc_value_t *arr = p;
193         uc_value_t *sig;
194 
195         if (!o->signature)
196                 return;
197 
198         sig = uc_blob_array_to_json(NULL, blob_data(o->signature), blob_len(o->signature), true);
199 
200         if (sig)
201                 ucv_array_push(arr, sig);
202 }
203 
204 static void
205 uc_ubus_objects_cb(struct ubus_context *c, struct ubus_object_data *o, void *p)
206 {
207         json_object *arr = p;
208         json_object *obj;
209 
210         obj = json_object_new_string(o->path);
211 
212         if (obj)
213                 json_object_array_add(arr, obj);
214 }
215 
216 static uc_value_t *
217 uc_ubus_list(uc_vm_t *vm, size_t nargs)
218 {
219         ubus_connection **c = uc_fn_this("ubus.connection");
220         uc_value_t *objname = uc_fn_arg(0);
221         uc_value_t *res = NULL;
222         enum ubus_msg_status rv;
223 
224         if (!c || !*c || !(*c)->ctx)
225                 err_return(UBUS_STATUS_CONNECTION_FAILED);
226 
227         if (objname && ucv_type(objname) != UC_STRING)
228                 err_return(UBUS_STATUS_INVALID_ARGUMENT);
229 
230         res = ucv_array_new(vm);
231 
232         if (!res)
233                 err_return(UBUS_STATUS_UNKNOWN_ERROR);
234 
235         rv = ubus_lookup((*c)->ctx,
236                          objname ? ucv_string_get(objname) : NULL,
237                          objname ? uc_ubus_signatures_cb : uc_ubus_objects_cb,
238                          res);
239 
240         if (rv != UBUS_STATUS_OK)
241                 err_return(rv);
242 
243         return res;
244 }
245 
246 static void
247 uc_ubus_call_cb(struct ubus_request *req, int type, struct blob_attr *msg)
248 {
249         uc_value_t **res = (uc_value_t **)req->priv;
250 
251         *res = msg ? uc_blob_array_to_json(NULL, blob_data(msg), blob_len(msg), true) : NULL;
252 }
253 
254 static void
255 uc_ubus_invoke_async_callback(ubus_deferred *defer, int ret, uc_value_t *reply)
256 {
257         uc_resource_t *r;
258         size_t i;
259 
260         if (defer->callback) {
261                 uc_vm_stack_push(defer->vm, ucv_get(defer->callback));
262                 uc_vm_stack_push(defer->vm, ucv_int64_new(ret));
263                 uc_vm_stack_push(defer->vm, ucv_get(reply));
264 
265                 if (uc_vm_call(defer->vm, false, 2) == EXCEPTION_NONE)
266                         ucv_put(uc_vm_stack_pop(defer->vm));
267 
268                 defer->callback = NULL;
269         }
270 
271         for (i = 0; i < ucv_array_length(cb_registry); i += 2) {
272                 r = (uc_resource_t *)ucv_array_get(cb_registry, i);
273 
274                 if (r && r->data == defer) {
275                         ucv_array_set(cb_registry, i, NULL);
276                         ucv_array_set(cb_registry, i + 1, NULL);
277                         break;
278                 }
279         }
280 
281         n_cb_active--;
282 
283         if (have_own_uloop && n_cb_active == 0)
284                 uloop_end();
285 }
286 
287 static void
288 uc_ubus_call_data_cb(struct ubus_request *req, int type, struct blob_attr *msg)
289 {
290         ubus_deferred *defer = container_of(req, ubus_deferred, request);
291 
292         if (defer->response == NULL)
293                 defer->response = uc_blob_array_to_json(defer->vm, blob_data(msg), blob_len(msg), true);
294 }
295 
296 static void
297 uc_ubus_call_done_cb(struct ubus_request *req, int ret)
298 {
299         ubus_deferred *defer = container_of(req, ubus_deferred, request);
300 
301         if (defer->complete)
302                 return;
303 
304         defer->complete = true;
305         uloop_timeout_cancel(&defer->timeout);
306 
307         uc_ubus_invoke_async_callback(defer, ret, defer->response);
308 }
309 
310 static void
311 uc_ubus_call_timeout_cb(struct uloop_timeout *timeout)
312 {
313         ubus_deferred *defer = container_of(timeout, ubus_deferred, timeout);
314 
315         if (defer->complete)
316                 return;
317 
318         defer->complete = true;
319         ubus_abort_request(defer->context, &defer->request);
320 
321         uc_ubus_invoke_async_callback(defer, UBUS_STATUS_TIMEOUT, NULL);
322 }
323 
324 static bool
325 uc_ubus_have_uloop(void)
326 {
327         bool prev = uloop_cancelled;
328         bool active;
329 
330         uloop_cancelled = true;
331         active = uloop_cancelling();
332         uloop_cancelled = prev;
333 
334         return active;
335 }
336 
337 static uc_value_t *
338 uc_ubus_call(uc_vm_t *vm, size_t nargs)
339 {
340         ubus_connection **c = uc_fn_this("ubus.connection");
341         uc_value_t *objname = uc_fn_arg(0);
342         uc_value_t *funname = uc_fn_arg(1);
343         uc_value_t *funargs = uc_fn_arg(2);
344         uc_value_t *res = NULL;
345         enum ubus_msg_status rv;
346         json_object *o;
347         uint32_t id;
348 
349         if (!c || !*c || !(*c)->ctx)
350                 err_return(UBUS_STATUS_CONNECTION_FAILED);
351 
352         if (ucv_type(objname) != UC_STRING ||
353             ucv_type(funname) != UC_STRING ||
354             (funargs && ucv_type(funargs) != UC_OBJECT))
355                 err_return(UBUS_STATUS_INVALID_ARGUMENT);
356 
357         blob_buf_init(&(*c)->buf, 0);
358 
359         if (funargs) {
360                 o = ucv_to_json(funargs);
361                 rv = blobmsg_add_object(&(*c)->buf, o);
362                 json_object_put(o);
363 
364                 if (!rv)
365                         err_return(UBUS_STATUS_UNKNOWN_ERROR);
366         }
367 
368         rv = ubus_lookup_id((*c)->ctx, ucv_string_get(objname), &id);
369 
370         if (rv != UBUS_STATUS_OK)
371                 err_return(rv);
372 
373         rv = ubus_invoke((*c)->ctx, id, ucv_string_get(funname), (*c)->buf.head,
374                          uc_ubus_call_cb, &res, (*c)->timeout * 1000);
375 
376         if (rv != UBUS_STATUS_OK)
377                 err_return(rv);
378 
379         return res;
380 }
381 
382 static uc_value_t *
383 uc_ubus_defer(uc_vm_t *vm, size_t nargs)
384 {
385         ubus_connection **c = uc_fn_this("ubus.connection");
386         uc_value_t *objname = uc_fn_arg(0);
387         uc_value_t *funname = uc_fn_arg(1);
388         uc_value_t *funargs = uc_fn_arg(2);
389         uc_value_t *replycb = uc_fn_arg(3);
390         uc_value_t *res = NULL;
391         enum ubus_msg_status rv;
392         ubus_deferred *defer;
393         json_object *o;
394         uint32_t id;
395         size_t i;
396 
397         if (!c || !*c || !(*c)->ctx)
398                 err_return(UBUS_STATUS_CONNECTION_FAILED);
399 
400         if (ucv_type(objname) != UC_STRING ||
401             ucv_type(funname) != UC_STRING ||
402             (funargs && ucv_type(funargs) != UC_OBJECT) ||
403             (replycb && !ucv_is_callable(replycb)))
404                 err_return(UBUS_STATUS_INVALID_ARGUMENT);
405 
406         blob_buf_init(&(*c)->buf, 0);
407 
408         if (funargs) {
409                 o = ucv_to_json(funargs);
410                 rv = blobmsg_add_object(&(*c)->buf, o);
411                 json_object_put(o);
412 
413                 if (!rv)
414                         err_return(UBUS_STATUS_UNKNOWN_ERROR);
415         }
416 
417         rv = ubus_lookup_id((*c)->ctx, ucv_string_get(objname), &id);
418 
419         if (rv != UBUS_STATUS_OK)
420                 err_return(rv);
421 
422         defer = xalloc(sizeof(*defer));
423 
424         rv = ubus_invoke_async((*c)->ctx, id, ucv_string_get(funname),
425                                (*c)->buf.head, &defer->request);
426 
427         if (rv == UBUS_STATUS_OK) {
428                 defer->vm = vm;
429                 defer->context = (*c)->ctx;
430                 defer->callback = replycb;
431 
432                 defer->request.data_cb = uc_ubus_call_data_cb;
433                 defer->request.complete_cb = uc_ubus_call_done_cb;
434                 ubus_complete_request_async((*c)->ctx, &defer->request);
435 
436                 defer->timeout.cb = uc_ubus_call_timeout_cb;
437                 uloop_timeout_set(&defer->timeout, (*c)->timeout * 1000);
438 
439                 res = uc_resource_new(defer_type, defer);
440 
441                 for (i = 0;; i += 2) {
442                         if (ucv_array_get(cb_registry, i) == NULL) {
443                                 ucv_array_set(cb_registry, i, ucv_get(res));
444                                 ucv_array_set(cb_registry, i + 1, ucv_get(replycb));
445                                 n_cb_active++;
446                                 break;
447                         }
448                 }
449 
450                 if (!uc_ubus_have_uloop()) {
451                         have_own_uloop = true;
452                         uloop_run();
453                 }
454         }
455         else {
456                 uc_vm_stack_push(vm, ucv_get(replycb));
457                 uc_vm_stack_push(vm, ucv_int64_new(rv));
458 
459                 if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE)
460                         ucv_put(uc_vm_stack_pop(vm));
461 
462                 free(defer);
463         }
464 
465         if (rv != UBUS_STATUS_OK)
466                 err_return(rv);
467 
468         return res;
469 }
470 
471 static uc_value_t *
472 uc_ubus_disconnect(uc_vm_t *vm, size_t nargs)
473 {
474         ubus_connection **c = uc_fn_this("ubus.connection");
475 
476         if (!c || !*c || !(*c)->ctx)
477                 err_return(UBUS_STATUS_CONNECTION_FAILED);
478 
479         ubus_free((*c)->ctx);
480         (*c)->ctx = NULL;
481 
482         return ucv_boolean_new(true);
483 }
484 
485 static uc_value_t *
486 uc_ubus_defer_complete(uc_vm_t *vm, size_t nargs)
487 {
488         ubus_deferred **d = uc_fn_this("ubus.deferred");
489 
490         if (!d || !*d)
491                 return NULL;
492 
493         return ucv_boolean_new((*d)->complete);
494 }
495 
496 static uc_value_t *
497 uc_ubus_defer_abort(uc_vm_t *vm, size_t nargs)
498 {
499         ubus_deferred **d = uc_fn_this("ubus.deferred");
500         uc_resource_t *r;
501         size_t i;
502 
503         if (!d || !*d)
504                 return NULL;
505 
506         if ((*d)->complete)
507                 return ucv_boolean_new(false);
508 
509         ubus_abort_request((*d)->context, &(*d)->request);
510         uloop_timeout_cancel(&(*d)->timeout);
511 
512         for (i = 0; i < ucv_array_length(cb_registry); i += 2) {
513                 r = (uc_resource_t *)ucv_array_get(cb_registry, i);
514 
515                 if (r && r->data == *d) {
516                         ucv_array_set(cb_registry, i, NULL);
517                         ucv_array_set(cb_registry, i + 1, NULL);
518                         break;
519                 }
520         }
521 
522         n_cb_active--;
523 
524         if (have_own_uloop && n_cb_active == 0)
525                 uloop_end();
526 
527         (*d)->callback = NULL;
528         (*d)->complete = true;
529 
530         return ucv_boolean_new(true);
531 }
532 
533 
534 static const uc_function_list_t global_fns[] = {
535         { "error",              uc_ubus_error },
536         { "connect",    uc_ubus_connect },
537 };
538 
539 static const uc_function_list_t conn_fns[] = {
540         { "list",               uc_ubus_list },
541         { "call",               uc_ubus_call },
542         { "defer",              uc_ubus_defer },
543         { "error",              uc_ubus_error },
544         { "disconnect", uc_ubus_disconnect },
545 };
546 
547 static const uc_function_list_t defer_fns[] = {
548         { "complete",   uc_ubus_defer_complete },
549         { "abort",              uc_ubus_defer_abort },
550 };
551 
552 
553 static void close_connection(void *ud) {
554         ubus_connection *conn = ud;
555 
556         blob_buf_free(&conn->buf);
557 
558         if (conn->ctx)
559                 ubus_free(conn->ctx);
560 
561         free(conn);
562 }
563 
564 static void close_deferred(void *ud) {
565         ubus_deferred *defer = ud;
566 
567         uloop_timeout_cancel(&defer->timeout);
568         ucv_put(defer->response);
569         free(defer);
570 }
571 
572 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
573 {
574         uc_function_list_register(scope, global_fns);
575 
576         conn_type = uc_type_declare(vm, "ubus.connection", conn_fns, close_connection);
577         defer_type = uc_type_declare(vm, "ubus.deferred", defer_fns, close_deferred);
578         cb_registry = ucv_array_new(vm);
579 
580         uc_vm_registry_set(vm, "ubus.cb_registry", cb_registry);
581 }
582 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt