summaryrefslogtreecommitdiffhomepage
path: root/lib
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2025-01-01 15:34:30 +0100
committerFelix Fietkau <nbd@nbd.name>2025-02-04 19:33:36 +0100
commit22b9523565a5b59d98e8ba2fa578fa5d994ecfdb (patch)
tree36b86eee9d2dd0c98c7da8a990c35ec55c60661f /lib
parent4acb960c90f77de933afeac0c6a8ba77209c68d2 (diff)
ubus: add support for receiving file descriptors in call and defer
Add a named parameter fd_cb, which is called when the callee returned a file descriptor. Signed-off-by: Felix Fietkau <nbd@nbd.name>
Diffstat (limited to 'lib')
-rw-r--r--lib/ubus.c70
1 files changed, 56 insertions, 14 deletions
diff --git a/lib/ubus.c b/lib/ubus.c
index 2e37f36..20e25d2 100644
--- a/lib/ubus.c
+++ b/lib/ubus.c
@@ -292,14 +292,14 @@ _uc_reg_clear(uc_vm_t *vm, const char *key, size_t idx, size_t nptrs)
}
-#define request_reg_add(vm, request, cb, conn, fd) \
- _uc_reg_add(vm, "ubus.requests", 4, request, cb, conn, fd)
+#define request_reg_add(vm, request, cb, fdcb, conn, fd) \
+ _uc_reg_add(vm, "ubus.requests", 5, request, cb, fdcb, conn, fd)
-#define request_reg_get(vm, idx, request, cb) \
- _uc_reg_get(vm, "ubus.requests", idx, 2, request, cb)
+#define request_reg_get(vm, idx, request, cb, fdcb) \
+ _uc_reg_get(vm, "ubus.requests", idx, 3, request, cb, fdcb)
#define request_reg_clear(vm, idx) \
- _uc_reg_clear(vm, "ubus.requests", idx, 4)
+ _uc_reg_clear(vm, "ubus.requests", idx, 5)
#define object_reg_add(vm, obj, msg, cb) \
@@ -619,7 +619,7 @@ uc_ubus_call_user_cb(uc_ubus_deferred_t *defer, int ret, uc_value_t *reply)
{
uc_value_t *this, *func;
- request_reg_get(defer->vm, defer->registry_index, &this, &func);
+ request_reg_get(defer->vm, defer->registry_index, &this, &func, NULL);
if (ucv_is_callable(func)) {
uc_vm_stack_push(defer->vm, ucv_get(this));
@@ -649,6 +649,28 @@ uc_ubus_call_data_cb(struct ubus_request *req, int type, struct blob_attr *msg)
}
static void
+uc_ubus_call_fd_cb(struct ubus_request *req, int fd)
+{
+ uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request);
+ uc_value_t *this, *func;
+
+ if (defer->complete)
+ return;
+
+ request_reg_get(defer->vm, defer->registry_index, &this, NULL, &func);
+ 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(fd));
+
+ if (uc_vm_call(defer->vm, true, 1) == EXCEPTION_NONE)
+ ucv_put(uc_vm_stack_pop(defer->vm));
+ else
+ uloop_end();
+ }
+}
+
+static void
uc_ubus_call_done_cb(struct ubus_request *req, int ret)
{
uc_ubus_deferred_t *defer = container_of(req, uc_ubus_deferred_t, request);
@@ -725,8 +747,9 @@ get_fd(uc_vm_t *vm, uc_value_t *val)
static uc_value_t *
uc_ubus_call(uc_vm_t *vm, size_t nargs)
{
- uc_value_t *objname, *funname, *funargs, *fd, *mret = NULL;
+ uc_value_t *objname, *funname, *funargs, *fd, *fdcb, *mret = NULL;
uc_ubus_call_res_t res = { 0 };
+ uc_ubus_deferred_t defer = {};
uc_ubus_connection_t *c;
enum ubus_msg_status rv;
int fd_val = -1;
@@ -739,7 +762,8 @@ uc_ubus_call(uc_vm_t *vm, size_t nargs)
"method", UC_STRING, REQUIRED, &funname,
"data", UC_OBJECT, OPTIONAL, &funargs,
"multiple_return", UC_BOOLEAN, OPTIONAL, &mret,
- "fd", 0, NAMED, &fd);
+ "fd", 0, NAMED, &fd,
+ "fd_cb", UC_CLOSURE, NAMED, &fdcb);
blob_buf_init(&c->buf, 0);
@@ -759,8 +783,22 @@ uc_ubus_call(uc_vm_t *vm, size_t nargs)
res.mret = ucv_is_truish(mret);
- rv = ubus_invoke_fd(&c->ctx, id, ucv_string_get(funname), c->buf.head,
- uc_ubus_call_cb, &res, c->timeout * 1000, fd_val);
+ rv = ubus_invoke_async_fd(&c->ctx, id, ucv_string_get(funname),
+ c->buf.head, &defer.request, fd_val);
+ defer.vm = vm;
+ defer.ctx = &c->ctx;
+ defer.request.data_cb = uc_ubus_call_cb;
+ defer.request.priv = &res;
+ if (ucv_is_callable(fdcb)) {
+ defer.request.fd_cb = uc_ubus_call_fd_cb;
+ defer.registry_index = request_reg_add(vm, NULL, NULL, ucv_get(fdcb), NULL, NULL);
+ }
+
+ if (rv == UBUS_STATUS_OK)
+ rv = ubus_complete_request(&c->ctx, &defer.request, c->timeout * 1000);
+
+ if (defer.request.fd_cb)
+ request_reg_clear(vm, defer.registry_index);
if (rv != UBUS_STATUS_OK)
err_return(rv, "Failed to invoke function '%s' on object '%s'",
@@ -772,7 +810,7 @@ uc_ubus_call(uc_vm_t *vm, size_t nargs)
static uc_value_t *
uc_ubus_defer(uc_vm_t *vm, size_t nargs)
{
- uc_value_t *objname, *funname, *funargs, *replycb, *fd, *conn, *res = NULL;
+ uc_value_t *objname, *funname, *funargs, *replycb, *fd, *fdcb, *conn, *res = NULL;
uc_ubus_deferred_t *defer;
uc_ubus_connection_t *c;
enum ubus_msg_status rv;
@@ -787,7 +825,8 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs)
"method", UC_STRING, REQUIRED, &funname,
"data", UC_OBJECT, OPTIONAL, &funargs,
"cb", UC_CLOSURE, OPTIONAL, &replycb,
- "fd", 0, NAMED, &fd);
+ "fd", 0, NAMED, &fd,
+ "fd_cb", UC_CLOSURE, NAMED, &fdcb);
blob_buf_init(&c->buf, 0);
@@ -816,7 +855,10 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs)
defer->ctx = &c->ctx;
defer->request.data_cb = uc_ubus_call_data_cb;
+ if (ucv_is_callable(fdcb))
+ defer->request.fd_cb = uc_ubus_call_fd_cb;
defer->request.complete_cb = uc_ubus_call_done_cb;
+
ubus_complete_request_async(&c->ctx, &defer->request);
defer->timeout.cb = uc_ubus_call_timeout_cb;
@@ -826,7 +868,7 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs)
frame = uc_vector_last(&vm->callframes);
conn = frame ? frame->ctx : NULL;
- defer->registry_index = request_reg_add(vm, ucv_get(res), ucv_get(replycb), ucv_get(conn), ucv_get(fd));
+ defer->registry_index = request_reg_add(vm, ucv_get(res), ucv_get(replycb), ucv_get(fdcb), ucv_get(conn), ucv_get(fd));
if (!uc_ubus_have_uloop()) {
have_own_uloop = true;
@@ -1361,7 +1403,7 @@ uc_ubus_handle_reply_common(struct ubus_context *ctx,
/* 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, NULL, NULL);
+ callctx->registry_index = request_reg_add(vm, ucv_get(reqobj), NULL, NULL, NULL, NULL);
}
/* Otherwise, when the function returned an object, treat it as