diff options
author | Felix Fietkau <nbd@nbd.name> | 2025-01-01 15:58:26 +0100 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2025-02-04 19:33:35 +0100 |
commit | 17dbf0bef4b6471e55331e86fc5983bd3e2602de (patch) | |
tree | 8f0963b5a76b8fdee4d5e2e7b911c056c6e2bad6 /lib | |
parent | 9da8b8aa4ad0ff12bc150892965cedf1a507064d (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.c | 91 |
1 files changed, 80 insertions, 11 deletions
@@ -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[] = { |