diff mbox series

[4/5] swuforward: add generic uploader

Message ID 20240309151159.8426-5-stefano.babic@swupdate.org
State Accepted
Headers show
Series Introduce generic Uploader | expand

Commit Message

Stefano Babic March 9, 2024, 3:11 p.m. UTC
The handler is used to push a SWU to other systems running SWUpdate.
This patch introduce the concept of generic uploader, and the handler
can upload an artifact (not only a SWU) to a URL. The handler will still
connect via Websocket to the webserver, and it will parse the answer. To
make it generic, the answer can be parsed by a custom Lua function, that
should be loaded previously via embedded-script or via a script, or even
via swupdate_handlers.lua at the startup.

Signed-off-by: Stefano Babic <stefano.babic@swupdate.org>
---
 corelib/lua_interface.c       |  4 +-
 handlers/swuforward-ws.c      | 97 +++++++++++++++++++++++++----------
 handlers/swuforward_handler.c | 17 ++++++
 handlers/swuforward_handler.h |  3 ++
 4 files changed, 92 insertions(+), 29 deletions(-)

--
2.34.1
diff mbox series

Patch

diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
index fbdb7d05..90aba6eb 100644
--- a/corelib/lua_interface.c
+++ b/corelib/lua_interface.c
@@ -1608,7 +1608,6 @@  int lua_handler_fn(lua_State *L, const char *fcn, const char *parms)
 		TRACE("Script : no %s in script, exiting", fcn);
 		return -1;
 	}
-	TRACE("Prepared to run %s", fcn);

 	/*
 	 * passing arguments
@@ -1629,7 +1628,8 @@  int lua_handler_fn(lua_State *L, const char *fcn, const char *parms)

 	lua_pop(L, 1); /* clear stack */

-	TRACE("Script returns %d", ret);
+	if (loglevel >= DEBUGLEVEL)
+		TRACE("Script returns %d", ret);

 	return ret;
 }
diff --git a/handlers/swuforward-ws.c b/handlers/swuforward-ws.c
index 2cd98002..ed881fad 100644
--- a/handlers/swuforward-ws.c
+++ b/handlers/swuforward-ws.c
@@ -35,16 +35,47 @@  struct wsconn {

 #define TEXTRANGE_TO_STR(f)	(f.first == NULL ? NULL : substring(f.first, 0, f.afterLast - f.first))

-static int callback_ws_swupdate(struct lws *wsi, enum lws_callback_reasons reason,
-				      void *user, void *in, size_t len)
+static void swupdate_web_answer(struct wsconn *ws, void *in, size_t len)
 {
-	struct wsconn *ws = (struct wsconn *)user;
 	struct json_tokener *json_tokenizer;
 	enum json_tokener_error json_res;
 	struct json_object *json_root;

-	switch (reason) {
+	json_tokenizer = json_tokener_new();
+
+	do {
+		json_root = json_tokener_parse_ex(
+			json_tokenizer, in, len);
+	} while ((json_res = json_tokener_get_error(json_tokenizer)) ==
+		json_tokener_continue);
+	if (json_res != json_tokener_success) {
+		ERROR("Error while parsing answer from %s returned JSON data: %s",
+			ws ? ws->conn->url : "", json_tokener_error_desc(json_res));
+	} else {
+		const char *reply_result = json_get_value(json_root, "type");
+		if (reply_result && !strcmp(reply_result, "status")) {
+			const char *status = json_get_value(json_root, "status");
+			if (!strcmp(status, "SUCCESS"))
+				ws->conn->SWUpdateStatus = SUCCESS;
+			if (!strcmp(status, "FAILURE"))
+				ws->conn->SWUpdateStatus = FAILURE;
+			TRACE("Change status on %s : %s", ws->conn->url,
+				status);
+		}
+		if (reply_result && !strcmp(reply_result, "message")) {
+			const char *text = json_get_value(json_root, "text");
+			TRACE("%s : %s", ws->conn->url, text);
+		}
+	}
+	json_tokener_free(json_tokenizer);
+}
+
+static int callback_ws_swupdate(struct lws *wsi, enum lws_callback_reasons reason,
+				      void *user, void *in, size_t len)
+{
+	struct wsconn *ws = (struct wsconn *)user;

+	switch (reason) {
 	/* because we are protocols[0] ... */
 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
 		ERROR("WS Client Connection Error to : %s", ws->conn->url);
@@ -58,33 +89,45 @@  static int callback_ws_swupdate(struct lws *wsi, enum lws_callback_reasons reaso
 		break;

 	case LWS_CALLBACK_CLIENT_RECEIVE:
-		json_tokenizer = json_tokener_new();
-
-		do {
-			json_root = json_tokener_parse_ex(
-				json_tokenizer, in, len);
-		} while ((json_res = json_tokener_get_error(json_tokenizer)) ==
-			json_tokener_continue);
-		if (json_res != json_tokener_success) {
-			ERROR("Error while parsing answer from %s returned JSON data: %s",
-				ws ? ws->conn->url : "", json_tokener_error_desc(json_res));
-		} else {
-			const char *reply_result = json_get_value(json_root, "type");
-			if (reply_result && !strcmp(reply_result, "status")) {
-				const char *status = json_get_value(json_root, "status");
-				if (!strcmp(status, "SUCCESS"))
+		/*
+		 * If it is not connected to SWUpdate's Webserver
+		 * call a custom Lua function that should be loaded
+		 * in advance
+		 */
+		if (ws->conn->fnparser && strlen(ws->conn->fnparser)) {
+			/*
+			 * First convert the incoming data
+			 * to a "data" stucture (string)
+			 * that is passed to the script.
+			 * Raw / Binary data aren't supported
+			 */
+			char *data = (char *) calloc(1, len + 1);
+			if (!data) {
+				ERROR("OOM when allocating buffer for Lua Custom Script");
+				ws->conn->SWUpdateStatus = FAILURE;
+			} else {
+				int ret;
+				memcpy(data, in, len);
+				ret = lua_handler_fn(ws->conn->L, ws->conn->fnparser, data);
+				switch (ret) {
+				case RUN:
+					break;
+				case FAILURE:
+					ws->conn->SWUpdateStatus = FAILURE;
+					break;
+				case SUCCESS:
 					ws->conn->SWUpdateStatus = SUCCESS;
-				if (!strcmp(status, "FAILURE"))
+					break;
+				default:
+					WARN("Error parsing answer from Webserver, %d", ret);
 					ws->conn->SWUpdateStatus = FAILURE;
-				TRACE("Change status on %s : %s", ws->conn->url,
-					status);
-			}
-			if (reply_result && !strcmp(reply_result, "message")) {
-				const char *text = json_get_value(json_root, "text");
-				TRACE("%s : %s", ws->conn->url, text);
+					break;
+				}
+				free(data);
 			}
+		} else {
+			swupdate_web_answer(ws, in, len);
 		}
-		json_tokener_free(json_tokenizer);
 		break;

 	case LWS_CALLBACK_CLIENT_CLOSED:
diff --git a/handlers/swuforward_handler.c b/handlers/swuforward_handler.c
index a4517430..4d346bfa 100644
--- a/handlers/swuforward_handler.c
+++ b/handlers/swuforward_handler.c
@@ -293,6 +293,7 @@  static int install_remote_swu(struct img_type *img,
 	struct dict_list_elem *url;
 	struct dict_list *urls;
 	int index = 0;
+	const char *fn_parse_answer = NULL;
 	pthread_attr_t attr;
 	int thread_ret = -1;

@@ -317,6 +318,17 @@  static int install_remote_swu(struct img_type *img,
 		return -EINVAL;
 	}

+	/*
+	 * Check if a custom function is set to parse the
+	 * answer form the Webserver
+	 */
+	fn_parse_answer = dict_get_value(&img->properties, "parser-function");
+	if (fn_parse_answer && !img->L) {
+		ERROR("Custom parser requires to enable Lua, exiting..");
+		ret = FAILURE;
+		goto handler_exit;
+	}
+
 	/* Reset list of connections */
 	LIST_INIT(&priv.conns);

@@ -344,9 +356,14 @@  static int install_remote_swu(struct img_type *img,
 			goto handler_exit;
 		}

+		/*
+		 * Set parameters to each connection structure
+		 */
 		conn->url = url->value;
 		conn->total_bytes = img->size;
 		conn->SWUpdateStatus = IDLE;
+		conn->L = img->L;
+		conn->fnparser = fn_parse_answer;

 		LIST_INSERT_HEAD(&priv.conns, conn, next);

diff --git a/handlers/swuforward_handler.h b/handlers/swuforward_handler.h
index 78d30731..0c32a173 100644
--- a/handlers/swuforward_handler.h
+++ b/handlers/swuforward_handler.h
@@ -12,6 +12,7 @@ 
 #include "bsdqueue.h"
 #include "channel_curl.h"
 #include "channel.h"
+#include "lua_util.h"

 /*
  * The Webserver in SWUpdate expets a custom header
@@ -52,6 +53,8 @@  struct curlconn {
 	size_t total_bytes;	/* size of SWU image */
 	int fifo[2];		/* Pipe for IPC */
 	char *url;		/* URL for forwarding */
+	const char *fnparser;	/* Parser for the answer via ws */
+	lua_State *L;		/* Required if fnparser is set */
 	bool gotMsg;		/* set if the remote board has sent a new msg */
 	RECOVERY_STATUS SWUpdateStatus;	/* final status of update */
 	channel_op_res_t response;