[PATCH 06/13] Introduce entry for post-update failure scripts

140 views
Skip to first unread message

Stefano Babic

unread,
Feb 21, 2024, 3:22:26 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
There are cases where an update fails, and some glue logic must be added
to restore the system before update has begun. To help this, introduce
FAILURE scripts, that should be called only in case an update fails (and
they are in alternative to POSTINSTALL).

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
include/handler.h | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/include/handler.h b/include/handler.h
index 36e860b3..509a89ac 100644
--- a/include/handler.h
+++ b/include/handler.h
@@ -11,10 +11,18 @@
#include <stdbool.h>

struct img_type;
+
+/*
+ * Identify the type of a script
+ * A script can contain functions for several of these
+ * entries. For example, it can contain both a pre- and post-
+ * install functions.
+ */
typedef enum {
NONE,
- PREINSTALL,
- POSTINSTALL
+ PREINSTALL, /* Script runs as preinstall */
+ POSTINSTALL, /* Script runs a postinstall */
+ POSTFAILURE /* Script called in case update fails */
} script_fn ;

/*
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:26 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
Add pointer to image structure to the global Lua state to allow handlers
to use it instead of creating a new one.

This allows to call Lua functions that were previously loaded by the
parser or by scripts, or loaded at the startup.

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
include/swupdate_image.h | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/include/swupdate_image.h b/include/swupdate_image.h
index 592a886b..68d97a02 100644
--- a/include/swupdate_image.h
+++ b/include/swupdate_image.h
@@ -12,6 +12,7 @@
#include "bsdqueue.h"
#include "globals.h"
#include "swupdate_dict.h"
+#include "lua_util.h"

typedef enum {
FLASH,
@@ -60,7 +61,14 @@ struct img_type {
int is_script;
int is_partitioner;
struct dict properties;
- struct dict *bootloader; /* pointer to swupdate_cfg's bootloader dict for handlers to modify */
+
+ /*
+ * Pointers to global structures
+ * that are alive during an installation. They can be used by handlers
+ */
+ struct dict *bootloader;/* pointer to swupdate_cfg's bootloader dict for handlers to modify */
+ lua_State *L; /* pointer to swupdate_cfg's LUa state created by parser */
+
long long partsize;
int fdin; /* Used for streaming file */
off_t offset; /* offset in cpio file */
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:26 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
A Lua context is created and destroyed for each Lua scriptthat should
run. This isolates each script, but it does not allow that a later
script can benefit and load a function previously defined.

It was decided that so high isolation is not necessary - instead of
having a context for each script, create a Lua context for each
installation request. The Lua context is then initialized when
sw-description is parsed and removed when the installation has finished,
independently from the state.

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
core/installer.c | 8 ++++++++
corelib/lua_interface.c | 14 +++++++++-----
include/lua_util.h | 12 +++++++-----
include/swupdate.h | 1 +
parser/parser.c | 16 +++++++++++-----
5 files changed, 36 insertions(+), 15 deletions(-)

diff --git a/core/installer.c b/core/installer.c
index 20b5b51e..2b85a6e7 100644
--- a/core/installer.c
+++ b/core/installer.c
@@ -33,6 +33,7 @@
#include "progress.h"
#include "pctl.h"
#include "swupdate_vars.h"
+#include "lua_util.h"

/*
* function returns:
@@ -478,6 +479,13 @@ void cleanup_files(struct swupdate_cfg *software) {
dict_drop_db(&software->bootloader);
dict_drop_db(&software->vars);

+ /*
+ * Drop Lua State if instantiated
+ */
+ if (software->lua_state) {
+ lua_exit(software->lua_state);
+ software->lua_state = NULL;
+ }
if (asprintf(&fn, "%s%s", TMPDIR, BOOT_SCRIPT_SUFFIX) != ENOMEM_ASPRINTF) {
remove_sw_file(fn);
free(fn);
diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
index 2bd8e6cf..33be8d3b 100644
--- a/corelib/lua_interface.c
+++ b/corelib/lua_interface.c
@@ -1517,7 +1517,7 @@ int lua_handlers_init(void)
return ret;
}

-lua_State *lua_parser_init(const char *buf, struct dict *bootenv)
+lua_State *lua_init(struct dict *bootenv)
{
lua_State *L = luaL_newstate(); /* opens Lua */

@@ -1533,14 +1533,18 @@ lua_State *lua_parser_init(const char *buf, struct dict *bootenv)
luaL_setfuncs(L, l_swupdate_bootenv, 1);
lua_pop(L, 1); /* remove unused copy left on stack */

+ return L;
+}
+
+int lua_load_buffer(lua_State *L, const char *buf)
+{
if (luaL_loadstring(L, buf) || lua_pcall(L, 0, 0, 0)) {
LUAstackDump(L);
- ERROR("ERROR preparing Lua embedded script in parser");
- lua_close(L);
- return NULL;
+ ERROR("ERROR loading Lua code");
+ return 1;
}

- return L;
+ return 0;
}

int lua_parser_fn(lua_State *L, const char *fcn, struct img_type *img)
diff --git a/include/lua_util.h b/include/lua_util.h
index 1d00bf82..5f5e67a7 100644
--- a/include/lua_util.h
+++ b/include/lua_util.h
@@ -21,7 +21,8 @@ typedef enum {

void LUAstackDump (lua_State *L);
int run_lua_script(const char *script, const char *function, char *parms);
-lua_State *lua_parser_init(const char *buf, struct dict *bootenv);
+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);
int lua_handlers_init(void);

@@ -34,7 +35,7 @@ int lua_notify_progress(lua_State *L);

int lua_get_swupdate_version(lua_State *L);

-#define lua_parser_exit(L) lua_close((lua_State *)L)
+#define lua_exit(L) lua_close((lua_State *)L)

#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
#define LUA_OK 0
@@ -81,9 +82,10 @@ void luaL_pushresult(luaL_Buffer_52 *B);
#else

#define lua_State void
-#define lua_parser_exit(L)
-static inline lua_State *lua_parser_init(const char __attribute__ ((__unused__)) *buf,
- struct dict __attribute__ ((__unused__)) *bootenv) { return NULL;}
+#define lua_exit(L)
+static inline lua_State *lua_init(struct dict __attribute__ ((__unused__)) *bootenv) { return NULL;}
+static inline int lua_load_buffer(lua_State __attribute__ ((__unused__)) *L,
+ const char __attribute__ ((__unused__)) *buf) {return 1;}
static inline int lua_parser_fn(lua_State __attribute__ ((__unused__)) *L,
const char __attribute__ ((__unused__)) *fcn,
struct img_type __attribute__ ((__unused__)) *img) { return -1; }
diff --git a/include/swupdate.h b/include/swupdate.h
index 883bad51..e18de8d3 100644
--- a/include/swupdate.h
+++ b/include/swupdate.h
@@ -59,6 +59,7 @@ struct swupdate_cfg {
char mtdblacklist[SWUPDATE_GENERAL_STRING_SIZE];
char forced_signer_name[SWUPDATE_GENERAL_STRING_SIZE];
char namespace_for_vars[SWUPDATE_GENERAL_STRING_SIZE];
+ void *lua_state;
bool syslog_enabled;
bool no_downgrading;
bool no_reinstalling;
diff --git a/parser/parser.c b/parser/parser.c
index 1d9a38db..3944de64 100644
--- a/parser/parser.c
+++ b/parser/parser.c
@@ -1022,15 +1022,24 @@ static int parser(parsertype p, void *cfg, struct swupdate_cfg *swcfg)
swcfg->embscript = get_field_string(p, scriptnode, NULL);
}

+ L = lua_init(&swcfg->bootloader);
+
if (swcfg->embscript) {
+ if (!L) {
+ ERROR("Required embedded script but no Lua not available");
+ return -1;
+ }
if (loglevel >= DEBUGLEVEL)
TRACE("Found Lua Software:\n%s", swcfg->embscript);
- L = lua_parser_init(swcfg->embscript, &swcfg->bootloader);
- if (!L) {
+ if (lua_load_buffer(L, swcfg->embscript)) {
ERROR("Required embedded script that cannot be loaded");
+ lua_close(L);
return -1;
}
}
+
+ swcfg->lua_state = L;
+
if (get_hw_revision(&swcfg->hw) < 0) {
TRACE("Hardware compatibility not found");
}
@@ -1049,9 +1058,6 @@ static int parser(parsertype p, void *cfg, struct swupdate_cfg *swcfg)
*/
parse_partitions(p, cfg, swcfg, L);

- if (L)
- lua_parser_exit(L);
-
if (LIST_EMPTY(&swcfg->images) &&
LIST_EMPTY(&swcfg->scripts) &&
LIST_EMPTY(&swcfg->bootloader)) {
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:26 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
Add a property "global-state" to run the script with a per installation
Lua state or creating a new local as in the past.

If global-state = "true" (as property, value is always a string), Lua
state is reused and the script can call any Lua function that was loaded
before during the installation.

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
handlers/lua_scripthandler.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/handlers/lua_scripthandler.c b/handlers/lua_scripthandler.c
index d4ac421e..213099de 100644
--- a/handlers/lua_scripthandler.c
+++ b/handlers/lua_scripthandler.c
@@ -36,7 +36,14 @@ static int start_lua_script(struct img_type *img, void *data)
if (!data)
return -1;

- L = lua_init(img->bootloader);
+ bool global = strtobool(dict_get_value(&img->properties, "global-state"));
+
+ if (global) {
+ TRACE("Executing with global state");
+ L = img->L;
+ } else {
+ L = lua_init(img->bootloader);
+ }

if (!L) {
ERROR("Lua state cannot be instantiated");
@@ -63,7 +70,8 @@ static int start_lua_script(struct img_type *img, void *data)

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

- lua_close(L);
+ if (!global)
+ lua_close(L);

return ret;
}
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:26 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
This patchset introduces two big changes:

- Lua life-cycle is extended to the whole update. It is still configurable,
and a Lua state can still isolate a single script (as before). This is
the default environment.
SWUpdate sets a Lua state at the beginning of an update and frees when the
update is terminated. This lets to use functions and variables defined
in scripts that ran before.
As example, an embedded-lua script in sw-description can set some Lua
variables, that a later script can read (and modify). This is also
a step for security, because no external data to transfer information
is needed, and everything remains in context of the Lua state.

- post-failure scripts. These are called only in case an update fails.
They are usually required for cleanup and to restore to the same
state before the update has begun, like restart some services, etc.


Stefano Babic (13):
Lua: prepare to extend Lua lifetime
Lua: move out state initialization from run_lua_script
image: create pointer to per install Lua state
Store install Lua State in image structure
Lua Script: runs with global Lua state
Introduce entry for post-update failure scripts
Export run_prepost_script to other modules
Introduce postfailure scripts
Lua Script: TRACE the name of the called function
run_lua_script: check input parms
Lua Script Handler: allows to run scripts globally
doc: explain Lua and shell script handlers
Fix warnings in case CONFIG_LUA is not set

core/installer.c | 10 +++-
core/stream_interface.c | 13 +++++
corelib/lua_interface.c | 55 +++++++++--------
doc/source/handlers.rst | 86 +++++++++++++++++++++++++--
doc/source/sw-description.rst | 20 ++++++-
handlers/lua_scripthandler.c | 104 ++++++++++++++++++++++++++++-----
handlers/shell_scripthandler.c | 3 +
include/handler.h | 12 +++-
include/installer.h | 1 +
include/lua_util.h | 17 ++++--
include/swupdate.h | 1 +
include/swupdate_image.h | 10 +++-
parser/parser.c | 17 ++++--
13 files changed, 288 insertions(+), 61 deletions(-)

--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:27 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
Moving the Lua state outside the function, parameters must be checked
for consistency.

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
corelib/lua_interface.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
index 72190383..78e27576 100644
--- a/corelib/lua_interface.c
+++ b/corelib/lua_interface.c
@@ -209,9 +209,14 @@ int run_lua_script(lua_State *L, const char *script, const char *function, char
int ret;
const char *output;

- if (luaL_loadfile(L, script)) {
- ERROR("ERROR loading %s", script);
- return -1;
+ if (!function || !L)
+ return -EINVAL;
+
+ if (script) {
+ if (luaL_loadfile(L, script)) {
+ ERROR("ERROR loading %s", script);
+ return -1;
+ }
}

ret = lua_pcall(L, 0, 0, 0);
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:27 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
These scripts run afet ran update fails. They can be used to restore the
system to a previous state before the Update was initiated. All scripts
will be executed, as it makes no sense to stop if one of them fails,
because the update state is already FAILURE.

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
core/stream_interface.c | 13 +++++++++++++
doc/source/sw-description.rst | 20 +++++++++++++++++---
handlers/lua_scripthandler.c | 13 +++++++++++--
handlers/shell_scripthandler.c | 3 +++
4 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/core/stream_interface.c b/core/stream_interface.c
index b9bce6d7..5ebaca68 100644
--- a/core/stream_interface.c
+++ b/core/stream_interface.c
@@ -674,6 +674,19 @@ void *network_initializer(void *data)
update_transaction_state(software, STATE_FAILED);
notify(FAILURE, RECOVERY_ERROR, ERRORLEVEL, "Installation failed !");
inst.last_install = FAILURE;
+
+ /*
+ * Try to run all POSTFAILURE scripts,
+ * their result does not change the state that remains FAILURE
+ * Goal is to restore to the state before update was started
+ * if this is needed. This is a best case attempt, ERRORs
+ * are just logged.
+ */
+ if (!software->parms.dry_run) {
+ if (run_prepost_scripts(&software->scripts, POSTFAILURE)) {
+ WARN("execute POST FAILURE scripts return error, ignoring..");
+ }
+ }
} else {
/*
* Clear the recovery variable to indicate to bootloader
diff --git a/doc/source/sw-description.rst b/doc/source/sw-description.rst
index b0363359..d4cb8971 100644
--- a/doc/source/sw-description.rst
+++ b/doc/source/sw-description.rst
@@ -759,7 +759,9 @@ with an error if the result is <> 0.
They are copied into a temporary directory before execution and their name must
be unique inside the same cpio archive.

-If no type is given, SWUpdate default to "lua".
+If no type is given, SWUpdate default to "lua". Please note that running a shell script
+opens a set of different security issues, check also chapter "Best practise".
+

Lua
...
@@ -790,12 +792,24 @@ called before installing the images.

function postinst()

+
SWUpdate scans for all scripts and check for a postinst function. It is
called after installing the images.

+::
+
+ function postfailure()
+
+Only in case an update fails, SWUpdate scans for all scripts and check
+for a postfailure function. This could be useful in case it is necessary
+to restore a previous state, for example, in case the application was
+stop, it should run again.
+
shellscript
...........

+SWUpdate will run the binary shell "/bin/sh" to execute the script.
+
::

scripts: (
@@ -805,9 +819,9 @@ shellscript
}
);

-Shell scripts are called via system command.
+Shell scripts are called by forking the process and running the shell as /bin/sh.
SWUpdate scans for all scripts and calls them before and after installing
-the images. SWUpdate passes 'preinst' or 'postinst' as first argument to
+the images. SWUpdate passes 'preinst', 'postinst' or 'postfailure' as first argument to
the script.
If the data attribute is defined, its value is passed as the last argument(s)
to the script.
diff --git a/handlers/lua_scripthandler.c b/handlers/lua_scripthandler.c
index 213099de..08479ef4 100644
--- a/handlers/lua_scripthandler.c
+++ b/handlers/lua_scripthandler.c
@@ -54,10 +54,19 @@ static int start_lua_script(struct img_type *img, void *data)

switch (script_data->scriptfn) {
case PREINSTALL:
- fnname="preinst";
+ fnname = dict_get_value(&img->properties, "preinstall");
+ if (!fnname)
+ fnname="preinst";
break;
case POSTINSTALL:
- fnname="postinst";
+ 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 */
diff --git a/handlers/shell_scripthandler.c b/handlers/shell_scripthandler.c
index a85e8bb9..5602b98b 100644
--- a/handlers/shell_scripthandler.c
+++ b/handlers/shell_scripthandler.c
@@ -63,6 +63,9 @@ static int start_shell_script(struct img_type *img, void *data)
case POSTINSTALL:
fnname="postinst";
break;
+ case POSTFAILURE:
+ fnname="failure";
+ break;
default:
/* no error, simply no call */
return 0;
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:27 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
handlers/lua_scripthandler.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/handlers/lua_scripthandler.c b/handlers/lua_scripthandler.c
index 08479ef4..83440e81 100644
--- a/handlers/lua_scripthandler.c
+++ b/handlers/lua_scripthandler.c
@@ -75,7 +75,7 @@ static int start_lua_script(struct img_type *img, void *data)

snprintf(filename, sizeof(filename),
"%s%s", tmp, img->fname);
- TRACE("Calling Lua %s", filename);
+ TRACE("Calling Lua %s with %s", filename, fnname);

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

--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:27 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
Function is generic and just accept a list of scripts. Export it to let
be called outside the install_images() function.

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
core/installer.c | 2 +-
include/installer.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/core/installer.c b/core/installer.c
index 2b85a6e7..f2fbbcab 100644
--- a/core/installer.c
+++ b/core/installer.c
@@ -216,7 +216,7 @@ static int update_swupdate_vars(struct swupdate_cfg *cfg, const char *script)
return ret;
}

-static int run_prepost_scripts(struct imglist *list, script_fn type)
+int run_prepost_scripts(struct imglist *list, script_fn type)
{
int ret;
struct img_type *img;
diff --git a/include/installer.h b/include/installer.h
index 613ecc71..aad77097 100644
--- a/include/installer.h
+++ b/include/installer.h
@@ -21,4 +21,5 @@ int install_single_image(struct img_type *img, bool dry_run);
int install_from_file(const char *filename, bool check);
int postupdate(struct swupdate_cfg *swcfg, const char *info);
int preupdatecmd(struct swupdate_cfg *swcfg);
+int run_prepost_scripts(struct imglist *list, script_fn type);
void cleanup_files(struct swupdate_cfg *software);
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:27 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
Each installation will have its own Lua state. Save it into image
structures to allow the handlers to access it.

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
parser/parser.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/parser/parser.c b/parser/parser.c
index 3944de64..5502e34a 100644
--- a/parser/parser.c
+++ b/parser/parser.c
@@ -384,6 +384,7 @@ static int is_image_higher(struct swver *sw_ver_list,
static void set_img_globals(struct img_type *img, struct swupdate_cfg *sw)
{
img->bootloader = &sw->bootloader;
+ img->L = sw->lua_state;
}

static int run_embscript(parsertype p, void *elem, struct img_type *img,
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:27 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
doc/source/handlers.rst | 86 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 82 insertions(+), 4 deletions(-)

diff --git a/doc/source/handlers.rst b/doc/source/handlers.rst
index 1a1988f0..da0bbeb8 100644
--- a/doc/source/handlers.rst
+++ b/doc/source/handlers.rst
@@ -457,6 +457,84 @@ image, this is not implemented as it carries some security
implications since the behavior of SWUpdate is changed
dynamically.

+Shell script handler
+--------------------
+
+This handler allows to run a shell script thta is packed into the SWU. Please note
+that running a shell script opens a set of different security issues. Shell scripts
+are supported due to their large acceptance, but you should prefer Lua Scripts.
+
+SWUpdate will run the binary shell "/bin/sh" to execute the script.
+
+Lua script handler
+------------------
+
+A Lua Script handler runs a script in Lua language. There are two possible ways to run the
+script:
+
+ - local: the script runs in own (isolated) Lua state that is created for the script.
+ The script has access only to function defined inside the script or functions
+ provided by external libraries, like the internal swupdate library called via
+ "require(swupdate)".
+ - global: SWUpdate create a Lua state at the beginning of an Update and this is
+ valid until the update is terminated. In this case, the script has access to any function
+ and structure that was defined during the update. For example, a function
+ can be defined inside sw-description, and the script can call it.
+
+As default, each script runs in isolated / local Lua state. If the property "global-state" is set,
+then the common LUa state used for each Update transaction is taken.
+
+Scripts ran in isolated context in previous versions. 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, existing functions with the same name are overwritten,
+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.
+
+
+Example:
+
+::
+
+ scripts: (
+ {
+ filename = "testscript.lua";
+ type = "lua";
+ properties: {
+ global-state = "true";
+ preinstall = "pretest1";
+ }
+ },
+ {
+ filename = "test2script.lua";
+ type = "lua";
+ properties: {
+ global-state = "true";
+ postinstall = "posttest2";
+ postfailure = "failure";
+ }
+ }
+
+Two scripts are defined. Both are using the global Lua state.
+Functions in test2script can find and run functions defined in testscript.lua,
+because both are belonging to the same context. When preinstall scripts are called, only the function
+"pretest1" from the first script is called, because no function name is defined for this step with
+the following scripts.
+
Remote handler
--------------

@@ -1326,7 +1404,7 @@ passed to the daemon:
Docker Remove Image
-------------------

-It is implemented as script (post install).
+It is implemented as script (post install).
Example:

::
@@ -1341,7 +1419,7 @@ Example:
Docker Delete Unused Images
---------------------------

-It is implemented as script (post install).
+It is implemented as script (post install).
Example:

::
@@ -1386,7 +1464,7 @@ Creating the container can be done in sw-description with:
Docker Remove Container
-----------------------

-It is implemented as script (post install).
+It is implemented as script (post install).
Example:

::
@@ -1411,7 +1489,7 @@ Examples:
name = "helloworld";
};
});
-
+
::

scripts: ( {
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:27 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
This is in preparation for using global state. Do not create the state
in the run_lua_script(), but accepts a L state as parameter. It is duty
of the caller to provide a valid state.

Change accordingly the Lua script handler.

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
corelib/lua_interface.c | 12 +-----------
handlers/lua_scripthandler.c | 14 +++++++++++---
include/lua_util.h | 2 +-
3 files changed, 13 insertions(+), 15 deletions(-)

diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
index 33be8d3b..72190383 100644
--- a/corelib/lua_interface.c
+++ b/corelib/lua_interface.c
@@ -204,18 +204,13 @@ static void lua_report_exception(lua_State *L)
} while (*s++);
}

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

- lua_State *L = luaL_newstate(); /* opens Lua */
- luaL_openlibs(L); /* opens the standard libraries */
- luaL_requiref(L, "swupdate", luaopen_swupdate, 1 );
-
if (luaL_loadfile(L, script)) {
ERROR("ERROR loading %s", script);
- lua_close(L);
return -1;
}

@@ -224,13 +219,11 @@ int run_lua_script(const char *script, const char *function, char *parms)
LUAstackDump(L);
ERROR("ERROR preparing Lua script %s %d",
script, ret);
- lua_close(L);
return -1;
}

lua_getglobal(L, function);
if(!lua_isfunction(L,lua_gettop(L))) {
- lua_close(L);
TRACE("Script : no %s in %s script, exiting", function, script);
return 0;
}
@@ -241,7 +234,6 @@ int run_lua_script(const char *script, const char *function, char *parms)
if (lua_pcall(L, 1, 2, 0)) {
LUAstackDump(L);
ERROR("ERROR Calling Lua script %s", script);
- lua_close(L);
return -1;
}

@@ -257,8 +249,6 @@ int run_lua_script(const char *script, const char *function, char *parms)
TRACE("Script output: %s script end", output);
}

- lua_close(L);
-
return ret;
}

diff --git a/handlers/lua_scripthandler.c b/handlers/lua_scripthandler.c
index 509b360d..d4ac421e 100644
--- a/handlers/lua_scripthandler.c
+++ b/handlers/lua_scripthandler.c
@@ -29,13 +29,20 @@ static int start_lua_script(struct img_type *img, void *data)
int ret;
const char *fnname;
struct script_handler_data *script_data;
-
+ lua_State *L;
const char* tmp = get_tmpdirscripts();
char filename[MAX_IMAGE_FNAME + strlen(tmp) + 2 + strlen(img->type_data)];

if (!data)
return -1;

+ L = lua_init(img->bootloader);
+
+ if (!L) {
+ ERROR("Lua state cannot be instantiated");
+ return -1;
+ }
+
script_data = data;

switch (script_data->scriptfn) {
@@ -54,10 +61,11 @@ static int start_lua_script(struct img_type *img, void *data)
"%s%s", tmp, img->fname);
TRACE("Calling Lua %s", filename);

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

- return ret;
+ lua_close(L);

+ return ret;
}

__attribute__((constructor))
diff --git a/include/lua_util.h b/include/lua_util.h
index 5f5e67a7..ff603b5c 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(const char *script, const char *function, char *parms);
+int run_lua_script(lua_State *L, const char *script, 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);
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:27 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
Add dummy lua_close() to avoid warnings in the parser.

Signed-off-by: Stefano Babic <stefan...@swupdate.org>
---
include/lua_util.h | 3 +++
1 file changed, 3 insertions(+)

diff --git a/include/lua_util.h b/include/lua_util.h
index 7b34eb89..bf8d2b0e 100644
--- a/include/lua_util.h
+++ b/include/lua_util.h
@@ -81,8 +81,11 @@ void luaL_pushresult(luaL_Buffer_52 *B);

#else

+struct img_type;
+
#define lua_State void
#define lua_exit(L)
+#define lua_close(L)
static inline lua_State *lua_init(struct dict __attribute__ ((__unused__)) *bootenv) { return NULL;}
static inline int lua_load_buffer(lua_State __attribute__ ((__unused__)) *L,
const char __attribute__ ((__unused__)) *buf) {return 1;}
--
2.34.1

Stefano Babic

unread,
Feb 21, 2024, 3:22:28 AM2/21/24
to swup...@googlegroups.com, Stefano Babic
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 <stefan...@swupdate.org>
---
corelib/lua_interface.c | 26 ++++++----
handlers/lua_scripthandler.c | 95 +++++++++++++++++++++++++++---------
include/lua_util.h | 2 +-
3 files changed, 90 insertions(+), 33 deletions(-)

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);
Reply all
Reply to author
Forward
0 new messages