summaryrefslogtreecommitdiffhomepage
path: root/lib
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2025-01-01 15:58:26 +0100
committerFelix Fietkau <nbd@nbd.name>2025-02-04 19:33:35 +0100
commit17dbf0bef4b6471e55331e86fc5983bd3e2602de (patch)
tree8f0963b5a76b8fdee4d5e2e7b911c056c6e2bad6 /lib
parent9da8b8aa4ad0ff12bc150892965cedf1a507064d (diff)
ubus: add request get_fd/set_fd methods
This can be used to send and receive file descriptors from within an object method call. Signed-off-by: Felix Fietkau <nbd@nbd.name>
Diffstat (limited to 'lib')
-rw-r--r--lib/ubus.c91
1 files changed, 80 insertions, 11 deletions
diff --git a/lib/ubus.c b/lib/ubus.c
index b49a134..330ef5d 100644
--- a/lib/ubus.c
+++ b/lib/ubus.c
@@ -15,6 +15,7 @@
*/
#include <unistd.h>
+#include <limits.h>
#include <libubus.h>
#include <libubox/blobmsg.h>
@@ -693,6 +694,34 @@ uc_ubus_have_uloop(void)
return active;
}
+static int
+get_fd(uc_vm_t *vm, uc_value_t *val)
+{
+ uc_value_t *fn;
+ int64_t n;
+
+ fn = ucv_property_get(val, "fileno");
+ if (ucv_is_callable(fn)) {
+ uc_vm_stack_push(vm, ucv_get(val));
+ uc_vm_stack_push(vm, ucv_get(fn));
+
+ if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE)
+ return -1;
+
+ val = uc_vm_stack_pop(vm);
+ n = ucv_int64_get(val);
+ ucv_put(val);
+ }
+ else {
+ n = ucv_int64_get(val);
+ }
+
+ if (errno || n < 0 || n > (int64_t)INT_MAX)
+ return -1;
+
+ return (int)n;
+}
+
static uc_value_t *
uc_ubus_call(uc_vm_t *vm, size_t nargs)
{
@@ -815,6 +844,19 @@ uc_ubus_defer(uc_vm_t *vm, size_t nargs)
*/
static void
+uc_ubus_request_finish_common(uc_ubus_request_t *callctx, int code)
+{
+ int fd;
+
+ fd = ubus_request_get_caller_fd(&callctx->req);
+ if (fd >= 0)
+ close(fd);
+
+ callctx->replied = true;
+ ubus_complete_deferred_request(callctx->ctx, &callctx->req, code);
+}
+
+static void
uc_ubus_request_finish(uc_ubus_request_t *callctx, int code, uc_value_t *reply)
{
if (callctx->replied)
@@ -826,9 +868,7 @@ uc_ubus_request_finish(uc_ubus_request_t *callctx, int code, uc_value_t *reply)
ubus_send_reply(callctx->ctx, &callctx->req, buf.head);
}
- callctx->replied = true;
-
- ubus_complete_deferred_request(callctx->ctx, &callctx->req, code);
+ uc_ubus_request_finish_common(callctx, code);
request_reg_clear(callctx->vm, callctx->registry_index);
}
@@ -882,6 +922,34 @@ uc_ubus_request_defer(uc_vm_t *vm, size_t nargs)
}
static uc_value_t *
+uc_ubus_request_get_fd(uc_vm_t *vm, size_t nargs)
+{
+ uc_ubus_request_t *callctx = uc_fn_thisval("ubus.request");
+
+ if (!callctx)
+ return NULL;
+
+ return ucv_int64_new(ubus_request_get_caller_fd(&callctx->req));
+}
+
+static uc_value_t *
+uc_ubus_request_set_fd(uc_vm_t *vm, size_t nargs)
+{
+ uc_ubus_request_t *callctx = uc_fn_thisval("ubus.request");
+ int fd;
+
+ if (!callctx)
+ err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid call context");
+
+ fd = get_fd(vm, uc_fn_arg(0));
+ if (fd < 0)
+ err_return(UBUS_STATUS_INVALID_ARGUMENT, "Invalid file descriptor");
+
+ ubus_request_set_fd(callctx->ctx, &callctx->req, fd);
+ 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");
@@ -1251,6 +1319,9 @@ uc_ubus_handle_reply_common(struct ubus_context *ctx,
ubus_defer_request(ctx, req, &callctx->req);
+ /* fd is copied to deferred request. ensure it does not get closed early */
+ ubus_request_get_caller_fd(req);
+
/* create ucode request type object and set properties */
reqobj = uc_resource_new(request_type, callctx);
@@ -1285,8 +1356,7 @@ uc_ubus_handle_reply_common(struct ubus_context *ctx,
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;
+ uc_ubus_request_finish_common(callctx, UBUS_STATUS_OK);
}
/* If neither a deferred ubus request, nor a plain object were
@@ -1302,8 +1372,7 @@ uc_ubus_handle_reply_common(struct ubus_context *ctx,
rv = UBUS_STATUS_UNKNOWN_ERROR;
}
- ubus_complete_deferred_request(ctx, &callctx->req, rv);
- callctx->replied = true;
+ uc_ubus_request_finish_common(callctx, rv);
}
ucv_put(res);
@@ -1317,15 +1386,13 @@ uc_ubus_handle_reply_common(struct ubus_context *ctx,
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;
+ uc_ubus_request_finish_common(callctx, rv);
break;
/* treat other exceptions as fatal and halt uloop */
default:
- ubus_complete_deferred_request(ctx, &callctx->req, UBUS_STATUS_UNKNOWN_ERROR);
+ uc_ubus_request_finish_common(callctx, UBUS_STATUS_UNKNOWN_ERROR);
uloop_end();
- callctx->replied = true;
break;
}
@@ -2064,6 +2131,8 @@ static const uc_function_list_t request_fns[] = {
{ "reply", uc_ubus_request_reply },
{ "error", uc_ubus_request_error },
{ "defer", uc_ubus_request_defer },
+ { "get_fd", uc_ubus_request_get_fd },
+ { "set_fd", uc_ubus_request_set_fd },
};
static const uc_function_list_t notify_fns[] = {