From: Felix Fietkau Date: Wed, 11 Jun 2025 18:40:10 +0200 Subject: [PATCH] ubus: add exception_handler_set function This allows calling the provided handler on exceptions, avoiding the existing behavior of calling uloop_end(). Signed-off-by: Felix Fietkau --- --- a/lib/ubus.c +++ b/lib/ubus.c @@ -573,6 +573,40 @@ uc_ubus_call_cb(struct ubus_request *req } static void +uc_ubus_vm_handle_exception(uc_vm_t *vm) +{ + uc_value_t *exh, *val; + + exh = uc_vm_registry_get(vm, "ubus.ex_handler"); + if (!ucv_is_callable(exh)) + goto error; + + val = uc_vm_exception_value(vm); + uc_vm_stack_push(vm, ucv_get(exh)); + uc_vm_stack_push(vm, ucv_get(val)); + + if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE) + goto error; + + ucv_put(uc_vm_stack_pop(vm)); + return; + +error: + uloop_end(); +} + +static bool +uc_ubus_vm_call(uc_vm_t *vm, bool mcall, size_t nargs) +{ + if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE) + return true; + + uc_ubus_vm_handle_exception(vm); + + return false; +} + +static void uc_ubus_call_user_cb(uc_ubus_deferred_t *defer, int ret, uc_value_t *reply) { uc_value_t *this = ucv_get(defer->res); @@ -623,10 +657,8 @@ uc_ubus_call_data_user_cb(struct ubus_re uc_vm_stack_push(vm, ucv_get(func)); uc_vm_stack_push(vm, ucv_get(reply)); - if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) + if (uc_ubus_vm_call(vm, true, 1)) ucv_put(uc_vm_stack_pop(vm)); - else - uloop_end(); } } @@ -645,10 +677,8 @@ uc_ubus_call_fd_cb(struct ubus_request * uc_vm_stack_push(vm, ucv_get(func)); uc_vm_stack_push(vm, ucv_int64_new(fd)); - if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) + if (uc_ubus_vm_call(vm, true, 1)) ucv_put(uc_vm_stack_pop(vm)); - else - uloop_end(); } } @@ -927,10 +957,8 @@ uc_ubus_defer_common(uc_vm_t *vm, uc_ubu uc_vm_stack_push(vm, ucv_get(replycb)); uc_vm_stack_push(vm, ucv_int64_new(rv)); - if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) + if (uc_ubus_vm_call(vm, false, 1)) ucv_put(uc_vm_stack_pop(vm)); - else - uloop_end(); ucv_put(res->res); } @@ -1199,10 +1227,8 @@ uc_ubus_object_notify_data_cb(struct ubu uc_vm_stack_push(vm, ucv_int64_new(type)); uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true)); - if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) + if (uc_ubus_vm_call(vm, true, 2)) ucv_put(uc_vm_stack_pop(vm)); - else - uloop_end(); } } @@ -1222,10 +1248,8 @@ uc_ubus_object_notify_status_cb(struct u uc_vm_stack_push(vm, ucv_int64_new(idx)); uc_vm_stack_push(vm, ucv_int64_new(ret)); - if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) + if (uc_ubus_vm_call(vm, true, 2)) ucv_put(uc_vm_stack_pop(vm)); - else - uloop_end(); } } @@ -1245,10 +1269,8 @@ uc_ubus_object_notify_complete_cb(struct uc_vm_stack_push(vm, ucv_int64_new(idx)); uc_vm_stack_push(vm, ucv_int64_new(ret)); - if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) + if (uc_ubus_vm_call(vm, true, 2)) ucv_put(uc_vm_stack_pop(vm)); - else - uloop_end(); } notifyctx->complete = true; @@ -1579,7 +1601,7 @@ uc_ubus_handle_reply_common(struct ubus_ /* treat other exceptions as fatal and halt uloop */ default: uc_ubus_request_finish_common(callctx, UBUS_STATUS_UNKNOWN_ERROR); - uloop_end(); + uc_ubus_vm_handle_exception(vm); break; } @@ -1638,10 +1660,8 @@ uc_ubus_object_subscribe_cb(struct ubus_ uc_vm_stack_push(uuobj->vm, ucv_get(uuobj->res)); uc_vm_stack_push(uuobj->vm, ucv_get(func)); - if (uc_vm_call(uuobj->vm, true, 0) == EXCEPTION_NONE) + if (uc_ubus_vm_call(uuobj->vm, true, 0)) ucv_put(uc_vm_stack_pop(uuobj->vm)); - else - uloop_end(); } static bool @@ -1917,10 +1937,8 @@ uc_ubus_listener_cb(struct ubus_context uc_vm_stack_push(vm, ucv_string_new(type)); uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true)); - if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE) + if (uc_ubus_vm_call(vm, true, 2)) ucv_put(uc_vm_stack_pop(vm)); - else - uloop_end(); } static uc_value_t * @@ -2040,10 +2058,8 @@ uc_ubus_subscriber_remove_cb(struct ubus uc_vm_stack_push(vm, ucv_get(func)); uc_vm_stack_push(vm, ucv_uint64_new(id)); - if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) + if (uc_ubus_vm_call(vm, true, 1)) ucv_put(uc_vm_stack_pop(vm)); - else - uloop_end(); } static uc_value_t * @@ -2334,10 +2350,8 @@ uc_ubus_channel_disconnect_cb(struct ubu uc_vm_stack_push(c->vm, ucv_get(c->res)); uc_vm_stack_push(c->vm, ucv_get(func)); - if (uc_vm_call(c->vm, true, 0) == EXCEPTION_NONE) + if (uc_ubus_vm_call(c->vm, true, 0)) ucv_put(uc_vm_stack_pop(c->vm)); - else - uloop_end(); } blob_buf_free(&c->buf); @@ -2438,10 +2452,25 @@ uc_ubus_channel_connect(uc_vm_t *vm, siz } +static uc_value_t * +uc_ubus_exception_handler_set(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + + if (arg && !ucv_is_callable(arg)) + return NULL; + + uc_vm_registry_set(vm, "ubus.ex_handler", ucv_get(arg)); + + return ucv_boolean_new(true); +} + + static const uc_function_list_t global_fns[] = { { "error", uc_ubus_error }, { "connect", uc_ubus_connect }, { "open_channel", uc_ubus_channel_connect }, + { "exception_handler_set", uc_ubus_exception_handler_set }, }; static const uc_function_list_t conn_fns[] = {