Add API to allow setting an exception handler for user provided callbacks Signed-off-by: Felix Fietkau <nbd@nbd.name>
		
			
				
	
	
		
			218 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From: Felix Fietkau <nbd@nbd.name>
 | 
						|
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 <nbd@nbd.name>
 | 
						|
---
 | 
						|
 | 
						|
--- 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);
 | 
						|
@@ -587,7 +621,7 @@ uc_ubus_call_user_cb(uc_ubus_deferred_t
 | 
						|
 		uc_vm_stack_push(vm, ucv_int64_new(ret));
 | 
						|
 		uc_vm_stack_push(vm, ucv_get(reply));
 | 
						|
 
 | 
						|
-		if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
 | 
						|
+		if (uc_ubus_vm_call(vm, true, 2))
 | 
						|
 			ucv_put(uc_vm_stack_pop(vm));
 | 
						|
 	}
 | 
						|
 
 | 
						|
@@ -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[] = {
 |