diff options
author | Jo-Philipp Wich <jo@mein.io> | 2022-03-03 23:39:55 +0100 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2022-03-07 00:01:43 +0100 |
commit | 1cb04f9b76e2710ca55909b61fc81d9b3c98bb2a (patch) | |
tree | 8f33c2d790305ea1e5415ec7e22e47c84511eefe /lib | |
parent | 0e859748a4e00cfad3bc50e386a06622066ee994 (diff) |
ubus: add object publishing, notify and subscribe support
Extend the ubus binding to cover ubus object publishing, notifications on
objects, as well as subscriber APIs.
Instantiating ubus objects:
obj = conn.publish("objname", {
methodname: {
args: { ...argspec... },
call: function(request) { ...method handler... }
},
...
}, function() { ...subscription status change handler... });
obj.notify(...);
obj.remove();
Emitting notifications:
obj.notify("notificationtype", { ...notification data... },
function(type, data) { ...data callback... },
function(idx, ret) { ...status callback... },
function() { ...completion callback... },
100 /* timeout */
);
Instantiating subscribers:
sub = conn.subscriber(
function(notify) { ...notification handler... },
function(id) { ...object gone handler... }
);
sub.subscribe("objname");
sub.unsubscribe("objname");
sub.remove();
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ubus.c | 1385 |
1 files changed, 1307 insertions, 78 deletions
@@ -24,28 +24,65 @@ #define err_return(err) do { last_error = err; return NULL; } while(0) static enum ubus_msg_status last_error = 0; +static uc_resource_type_t *subscriber_type; +static uc_resource_type_t *request_type; +static uc_resource_type_t *notify_type; +static uc_resource_type_t *object_type; static uc_resource_type_t *defer_type; static uc_resource_type_t *conn_type; -static uc_value_t *cb_registry; static uint64_t n_cb_active; static bool have_own_uloop; +static struct blob_buf buf; + typedef struct { int timeout; struct blob_buf buf; struct ubus_context *ctx; -} ubus_connection; +} uc_ubus_connection_t; typedef struct { - struct ubus_context *context; struct ubus_request request; struct uloop_timeout timeout; + struct ubus_context *ctx; + size_t registry_index; bool complete; uc_vm_t *vm; uc_value_t *callback; uc_value_t *response; -} ubus_deferred; +} uc_ubus_deferred_t; + +typedef struct { + struct ubus_object obj; + struct ubus_context *ctx; + size_t registry_index; + uc_vm_t *vm; +} uc_ubus_object_t; + +typedef struct { + struct ubus_request_data req; + struct uloop_timeout timeout; + struct ubus_context *ctx; + size_t registry_index; + bool replied; + uc_vm_t *vm; +} uc_ubus_request_t; + +typedef struct { + struct ubus_notify_request req; + struct ubus_context *ctx; + size_t registry_index; + bool complete; + uc_vm_t *vm; +} uc_ubus_notify_t; + +typedef struct { + struct ubus_subscriber sub; + struct ubus_context *ctx; + size_t registry_index; + uc_vm_t *vm; +} uc_ubus_subscriber_t; static uc_value_t * uc_ubus_error(uc_vm_t *vm, size_t nargs) @@ -61,6 +98,105 @@ uc_ubus_error(uc_vm_t *vm, size_t nargs) return errmsg; } +static void +_uc_reg_get(uc_vm_t *vm, const char *key, size_t idx, size_t nptrs, ...) +{ + uc_value_t *reg = uc_vm_registry_get(vm, key), **val; + va_list ap; + size_t i; + + va_start(ap, nptrs); + + for (i = 0; i < nptrs; i++) { + val = va_arg(ap, uc_value_t **); + + if (val) + *val = ucv_array_get(reg, idx + i); + } + + va_end(ap); +} + +static size_t +_uc_reg_add(uc_vm_t *vm, const char *key, size_t nptrs, ...) +{ + uc_value_t *reg = uc_vm_registry_get(vm, key); + size_t idx, i; + va_list ap; + + if (!reg) { + reg = ucv_array_new(vm); + uc_vm_registry_set(vm, key, reg); + } + + va_start(ap, nptrs); + + for (idx = 0;; idx += nptrs) { + if (ucv_array_get(reg, idx) == NULL) { + for (i = 0; i < nptrs; i++) + ucv_array_set(reg, idx + i, va_arg(ap, uc_value_t *)); + + break; + } + } + + va_end(ap); + + return idx; +} + +static void +_uc_reg_clear(uc_vm_t *vm, const char *key, size_t idx, size_t nptrs) +{ + uc_value_t *reg = uc_vm_registry_get(vm, key); + + while (nptrs > 0) { + nptrs--; + ucv_array_set(reg, idx + nptrs, NULL); + } +} + + +#define request_reg_add(vm, request, cb) \ + _uc_reg_add(vm, "ubus.requests", 2, request, cb) + +#define request_reg_get(vm, idx, request, cb) \ + _uc_reg_get(vm, "ubus.requests", idx, 2, request, cb) + +#define request_reg_clear(vm, idx) \ + _uc_reg_clear(vm, "ubus.requests", idx, 2) + + +#define object_reg_add(vm, obj, msg, cb) \ + _uc_reg_add(vm, "ubus.objects", 3, obj, msg, cb) + +#define object_reg_get(vm, idx, obj, msg, cb) \ + _uc_reg_get(vm, "ubus.objects", idx, 3, obj, msg, cb) + +#define object_reg_clear(vm, idx) \ + _uc_reg_clear(vm, "ubus.objects", idx, 3) + + +#define notify_reg_add(vm, notify, dcb, scb, ccb) \ + _uc_reg_add(vm, "ubus.notifications", 4, notify, dcb, scb, ccb) + +#define notify_reg_get(vm, idx, notify, dcb, scb, ccb) \ + _uc_reg_get(vm, "ubus.notifications", idx, 4, notify, dcb, scb, ccb) + +#define notify_reg_clear(vm, idx) \ + _uc_reg_clear(vm, "ubus.notifications", idx, 4) + + +#define subscriber_reg_add(vm, subscriber, ncb, rcb) \ + _uc_reg_add(vm, "ubus.subscribers", 3, subscriber, ncb, rcb) + +#define subscriber_reg_get(vm, idx, subscriber, ncb, rcb) \ + _uc_reg_get(vm, "ubus.subscribers", idx, 3, subscriber, ncb, rcb) + +#define subscriber_reg_clear(vm, idx) \ + _uc_reg_clear(vm, "ubus.subscribers", idx, 3) + + static uc_value_t * uc_blob_to_json(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **name); @@ -144,6 +280,80 @@ uc_blob_to_json(uc_vm_t *vm, struct blob_attr *attr, bool table, const char **na } } +static void +ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob); + +static void +ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob); + +static void +ucv_to_blob(const char *name, uc_value_t *val, struct blob_buf *blob) +{ + int64_t n; + void *c; + + switch (ucv_type(val)) { + case UC_NULL: + blobmsg_add_field(blob, BLOBMSG_TYPE_UNSPEC, name, NULL, 0); + break; + + case UC_BOOLEAN: + blobmsg_add_u8(blob, name, ucv_boolean_get(val)); + break; + + case UC_INTEGER: + n = ucv_int64_get(val); + + if (errno == ERANGE) + blobmsg_add_u64(blob, name, ucv_uint64_get(val)); + else if (n >= INT32_MIN && n <= INT32_MAX) + blobmsg_add_u32(blob, name, n); + else + blobmsg_add_u64(blob, name, n); + + break; + + case UC_DOUBLE: + blobmsg_add_double(blob, name, ucv_double_get(val)); + break; + + case UC_STRING: + blobmsg_add_string(blob, name, ucv_string_get(val)); + break; + + case UC_ARRAY: + c = blobmsg_open_array(blob, name); + ucv_array_to_blob(val, blob); + blobmsg_close_array(blob, c); + break; + + case UC_OBJECT: + c = blobmsg_open_table(blob, name); + ucv_object_to_blob(val, blob); + blobmsg_close_table(blob, c); + break; + + default: + break; + } +} + +static void +ucv_array_to_blob(uc_value_t *val, struct blob_buf *blob) +{ + size_t i; + + for (i = 0; i < ucv_array_length(val); i++) + ucv_to_blob(NULL, ucv_array_get(val, i), blob); +} + +static void +ucv_object_to_blob(uc_value_t *val, struct blob_buf *blob) +{ + ucv_object_foreach(val, k, v) + ucv_to_blob(k, v, blob); +} + static uc_value_t * uc_ubus_connect(uc_vm_t *vm, size_t nargs) @@ -151,7 +361,7 @@ uc_ubus_connect(uc_vm_t *vm, size_t nargs) uc_value_t *socket = uc_fn_arg(0); uc_value_t *timeout = uc_fn_arg(1); uc_value_t *co; - ubus_connection *c; + uc_ubus_connection_t *c; if ((socket && ucv_type(socket) != UC_STRING) || (timeout && ucv_type(timeout) != UC_INTEGER)) @@ -216,7 +426,7 @@ uc_ubus_objects_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) static uc_value_t * uc_ubus_list(uc_vm_t *vm, size_t nargs) { - ubus_connection **c = uc_fn_this("ubus.connection"); + uc_ubus_connection_t **c = uc_fn_this("ubus.connection"); uc_value_t *objname = uc_fn_arg(0); uc_value_t *res = NULL; enum ubus_msg_status rv; @@ -252,31 +462,23 @@ uc_ubus_call_cb(struct ubus_request *req, int type, struct blob_attr *msg) } static void -uc_ubus_invoke_async_callback(ubus_deferred *defer, int ret, uc_value_t *reply) +uc_ubus_call_user_cb(uc_ubus_deferred_t *defer, int ret, uc_value_t *reply) { - uc_resource_t *r; - size_t i; + uc_value_t *this, *func; + + request_reg_get(defer->vm, defer->registry_index, &this, &func); - if (defer->callback) { - uc_vm_stack_push(defer->vm, ucv_get(defer->callback)); + if (ucv_is_callable(func)) { + uc_vm_stack_push(defer->vm, ucv_get(this)); + uc_vm_stack_push(defer->vm, ucv_get(func)); uc_vm_stack_push(defer->vm, ucv_int64_new(ret)); uc_vm_stack_push(defer->vm, ucv_get(reply)); - if (uc_vm_call(defer->vm, false, 2) == EXCEPTION_NONE) + if (uc_vm_call(defer->vm, true, 2) == EXCEPTION_NONE) ucv_put(uc_vm_stack_pop(defer->vm)); - - defer->callback = NULL; } - for (i = 0; i < ucv_array_length(cb_registry); i += 2) { - r = (uc_resource_t *)ucv_array_get(cb_registry, i); - - if (r && r->data == defer) { - ucv_array_set(cb_registry, i, NULL); - ucv_array_set(cb_registry, i + 1, NULL); - break; - } - } + request_reg_clear(defer->vm, defer->registry_index); n_cb_active--; @@ -287,7 +489,7 @@ uc_ubus_invoke_async_callback(ubus_deferred *defer, int ret, uc_value_t *reply) static void uc_ubus_call_data_cb(struct ubus_request *req, int type, struct blob_attr *msg) { - ubus_deferred *defer = container_of(req, ubus_deferred, request); + uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request); if (defer->response == NULL) defer->response = uc_blob_array_to_json(defer->vm, blob_data(msg), blob_len(msg), true); @@ -296,7 +498,7 @@ uc_ubus_call_data_cb(struct ubus_request *req, int type, struct blob_attr *msg) static void uc_ubus_call_done_cb(struct ubus_request *req, int ret) { - ubus_deferred *defer = container_of(req, ubus_deferred, request); + uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request); if (defer->complete) return; @@ -304,21 +506,21 @@ uc_ubus_call_done_cb(struct ubus_request *req, int ret) defer->complete = true; uloop_timeout_cancel(&defer->timeout); - uc_ubus_invoke_async_callback(defer, ret, defer->response); + uc_ubus_call_user_cb(defer, ret, defer->response); } static void uc_ubus_call_timeout_cb(struct uloop_timeout *timeout) { - ubus_deferred *defer = container_of(timeout, ubus_deferred, timeout); + uc_ubus_deferred_t *defer = container_of(timeout, uc_ubus_deferred_t, timeout); if (defer->complete) return; defer->complete = true; - ubus_abort_request(defer->context, &defer->request); + ubus_abort_request(defer->ctx, &defer->request); - uc_ubus_invoke_async_callback(defer, UBUS_STATUS_TIMEOUT, NULL); + uc_ubus_call_user_cb(defer, UBUS_STATUS_TIMEOUT, NULL); } static bool @@ -337,7 +539,7 @@ uc_ubus_have_uloop(void) static uc_value_t * uc_ubus_call(uc_vm_t *vm, size_t nargs) { - ubus_connection **c = uc_fn_this("ubus.connection"); + uc_ubus_connection_t **c = uc_fn_this("ubus.connection"); uc_value_t *objname = uc_fn_arg(0); uc_value_t *funname = uc_fn_arg(1); uc_value_t *funargs = uc_fn_arg(2); @@ -382,17 +584,16 @@ uc_ubus_call(uc_vm_t *vm, size_t nargs) static uc_value_t * uc_ubus_defer(uc_vm_t *vm, size_t nargs) { - ubus_connection **c = uc_fn_this("ubus.connection"); + uc_ubus_connection_t **c = uc_fn_this("ubus.connection"); uc_value_t *objname = uc_fn_arg(0); uc_value_t *funname = uc_fn_arg(1); uc_value_t *funargs = uc_fn_arg(2); uc_value_t *replycb = uc_fn_arg(3); uc_value_t *res = NULL; enum ubus_msg_status rv; - ubus_deferred *defer; + uc_ubus_deferred_t *defer; json_object *o; uint32_t id; - size_t i; if (!c || !*c || !(*c)->ctx) err_return(UBUS_STATUS_CONNECTION_FAILED); @@ -426,8 +627,7 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs) if (rv == UBUS_STATUS_OK) { defer->vm = vm; - defer->context = (*c)->ctx; - defer->callback = replycb; + defer->ctx = (*c)->ctx; defer->request.data_cb = uc_ubus_call_data_cb; defer->request.complete_cb = uc_ubus_call_done_cb; @@ -438,14 +638,7 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs) res = uc_resource_new(defer_type, defer); - for (i = 0;; i += 2) { - if (ucv_array_get(cb_registry, i) == NULL) { - ucv_array_set(cb_registry, i, ucv_get(res)); - ucv_array_set(cb_registry, i + 1, ucv_get(replycb)); - n_cb_active++; - break; - } - } + defer->registry_index = request_reg_add(vm, ucv_get(res), ucv_get(replycb)); if (!uc_ubus_have_uloop()) { have_own_uloop = true; @@ -468,10 +661,997 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs) return res; } + +/* + * ubus object request context functions + * -------------------------------------------------------------------------- + */ + +static void +uc_ubus_request_finish(uc_ubus_request_t *callctx, int code, uc_value_t *reply) +{ + if (callctx->replied) + return; + + if (reply) { + blob_buf_init(&buf, 0); + ucv_object_to_blob(reply, &buf); + ubus_send_reply(callctx->ctx, &callctx->req, buf.head); + } + + callctx->replied = true; + + ubus_complete_deferred_request(callctx->ctx, &callctx->req, code); + request_reg_clear(callctx->vm, callctx->registry_index); +} + +static void +uc_ubus_request_timeout(struct uloop_timeout *timeout) +{ + uc_ubus_request_t *callctx = container_of(timeout, uc_ubus_request_t, timeout); + + uc_ubus_request_finish(callctx, UBUS_STATUS_TIMEOUT, NULL); +} + +static uc_value_t * +uc_ubus_request_reply(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_request_t **callctx = uc_fn_this("ubus.request"); + uc_value_t *reply = uc_fn_arg(0); + uc_value_t *rcode = uc_fn_arg(1); + int64_t code = UBUS_STATUS_OK; + + if (!callctx || !*callctx) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if (reply && ucv_type(reply) != UC_OBJECT) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if (rcode && ucv_type(rcode) != UC_INTEGER) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if ((*callctx)->replied) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if (rcode) { + code = ucv_int64_get(rcode); + + if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST) + code = UBUS_STATUS_UNKNOWN_ERROR; + } + + uc_ubus_request_finish(*callctx, code, reply); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_ubus_request_error(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_request_t **callctx = uc_fn_this("ubus.request"); + uc_value_t *rcode = uc_fn_arg(0); + int64_t code; + + if (!callctx || !*callctx) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if (ucv_type(rcode) != UC_INTEGER) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if ((*callctx)->replied) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + code = ucv_int64_get(rcode); + + if (errno == ERANGE || code < 0 || code > __UBUS_STATUS_LAST) + code = UBUS_STATUS_UNKNOWN_ERROR; + + uc_ubus_request_finish(*callctx, code, NULL); + + return ucv_boolean_new(true); +} + + +/* + * ubus object notify + * -------------------------------------------------------------------------- + */ + +static uc_value_t * +uc_ubus_notify_completed(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_notify_t **notifyctx = uc_fn_this("ubus.notify"); + + if (!notifyctx || !*notifyctx) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + return ucv_boolean_new((*notifyctx)->complete); +} + +static uc_value_t * +uc_ubus_notify_abort(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_notify_t **notifyctx = uc_fn_this("ubus.notify"); + + if (!notifyctx || !*notifyctx) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if ((*notifyctx)->complete) + return ucv_boolean_new(false); + + ubus_abort_request((*notifyctx)->ctx, &(*notifyctx)->req.req); + (*notifyctx)->complete = true; + + return ucv_boolean_new(true); +} + +static void +uc_ubus_object_notify_data_cb(struct ubus_notify_request *req, int type, struct blob_attr *msg) +{ + uc_ubus_notify_t *notifyctx = (uc_ubus_notify_t *)req; + uc_value_t *this, *func; + + notify_reg_get(notifyctx->vm, notifyctx->registry_index, &this, &func, NULL, NULL); + + if (ucv_is_callable(func)) { + uc_vm_stack_push(notifyctx->vm, ucv_get(this)); + uc_vm_stack_push(notifyctx->vm, ucv_get(func)); + uc_vm_stack_push(notifyctx->vm, ucv_int64_new(type)); + uc_vm_stack_push(notifyctx->vm, uc_blob_array_to_json(notifyctx->vm, blob_data(msg), blob_len(msg), true)); + + if (uc_vm_call(notifyctx->vm, true, 2) == EXCEPTION_NONE) + ucv_put(uc_vm_stack_pop(notifyctx->vm)); + } +} + +static void +uc_ubus_object_notify_status_cb(struct ubus_notify_request *req, int idx, int ret) +{ + uc_ubus_notify_t *notifyctx = (uc_ubus_notify_t *)req; + uc_value_t *this, *func; + + notify_reg_get(notifyctx->vm, notifyctx->registry_index, &this, NULL, &func, NULL); + + if (ucv_is_callable(func)) { + uc_vm_stack_push(notifyctx->vm, ucv_get(this)); + uc_vm_stack_push(notifyctx->vm, ucv_get(func)); + uc_vm_stack_push(notifyctx->vm, ucv_int64_new(idx)); + uc_vm_stack_push(notifyctx->vm, ucv_int64_new(ret)); + + if (uc_vm_call(notifyctx->vm, true, 2) == EXCEPTION_NONE) + ucv_put(uc_vm_stack_pop(notifyctx->vm)); + } +} + +static void +uc_ubus_object_notify_complete_cb(struct ubus_notify_request *req, int idx, int ret) +{ + uc_ubus_notify_t *notifyctx = (uc_ubus_notify_t *)req; + uc_value_t *this, *func; + + notify_reg_get(notifyctx->vm, notifyctx->registry_index, &this, NULL, NULL, &func); + + if (ucv_is_callable(func)) { + uc_vm_stack_push(notifyctx->vm, ucv_get(this)); + uc_vm_stack_push(notifyctx->vm, ucv_get(func)); + uc_vm_stack_push(notifyctx->vm, ucv_int64_new(idx)); + uc_vm_stack_push(notifyctx->vm, ucv_int64_new(ret)); + + if (uc_vm_call(notifyctx->vm, true, 2) == EXCEPTION_NONE) + ucv_put(uc_vm_stack_pop(notifyctx->vm)); + } + + notifyctx->complete = true; + + notify_reg_clear(notifyctx->vm, notifyctx->registry_index); +} + +static uc_value_t * +uc_ubus_object_notify(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_object_t **uuobj = uc_fn_this("ubus.object"); + uc_value_t *typename = uc_fn_arg(0); + uc_value_t *message = uc_fn_arg(1); + uc_value_t *data_cb = uc_fn_arg(2); + uc_value_t *status_cb = uc_fn_arg(3); + uc_value_t *complete_cb = uc_fn_arg(4); + uc_value_t *timeout = uc_fn_arg(5); + uc_ubus_notify_t *notifyctx; + uc_value_t *res; + int64_t t; + int rv; + + if (!uuobj || !*uuobj) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if (ucv_type(typename) != UC_STRING || + (message && ucv_type(message) != UC_OBJECT) || + (data_cb && !ucv_is_callable(data_cb)) || + (status_cb && !ucv_is_callable(status_cb)) || + (complete_cb && !ucv_is_callable(complete_cb)) || + (timeout && ucv_type(timeout) != UC_INTEGER)) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + t = timeout ? ucv_int64_get(timeout) : -1; + + if (errno) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + notifyctx = xalloc(sizeof(*notifyctx)); + notifyctx->vm = vm; + notifyctx->ctx = (*uuobj)->ctx; + + blob_buf_init(&buf, 0); + + if (message) + ucv_object_to_blob(message, &buf); + + rv = ubus_notify_async((*uuobj)->ctx, &(*uuobj)->obj, + ucv_string_get(typename), buf.head, + ¬ifyctx->req); + + if (rv != UBUS_STATUS_OK) { + free(notifyctx); + err_return(rv); + } + + notifyctx->req.data_cb = uc_ubus_object_notify_data_cb; + notifyctx->req.status_cb = uc_ubus_object_notify_status_cb; + notifyctx->req.complete_cb = uc_ubus_object_notify_complete_cb; + + res = uc_resource_new(notify_type, notifyctx); + + notifyctx->registry_index = notify_reg_add(vm, + ucv_get(res), ucv_get(data_cb), ucv_get(status_cb), ucv_get(complete_cb)); + + if (t >= 0) { + rv = ubus_complete_request((*uuobj)->ctx, ¬ifyctx->req.req, t); + + notify_reg_clear(vm, notifyctx->registry_index); + + ucv_put(res); + + return ucv_int64_new(rv); + } + + ubus_complete_request_async((*uuobj)->ctx, ¬ifyctx->req.req); + + return res; +} + + +/* + * ubus object remove + * -------------------------------------------------------------------------- + */ + +static int +uc_ubus_object_remove_common(uc_ubus_object_t *uuobj) +{ + int rv = ubus_remove_object(uuobj->ctx, &uuobj->obj); + + if (rv == UBUS_STATUS_OK) + object_reg_clear(uuobj->vm, uuobj->registry_index); + + return rv; +} + +static uc_value_t * +uc_ubus_object_remove(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_object_t **uuobj = uc_fn_this("ubus.object"); + int rv; + + if (!uuobj || !*uuobj) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + rv = uc_ubus_object_remove_common(*uuobj); + + if (rv != UBUS_STATUS_OK) + err_return(rv); + + return ucv_boolean_new(true); +} + + +/* + * ubus object subscription status + */ + +static uc_value_t * +uc_ubus_object_subscribed(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_object_t **uuobj = uc_fn_this("ubus.object"); + + if (!uuobj || !*uuobj) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + return ucv_boolean_new((*uuobj)->obj.has_subscribers); +} + + +/* + * ubus object method call handling + * -------------------------------------------------------------------------- + */ + +static int +uc_ubus_object_call_args(struct ubus_object *obj, const char *ubus_method_name, + struct blob_attr *msg, uc_value_t **res) +{ + uc_ubus_object_t *uuobj = (uc_ubus_object_t *)obj; + const struct ubus_method *method = NULL; + const struct blobmsg_hdr *hdr; + struct blob_attr *attr; + size_t len; + bool found; + int i; + + for (i = 0; i < obj->n_methods; i++) { + if (!strcmp(obj->methods[i].name, ubus_method_name)) { + method = &obj->methods[i]; + break; + } + } + + if (!method) + return UBUS_STATUS_METHOD_NOT_FOUND; + + len = blob_len(msg); + + __blob_for_each_attr(attr, blob_data(msg), len) { + if (!blobmsg_check_attr_len(attr, false, len)) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (!blob_is_extended(attr)) + return UBUS_STATUS_INVALID_ARGUMENT; + + hdr = blob_data(attr); + found = false; + + for (i = 0; i < method->n_policy; i++) { + if (blobmsg_namelen(hdr) != strlen(method->policy[i].name)) + continue; + + if (strcmp(method->policy[i].name, (char *)hdr->name)) + continue; + + /* named argument found but wrong type */ + if (blob_id(attr) != method->policy[i].type) + goto inval; + + found = true; + break; + } + + /* named argument not found in policy */ + if (!found) + goto inval; + } + + *res = uc_blob_array_to_json(uuobj->vm, blob_data(msg), blob_len(msg), true); + + return UBUS_STATUS_OK; + +inval: + *res = NULL; + + return UBUS_STATUS_INVALID_ARGUMENT; +} + +static uc_value_t * +uc_ubus_object_call_info(uc_vm_t *vm, + struct ubus_context *ctx, struct ubus_request_data *req, + struct ubus_object *obj, const char *ubus_method_name) +{ + uc_value_t *info, *o; + + info = ucv_object_new(vm); + + o = ucv_object_new(vm); + + ucv_object_add(o, "user", ucv_string_new(req->acl.user)); + ucv_object_add(o, "group", ucv_string_new(req->acl.group)); + + if (req->acl.object) + ucv_object_add(o, "object", ucv_string_new(req->acl.object)); + + ucv_object_add(info, "acl", o); + + o = ucv_object_new(vm); + + ucv_object_add(o, "id", ucv_int64_new(obj->id)); + + if (obj->name) + ucv_object_add(o, "name", ucv_string_new(obj->name)); + + if (obj->path) + ucv_object_add(o, "path", ucv_string_new(obj->path)); + + ucv_object_add(info, "object", o); + + if (ubus_method_name) + ucv_object_add(info, "method", ucv_string_new(ubus_method_name)); + + return info; +} + +static int +uc_ubus_handle_reply_common(struct ubus_context *ctx, + struct ubus_request_data *req, + uc_vm_t *vm, uc_value_t *this, uc_value_t *func, + uc_value_t *reqproto) +{ + uc_ubus_request_t *callctx; + uc_value_t *reqobj, *res; + int rv; + + /* allocate deferred method call context */ + callctx = xalloc(sizeof(*callctx)); + callctx->ctx = ctx; + callctx->vm = vm; + + ubus_defer_request(ctx, req, &callctx->req); + + /* create ucode request type object and set properties */ + reqobj = uc_resource_new(request_type, callctx); + + if (reqproto) + ucv_prototype_set(ucv_prototype_get(reqobj), reqproto); + + /* push object context, handler and request object onto stack */ + uc_vm_stack_push(vm, ucv_get(this)); + uc_vm_stack_push(vm, ucv_get(func)); + uc_vm_stack_push(vm, ucv_get(reqobj)); + + /* execute request handler function */ + switch (uc_vm_call(vm, true, 1)) { + case EXCEPTION_NONE: + res = uc_vm_stack_pop(vm); + + /* The handler function invoked a nested aync ubus request and returned it */ + if (ucv_resource_dataptr(res, "ubus.deferred")) { + /* Install guard timer in case the reply callback is never called */ + callctx->timeout.cb = uc_ubus_request_timeout; + uloop_timeout_set(&callctx->timeout, 10000 /* FIXME */); + + /* Add wrapped request context into registry to prevent GC'ing + * until reply or timeout occurred */ + callctx->registry_index = request_reg_add(vm, ucv_get(reqobj), NULL); + } + + + /* Otherwise, when the function returned an object, treat it as + * reply data and conclude deferred request immediately */ + else if (ucv_type(res) == UC_OBJECT) { + blob_buf_init(&buf, 0); + ucv_object_to_blob(res, &buf); + ubus_send_reply(ctx, &callctx->req, buf.head); + + ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_OK); + callctx->replied = true; + } + + /* If neither a deferred ubus request, nor a plain object were + * returned and if reqobj.reply() hasn't been called, immediately + * finish deferred request with UBUS_STATUS_NO_DATA. */ + else if (!callctx->replied) { + ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_NO_DATA); + callctx->replied = true; + } + + ucv_put(res); + break; + + /* if the handler function invoked exit(), forward exit status as ubus + * return code, map out of range values to UBUS_STATUS_UNKNOWN_ERROR. */ + case EXCEPTION_EXIT: + rv = vm->arg.s32; + + if (rv < UBUS_STATUS_OK || rv >= __UBUS_STATUS_LAST) + rv = UBUS_STATUS_UNKNOWN_ERROR; + + ubus_complete_deferred_request(ctx, &callctx->req, rv); + callctx->replied = true; + break; + + /* treat other exceptions as unknown error */ + default: + ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_UNKNOWN_ERROR); + callctx->replied = true; + break; + } + + /* release request object */ + ucv_put(reqobj); + + /* garbage collect */ + ucv_gc(vm); + + return UBUS_STATUS_OK; +} + +static int +uc_ubus_object_call_cb(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *ubus_method_name, + struct blob_attr *msg) +{ + uc_value_t *this, *func, *args = NULL, *reqproto, *methods; + uc_ubus_object_t *uuobj = (uc_ubus_object_t *)obj; + int rv; + + object_reg_get(uuobj->vm, uuobj->registry_index, &this, &methods, NULL); + + func = ucv_object_get(ucv_object_get(methods, ubus_method_name, NULL), "call", NULL); + + if (!ucv_is_callable(func)) + return UBUS_STATUS_METHOD_NOT_FOUND; + + rv = uc_ubus_object_call_args(obj, ubus_method_name, msg, &args); + + if (rv != UBUS_STATUS_OK) + return rv; + + reqproto = ucv_object_new(uuobj->vm); + + ucv_object_add(reqproto, "args", args); + ucv_object_add(reqproto, "info", + uc_ubus_object_call_info(uuobj->vm, ctx, req, obj, ubus_method_name)); + + return uc_ubus_handle_reply_common(ctx, req, uuobj->vm, this, func, reqproto); +} + + +/* + * ubus object registration + * -------------------------------------------------------------------------- + */ + +static void +uc_ubus_object_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj) +{ + uc_ubus_object_t *uuobj = (uc_ubus_object_t *)obj; + uc_value_t *this, *func; + + object_reg_get(uuobj->vm, uuobj->registry_index, &this, NULL, &func); + + uc_vm_stack_push(uuobj->vm, ucv_get(this)); + uc_vm_stack_push(uuobj->vm, ucv_get(func)); + + if (uc_vm_call(uuobj->vm, true, 0) == EXCEPTION_NONE) + ucv_put(uc_vm_stack_pop(uuobj->vm)); + else + uloop_end(); +} + +static bool +uc_ubus_object_methods_validate(uc_value_t *methods) +{ + uc_value_t *func, *args; + + if (ucv_type(methods) != UC_OBJECT) + return false; + + ucv_object_foreach(methods, ubus_method_name, ubus_method_definition) { + (void)ubus_method_name; + + func = ucv_object_get(ubus_method_definition, "call", NULL); + args = ucv_object_get(ubus_method_definition, "args", NULL); + + if (!ucv_is_callable(func)) + return false; + + if (args) { + if (ucv_type(args) != UC_OBJECT) + return false; + + ucv_object_foreach(args, ubus_argument_name, ubus_argument_typehint) { + (void)ubus_argument_name; + + switch (ucv_type(ubus_argument_typehint)) { + case UC_BOOLEAN: + case UC_INTEGER: + case UC_DOUBLE: + case UC_STRING: + case UC_ARRAY: + case UC_OBJECT: + continue; + + default: + return false; + } + } + } + } + + return true; +} + +static bool +uc_ubus_object_method_register(struct ubus_method *method, const char *ubus_method_name, + uc_value_t *ubus_method_arguments) +{ + struct blobmsg_policy *policy; + enum blobmsg_type type; + + method->name = strdup(ubus_method_name); + method->policy = calloc(ucv_object_length(ubus_method_arguments), sizeof(*method->policy)); + method->handler = uc_ubus_object_call_cb; + + if (!method->name || !method->policy) + return false; + + ucv_object_foreach(ubus_method_arguments, ubus_argument_name, ubus_argument_typehint) { + switch (ucv_type(ubus_argument_typehint)) { + case UC_BOOLEAN: + type = BLOBMSG_TYPE_INT8; + break; + + case UC_INTEGER: + switch (ucv_int64_get(ubus_argument_typehint)) { + case 8: + type = BLOBMSG_TYPE_INT8; + break; + + case 16: + type = BLOBMSG_TYPE_INT16; + break; + + case 64: + type = BLOBMSG_TYPE_INT64; + break; + + default: + type = BLOBMSG_TYPE_INT32; + break; + } + + break; + + case UC_DOUBLE: + type = BLOBMSG_TYPE_DOUBLE; + break; + + case UC_ARRAY: + type = BLOBMSG_TYPE_ARRAY; + break; + + case UC_OBJECT: + type = BLOBMSG_TYPE_TABLE; + break; + + default: + type = BLOBMSG_TYPE_STRING; + break; + } + + policy = (struct blobmsg_policy *)&method->policy[method->n_policy++]; + policy->type = type; + policy->name = strdup(ubus_argument_name); + + if (!policy->name) + return false; + } + + return true; +} + +static uc_ubus_object_t * +uc_ubus_object_register(struct ubus_context *ctx, const char *ubus_object_name, + uc_value_t *ubus_object_methods) +{ + const struct blobmsg_policy *policy; + uc_ubus_object_t *uuobj = NULL; + int rv = UBUS_STATUS_UNKNOWN_ERROR; + char *tptr, *tnptr, *onptr, *mptr; + struct ubus_method *method; + struct ubus_object *obj; + size_t typelen, namelen; + uc_value_t *args; + + namelen = strlen(ubus_object_name); + typelen = strlen("ucode-ubus-") + namelen; + + uuobj = calloc_a(sizeof(*uuobj), + &onptr, namelen + 1, + &mptr, ucv_object_length(ubus_object_methods) * sizeof(struct ubus_method), + &tptr, sizeof(struct ubus_object_type), + &tnptr, typelen + 1); + + if (!uuobj) + err_return(rv); + + snprintf(tnptr, typelen, "ucode-ubus-%s", ubus_object_name); + + method = (struct ubus_method *)mptr; + + obj = &uuobj->obj; + obj->name = strncpy(onptr, ubus_object_name, namelen); + obj->methods = method; + + if (ubus_object_methods) { + ucv_object_foreach(ubus_object_methods, ubus_method_name, ubus_method_definition) { + args = ucv_object_get(ubus_method_definition, "args", NULL); + + if (!uc_ubus_object_method_register(&method[obj->n_methods++], ubus_method_name, args)) + goto out; + } + } + + obj->type = (struct ubus_object_type *)tptr; + obj->type->name = tnptr; + obj->type->methods = obj->methods; + obj->type->n_methods = obj->n_methods; + + rv = ubus_add_object(ctx, obj); + + if (rv == UBUS_STATUS_OK) + return uuobj; + +out: + for (; obj->n_methods > 0; method++, obj->n_methods--) { + for (policy = method->policy; method->n_policy > 0; policy++, method->n_policy--) + free((char *)policy->name); + + free((char *)method->name); + free((char *)method->policy); + } + + free(uuobj); + + err_return(rv); /* ENOMEM */ +} + +static uc_value_t * +uc_ubus_publish(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_connection_t **c = uc_fn_this("ubus.connection"); + uc_value_t *objname = uc_fn_arg(0); + uc_value_t *methods = uc_fn_arg(1); + uc_value_t *subscribecb = uc_fn_arg(2); + uc_ubus_object_t *uuobj; + uc_value_t *res; + + if (!c || !*c || !(*c)->ctx) + err_return(UBUS_STATUS_CONNECTION_FAILED); + + if (ucv_type(objname) != UC_STRING) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if (!methods && !subscribecb) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if (methods && !uc_ubus_object_methods_validate(methods)) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if (subscribecb && !ucv_is_callable(subscribecb)) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + uuobj = uc_ubus_object_register((*c)->ctx, ucv_string_get(objname), methods); + + if (!uuobj) + return NULL; + + if (subscribecb) + uuobj->obj.subscribe_cb = uc_ubus_object_subscribe_cb; + + res = uc_resource_new(object_type, uuobj); + + uuobj->vm = vm; + uuobj->ctx = (*c)->ctx; + uuobj->registry_index = object_reg_add(vm, ucv_get(res), ucv_get(methods), ucv_get(subscribecb)); + + return res; +} + + +/* + * ubus subscriptions + * -------------------------------------------------------------------------- + */ + +static int +uc_ubus_subscriber_notify_cb(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct ubus_subscriber *sub = container_of(obj, struct ubus_subscriber, obj); + uc_ubus_subscriber_t *uusub = container_of(sub, uc_ubus_subscriber_t, sub); + uc_value_t *this, *func, *reqproto; + + subscriber_reg_get(uusub->vm, uusub->registry_index, &this, &func, NULL); + + if (!ucv_is_callable(func)) + return UBUS_STATUS_METHOD_NOT_FOUND; + + reqproto = ucv_object_new(uusub->vm); + + ucv_object_add(reqproto, "type", ucv_string_new(method)); + + ucv_object_add(reqproto, "data", + uc_blob_array_to_json(uusub->vm, blob_data(msg), blob_len(msg), true)); + + ucv_object_add(reqproto, "info", + uc_ubus_object_call_info(uusub->vm, ctx, req, obj, NULL)); + + return uc_ubus_handle_reply_common(ctx, req, uusub->vm, this, func, reqproto); +} + +static void +uc_ubus_subscriber_remove_cb(struct ubus_context *ctx, + struct ubus_subscriber *sub, uint32_t id) +{ + uc_ubus_subscriber_t *uusub = container_of(sub, uc_ubus_subscriber_t, sub); + uc_value_t *this, *func; + + subscriber_reg_get(uusub->vm, uusub->registry_index, &this, NULL, &func); + + if (!ucv_is_callable(func)) + return; + + uc_vm_stack_push(uusub->vm, ucv_get(this)); + uc_vm_stack_push(uusub->vm, ucv_get(func)); + uc_vm_stack_push(uusub->vm, ucv_uint64_new(id)); + + if (uc_vm_call(uusub->vm, true, 1) == EXCEPTION_NONE) + ucv_put(uc_vm_stack_pop(uusub->vm)); + else + uloop_end(); +} + +static uc_value_t * +uc_ubus_subscriber_subunsub_common(uc_vm_t *vm, size_t nargs, bool subscribe) +{ + uc_ubus_subscriber_t **uusub = uc_fn_this("ubus.subscriber"); + uc_value_t *objname = uc_fn_arg(0); + uint32_t id; + int rv; + + if (!uusub || !*uusub) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if (ucv_type(objname) != UC_STRING) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + rv = ubus_lookup_id((*uusub)->ctx, ucv_string_get(objname), &id); + + if (rv != UBUS_STATUS_OK) + err_return(rv); + + if (subscribe) + rv = ubus_subscribe((*uusub)->ctx, &(*uusub)->sub, id); + else + rv = ubus_unsubscribe((*uusub)->ctx, &(*uusub)->sub, id); + + if (rv != UBUS_STATUS_OK) + err_return(rv); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_ubus_subscriber_subscribe(uc_vm_t *vm, size_t nargs) +{ + return uc_ubus_subscriber_subunsub_common(vm, nargs, true); +} + +static uc_value_t * +uc_ubus_subscriber_unsubscribe(uc_vm_t *vm, size_t nargs) +{ + return uc_ubus_subscriber_subunsub_common(vm, nargs, false); +} + +static int +uc_ubus_subscriber_remove_common(uc_ubus_subscriber_t *uusub) +{ + int rv = ubus_unregister_subscriber(uusub->ctx, &uusub->sub); + + if (rv == UBUS_STATUS_OK) + subscriber_reg_clear(uusub->vm, uusub->registry_index); + + return rv; +} + +static uc_value_t * +uc_ubus_subscriber_remove(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_subscriber_t **uusub = uc_fn_this("ubus.subscriber"); + int rv; + + if (!uusub || !*uusub) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + rv = uc_ubus_subscriber_remove_common(*uusub); + + if (rv != UBUS_STATUS_OK) + err_return(rv); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_ubus_subscriber(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_connection_t **c = uc_fn_this("ubus.connection"); + uc_value_t *notify_cb = uc_fn_arg(0); + uc_value_t *remove_cb = uc_fn_arg(1); + uc_ubus_subscriber_t *uusub; + uc_value_t *res; + int rv; + + if (!c || !*c || !(*c)->ctx) + err_return(UBUS_STATUS_CONNECTION_FAILED); + + if (!notify_cb && !remove_cb) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + if ((notify_cb && !ucv_is_callable(notify_cb)) || + (remove_cb && !ucv_is_callable(remove_cb))) + err_return(UBUS_STATUS_INVALID_ARGUMENT); + + uusub = xalloc(sizeof(*uusub)); + uusub->vm = vm; + uusub->ctx = (*c)->ctx; + + rv = ubus_register_subscriber((*c)->ctx, &uusub->sub); + + if (rv != UBUS_STATUS_OK) { + free(uusub); + err_return(rv); + } + + if (notify_cb) + uusub->sub.cb = uc_ubus_subscriber_notify_cb; + + if (remove_cb) + uusub->sub.remove_cb = uc_ubus_subscriber_remove_cb; + + res = uc_resource_new(subscriber_type, uusub); + + uusub->registry_index = subscriber_reg_add(vm, + ucv_get(res), ucv_get(notify_cb), ucv_get(remove_cb)); + + return res; +} + + +/* + * connection methods + * -------------------------------------------------------------------------- + */ + +static uc_value_t * +uc_ubus_remove(uc_vm_t *vm, size_t nargs) +{ + uc_ubus_connection_t **c = uc_fn_this("ubus.connection"); + uc_ubus_subscriber_t **uusub; + uc_ubus_object_t **uuobj; + int rv; + + if (!c || !*c || !(*c)->ctx) + err_return(UBUS_STATUS_CONNECTION_FAILED); + + uusub = (uc_ubus_subscriber_t **)ucv_resource_dataptr(uc_fn_arg(0), "ubus.subscriber"); + uuobj = (uc_ubus_object_t **)ucv_resource_dataptr(uc_fn_arg(0), "ubus.object"); + + rv = UBUS_STATUS_INVALID_ARGUMENT; + + if (uusub && *uusub && (*uusub)->ctx == (*c)->ctx) + rv = uc_ubus_subscriber_remove_common(*uusub); + else if (uuobj && *uuobj && (*uuobj)->ctx == (*c)->ctx) + rv = uc_ubus_object_remove_common(*uuobj); + + if (rv != UBUS_STATUS_OK) + err_return(rv); + + return ucv_boolean_new(true); +} + + static uc_value_t * uc_ubus_disconnect(uc_vm_t *vm, size_t nargs) { - ubus_connection **c = uc_fn_this("ubus.connection"); + uc_ubus_connection_t **c = uc_fn_this("ubus.connection"); if (!c || !*c || !(*c)->ctx) err_return(UBUS_STATUS_CONNECTION_FAILED); @@ -483,9 +1663,9 @@ uc_ubus_disconnect(uc_vm_t *vm, size_t nargs) } static uc_value_t * -uc_ubus_defer_complete(uc_vm_t *vm, size_t nargs) +uc_ubus_defer_completed(uc_vm_t *vm, size_t nargs) { - ubus_deferred **d = uc_fn_this("ubus.deferred"); + uc_ubus_deferred_t **d = uc_fn_this("ubus.deferred"); if (!d || !*d) return NULL; @@ -496,9 +1676,7 @@ uc_ubus_defer_complete(uc_vm_t *vm, size_t nargs) static uc_value_t * uc_ubus_defer_abort(uc_vm_t *vm, size_t nargs) { - ubus_deferred **d = uc_fn_this("ubus.deferred"); - uc_resource_t *r; - size_t i; + uc_ubus_deferred_t **d = uc_fn_this("ubus.deferred"); if (!d || !*d) return NULL; @@ -506,25 +1684,16 @@ uc_ubus_defer_abort(uc_vm_t *vm, size_t nargs) if ((*d)->complete) return ucv_boolean_new(false); - ubus_abort_request((*d)->context, &(*d)->request); + ubus_abort_request((*d)->ctx, &(*d)->request); uloop_timeout_cancel(&(*d)->timeout); - for (i = 0; i < ucv_array_length(cb_registry); i += 2) { - r = (uc_resource_t *)ucv_array_get(cb_registry, i); - - if (r && r->data == *d) { - ucv_array_set(cb_registry, i, NULL); - ucv_array_set(cb_registry, i + 1, NULL); - break; - } - } + request_reg_clear((*d)->vm, (*d)->registry_index); n_cb_active--; if (have_own_uloop && n_cb_active == 0) uloop_end(); - (*d)->callback = NULL; (*d)->complete = true; return ucv_boolean_new(true); @@ -532,26 +1701,50 @@ uc_ubus_defer_abort(uc_vm_t *vm, size_t nargs) static const uc_function_list_t global_fns[] = { - { "error", uc_ubus_error }, - { "connect", uc_ubus_connect }, + { "error", uc_ubus_error }, + { "connect", uc_ubus_connect }, }; static const uc_function_list_t conn_fns[] = { - { "list", uc_ubus_list }, - { "call", uc_ubus_call }, - { "defer", uc_ubus_defer }, - { "error", uc_ubus_error }, - { "disconnect", uc_ubus_disconnect }, + { "list", uc_ubus_list }, + { "call", uc_ubus_call }, + { "defer", uc_ubus_defer }, + { "publish", uc_ubus_publish }, + { "remove", uc_ubus_remove }, + { "subscriber", uc_ubus_subscriber }, + { "error", uc_ubus_error }, + { "disconnect", uc_ubus_disconnect }, }; static const uc_function_list_t defer_fns[] = { - { "complete", uc_ubus_defer_complete }, - { "abort", uc_ubus_defer_abort }, + { "completed", uc_ubus_defer_completed }, + { "abort", uc_ubus_defer_abort }, +}; + +static const uc_function_list_t object_fns[] = { + { "subscribed", uc_ubus_object_subscribed }, + { "notify", uc_ubus_object_notify }, + { "remove", uc_ubus_object_remove }, +}; + +static const uc_function_list_t request_fns[] = { + { "reply", uc_ubus_request_reply }, + { "error", uc_ubus_request_error }, +}; + +static const uc_function_list_t notify_fns[] = { + { "completed", uc_ubus_notify_completed }, + { "abort", uc_ubus_notify_abort }, }; +static const uc_function_list_t subscriber_fns[] = { + { "subscribe", uc_ubus_subscriber_subscribe }, + { "unsubscribe", uc_ubus_subscriber_unsubscribe }, + { "remove", uc_ubus_subscriber_remove }, +}; -static void close_connection(void *ud) { - ubus_connection *conn = ud; +static void free_connection(void *ud) { + uc_ubus_connection_t *conn = ud; blob_buf_free(&conn->buf); @@ -561,21 +1754,57 @@ static void close_connection(void *ud) { free(conn); } -static void close_deferred(void *ud) { - ubus_deferred *defer = ud; +static void free_deferred(void *ud) { + uc_ubus_deferred_t *defer = ud; uloop_timeout_cancel(&defer->timeout); ucv_put(defer->response); free(defer); } +static void free_object(void *ud) { + uc_ubus_object_t *uuobj = ud; + struct ubus_object *obj = &uuobj->obj; + int i, j; + + for (i = 0; i < obj->n_methods; i++) { + for (j = 0; j < obj->methods[i].n_policy; j++) + free((char *)obj->methods[i].policy[j].name); + + free((char *)obj->methods[i].name); + free((char *)obj->methods[i].policy); + } + + free(uuobj); +} + +static void free_request(void *ud) { + uc_ubus_request_t *callctx = ud; + + uloop_timeout_cancel(&callctx->timeout); + free(callctx); +} + +static void free_notify(void *ud) { + uc_ubus_notify_t *notifyctx = ud; + + free(notifyctx); +} + +static void free_subscriber(void *ud) { + uc_ubus_subscriber_t *subscriber = ud; + + free(subscriber); +} + void uc_module_init(uc_vm_t *vm, uc_value_t *scope) { uc_function_list_register(scope, global_fns); - conn_type = uc_type_declare(vm, "ubus.connection", conn_fns, close_connection); - defer_type = uc_type_declare(vm, "ubus.deferred", defer_fns, close_deferred); - cb_registry = ucv_array_new(vm); - - uc_vm_registry_set(vm, "ubus.cb_registry", cb_registry); + conn_type = uc_type_declare(vm, "ubus.connection", conn_fns, free_connection); + defer_type = uc_type_declare(vm, "ubus.deferred", defer_fns, free_deferred); + object_type = uc_type_declare(vm, "ubus.object", object_fns, free_object); + notify_type = uc_type_declare(vm, "ubus.notify", notify_fns, free_notify); + request_type = uc_type_declare(vm, "ubus.request", request_fns, free_request); + subscriber_type = uc_type_declare(vm, "ubus.subscriber", subscriber_fns, free_subscriber); } |