diff mbox series

[11/13] Lua Script Handler: allows to run scripts globally

Message ID 20240221082221.11997-12-stefano.babic@swupdate.org
State Accepted
Delegated to: Stefano Babic
Headers show
Series Extend Lua Environemnt and post-failure scripts | expand

Commit Message

Stefano Babic Feb. 21, 2024, 8:22 a.m. UTC
Up now, scripts have run in isolated context: SWUpdate allocates a new
Lua state, and import the basic libraries before loading the script. A
script is then isolated, but it cannot access to function already
loaded, or it is not possible to reuse functions from 2 or more scripts.

With the introduction of a per installation Lua state, Lua scripts can
call functions already defined in previous scripts, or defined in
sw-description. Because when a script is loaded, it could overwrite
existing functions with the same name, it was decided that functions in
scripts must be unique, that is each function should be declared just
once during an installation process.

This means that for global state,  sw-description should contain the
name of the function for each step (pre- , postinstall or postfailure)
that should be called: the names preinst, postinst and postfailure are
still valid in case the script runs with isolated state.

This allows also to load a script without executing if no functions are
defined, and functions in the script can be called by later scripts.

Note that the handler will load the script in case of global state just
once during the "preinstall" call. Later, it is assumed that functions
will be already available.

Signed-off-by: Stefano Babic <stefano.babic@swupdate.org>
---
 corelib/lua_interface.c      | 26 ++++++----
 handlers/lua_scripthandler.c | 95 +++++++++++++++++++++++++++---------
 include/lua_util.h           |  2 +-
 3 files changed, 90 insertions(+), 33 deletions(-)

--
2.34.1
diff mbox series

Patch

diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
index 78e27576..747cd96b 100644
--- a/corelib/lua_interface.c
+++ b/corelib/lua_interface.c
@@ -204,27 +204,35 @@  static void lua_report_exception(lua_State *L)
 	} while (*s++);
 }

-int run_lua_script(lua_State *L, const char *script, const char *function, char *parms)
+int run_lua_script(lua_State *L, const char *script, bool load, const char *function, char *parms)
 {
 	int ret;
 	const char *output;

-	if (!function || !L)
+	if (!L) {
+		ERROR("Lua script must be executed, but no valid Lua state was set");
 		return -EINVAL;
+	}

-	if (script) {
+	if (load) {
+		TRACE("Loading Lua %s script", script);
 		if (luaL_loadfile(L, script)) {
 			ERROR("ERROR loading %s", script);
 			return -1;
 		}
+
+		ret = lua_pcall(L, 0, 0, 0);
+		if (ret) {
+			LUAstackDump(L);
+			ERROR("ERROR preparing Lua script %s %d",
+				script, ret);
+			return -1;
+		}
 	}

-	ret = lua_pcall(L, 0, 0, 0);
-	if (ret) {
-		LUAstackDump(L);
-		ERROR("ERROR preparing Lua script %s %d",
-			script, ret);
-		return -1;
+	if (!function) {
+		WARN("Script was loaded, no function was set to be executed !");
+		return 0;
 	}

 	lua_getglobal(L, function);
diff --git a/handlers/lua_scripthandler.c b/handlers/lua_scripthandler.c
index 83440e81..009c0557 100644
--- a/handlers/lua_scripthandler.c
+++ b/handlers/lua_scripthandler.c
@@ -24,10 +24,21 @@ 

 static void lua_handler(void);

+struct fn_names {
+	const char *property_name;	/* Name of property in sw-description */
+	const char *def_fn;		/* Default name function if property is not set */
+};
+
+const struct fn_names fn_property_names[] = {
+	[PREINSTALL] = {"preinstall", "preinst"},
+	[POSTINSTALL] = {"postinstall", "postinst"},
+	[POSTFAILURE] = {"postfailure", "postfailure"}
+};
+
 static int start_lua_script(struct img_type *img, void *data)
 {
 	int ret;
-	const char *fnname;
+	const char *fnname = NULL;
 	struct script_handler_data *script_data;
 	lua_State *L;
 	const char* tmp = get_tmpdirscripts();
@@ -36,8 +47,29 @@  static int start_lua_script(struct img_type *img, void *data)
 	if (!data)
 		return -1;

+	script_data = data;
+
+	/*
+	 * A little paranoid, thios shouln't happen
+	 */
+	if (script_data->scriptfn < PREINSTALL || script_data->scriptfn > POSTFAILURE)
+		return -EINVAL;
+
 	bool global  = strtobool(dict_get_value(&img->properties, "global-state"));

+	/*
+	 * Note: if global is set, functions should be unique
+	 * The name of function should be set inside the script
+	 */
+	fnname = dict_get_value(&img->properties, fn_property_names[script_data->scriptfn].property_name);
+
+	if (!fnname && !global) {
+		fnname = fn_property_names[script_data->scriptfn].def_fn;
+	}
+
+	/*
+	 * Assign the Lua state
+	 */
 	if (global) {
 		TRACE("Executing with global state");
 		L = img->L;
@@ -50,34 +82,51 @@  static int start_lua_script(struct img_type *img, void *data)
 		return -1;
 	}

-	script_data = data;
+	/*
+	 * In case of global, loads the script just once. Check if a function is already
+	 * present. An Overwrite is excluded by design. All functions should be unique.
+	 */

-	switch (script_data->scriptfn) {
-	case PREINSTALL:
-		fnname = dict_get_value(&img->properties, "preinstall");
-		if (!fnname)
-			fnname="preinst";
-		break;
-	case POSTINSTALL:
-		fnname = dict_get_value(&img->properties, "postinstall");
-		if (!fnname)
-			fnname="postinst";
-		break;
-	case POSTFAILURE:
-		fnname = dict_get_value(&img->properties, "postfailure");
-		if (!fnname)
-			fnname="postfailure";
-		break;
-	default:
-		/* no error, simply no call */
-		return 0;
+	bool load_script = true;
+
+	if (global) {
+		if (script_data->scriptfn == PREINSTALL) {
+			for (int i = PREINSTALL; i <= POSTINSTALL; i++) {
+				const char *fn = fn_property_names[script_data->scriptfn].property_name;
+				if (!fn)
+					continue;
+				lua_getglobal(L, fn);
+				if(lua_isfunction(L,lua_gettop(L))) {
+					ERROR("Function %s already defined, functions must be unique", fn);
+					return -1;
+				}
+			}
+		} else {
+			/*
+			 * Script was already loaded, skip it
+			 */
+			load_script = false;
+		}
 	}

+	/*
+	 * Trace what should be done
+	 */
 	snprintf(filename, sizeof(filename),
 		"%s%s", tmp, img->fname);
-	TRACE("Calling Lua %s with %s", filename, fnname);
+	TRACE("%s: Calling Lua %s with %s",
+	      fn_property_names[script_data->scriptfn].property_name,
+	      filename,
+	      fnname ? fnname : "no function, just loaded");
+
+	/*
+	 * In case no function is selected and we run in global,
+	 * script was already loaded and there is nothing to do
+	 */
+	if (global && !fnname && !load_script)
+		return 0;

-	ret = run_lua_script(L, filename, fnname, img->type_data);
+	ret = run_lua_script(L, filename, load_script, fnname, img->type_data);

 	if (!global)
 		lua_close(L);
diff --git a/include/lua_util.h b/include/lua_util.h
index ff603b5c..7b34eb89 100644
--- a/include/lua_util.h
+++ b/include/lua_util.h
@@ -20,7 +20,7 @@  typedef enum {
 } root_dev_type;

 void LUAstackDump (lua_State *L);
-int run_lua_script(lua_State *L, const char *script, const char *function, char *parms);
+int run_lua_script(lua_State *L, const char *script, bool load, const char *function, char *parms);
 lua_State *lua_init(struct dict *bootenv);
 int lua_load_buffer(lua_State *L, const char *buf);
 int lua_parser_fn(lua_State *L, const char *fcn, struct img_type *img);