From: Konrad Schwarz <
konrad....@siemens.com>
Progress messages delivered via lua_swupdate.progress contain table fields
"status" and "source", of types "lua_swupdate.RECOVERY_STATUS" resp.
"lua_swupdate.sourcetype".
Somewhat unfortunately, these fields are numeric; the tables
"lua_swupdate.RECOVERY_STATUS" and "lua_swupdate.sourcetype" map from
string labels to these numeric values. It is a bit of a chore to
display the values symbolically as human-readable strings that are
useful for simple monitoring and debugging: The inverse mapping of
"lua_swupdate.RECOVERY_STATUS" and "lua_swupdate.sourcetype" must be
generated and can then be indexed by the field values.
Although these inverse mappings are not difficult to create in Lua,
as a convenience, this patch makes the symbols (strings) available as
the new fields "status_label" and "source_label" in progress messages.
The patch itself takes great pains to hoist as much string
processing out of the receive loop as possible: the labels of
"lua_swupdate.RECOVERY_STATUS" and "lua_swupdate.sourcetype", as well
as the field names are recorded in Lua exactly once, in the package's
luaopen_ routine. To prevent the strings from being garbage collected,
they (or tables holding them) are registered as light userdata in the
Lua registry. No so such string is registered twice; the intent is that
string comparision can be done quickly as pointer identity checks.
Note that the (undocumented) internment of short strings apparently
implemented in at least some Lua versions provides most of the same
benefits -- except that an extra hash table lookup is required to check
if a short string has been interned already each time such a string is
passed to Lua. The purely functional improvement could be implemented
in simpler fashion.
Signed-off-by: Konrad Schwarz <
konrad....@siemens.com>
---
bindings/lua_swupdate.c | 418 ++++++++++++++++++++++++++++++++------
bindings/lua_swupdate.lua | 10 +-
2 files changed, 368 insertions(+), 60 deletions(-)
diff --git a/bindings/lua_swupdate.c b/bindings/lua_swupdate.c
index bd5f876a..e81f4030 100644
--- a/bindings/lua_swupdate.c
+++ b/bindings/lua_swupdate.c
@@ -2,15 +2,15 @@
* (C) Copyright 2018
* Stefano Babic,
stefan...@swupdate.org.
*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include <progress_ipc.h>
#include <lua.h>
#include <lauxlib.h>
-#include <string.h>
+#include <string.h>
#include <ifaddrs.h>
-#include <netinet/in.h>
+#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
@@ -25,27 +25,66 @@
#include "auxiliar.h"
#include "lua_compat.h"
+/* Comment Legend:
+ * Lua uses an argument stack for object manipulation.
+ * Routines that manipulate the stack in non-trival ways
+ * use the following notation to indicate the current stack layout,
+ * derived from FORTH stack notation:
+ */
+ /* stack_bottom ... stack_top */
+/*
+ * I.e., the rightmost element is the stack top.
+ * A stack entry is identified
+ * with a code that is supposed to be mnemonic,
+ * typically utilizing an abbreviation of its type.
+ * E.g., "t_a" for "table A".
+ *
+ * For referencce: LUa defines stack offsets as follows:
+ *
+ * 0 1 2 ... -2 -1
+ *
+ */
+
+
+/* always returning the type helps assertion checking */
+# if 503 > LUA_VERSION_NUM
+# define PR_LUA_RAWGET(L, index)\
+ (lua_rawget (L, index), lua_type (L, -1))
+# define PR_LUA_RAWGETI(L, index, integer)\
+ (lua_rawgeti (L, index, integer), lua_type (L, -1))
+# else
+# define PR_LUA_RAWGET(L, index)\
+ lua_rawget (L, index)
+# define PR_LUA_RAWGETI(L, index, integer)\
+ lua_rawgeti (L, index, integer)
+# endif
+
+
+/* work around the non-const-qualification of argument p */
+# define PR_LUA_PUSHLIGHTUSERDATA(L, P)\
+ lua_pushlightuserdata (L, (void *) P)
+
#define WAIT 1
-#define LUA_PUSH_STRING(key, data) do { \
+#define LUA_PUSH_STRING(key, data) do { \
lua_pushstring(L, key); \
lua_pushstring(L, data); \
lua_settable(L, -3); \
} while (0)
-#define LUA_PUSH_BOOL(key, data) do { \
+#define LUA_PUSH_BOOL(key, data) do { \
lua_pushstring(L, key); \
lua_pushboolean(L, data); \
lua_settable(L, -3); \
} while (0)
-#define LUA_PUSH_NUMBER(key, data) do { \
+#define LUA_PUSH_NUMBER(key, data) do { \
lua_pushstring(L, key); \
lua_pushnumber(L, (double) data); \
lua_settable(L, -3); \
} while (0)
-#define LUA_PUSH_INT(key, data) do { \
+#define LUA_PUSH_INT(key, data) do { \
lua_pushstring(L, key); \
lua_pushinteger(L, data); \
lua_settable(L, -3); \
@@ -62,8 +101,6 @@ static int ctrl_write(lua_State *L);
static int ctrl_close(lua_State *L);
static int ctrl_close_socket(lua_State *L);
-int luaopen_lua_swupdate(lua_State *L);
-
/*
* Return a table with all interface and their IP address
*/
@@ -118,7 +155,7 @@ static luaL_Reg ctrl_methods[] = {
{"connect", ctrl_connect},
{"write", ctrl_write},
{"close", ctrl_close},
- {NULL, NULL}
+ {}
};
/**
@@ -271,12 +308,12 @@ struct prog_obj {
/* progress object methods */
static luaL_Reg progress_methods[] = {
- {"__gc", progress_close},
- {"__tostring", auxiliar_tostring},
- {"close", progress_close},
- {"connect", progress_connect},
- {"receive", progress_receive},
- {NULL, NULL}
+ {"__gc", progress_close},
+ {"__tostring", auxiliar_tostring},
+ {"close", progress_close},
+ {"connect", progress_connect},
+ {"receive", progress_receive},
+ {}
};
/**
@@ -284,7 +321,7 @@ static luaL_Reg progress_methods[] = {
*
* @param [Lua] The swupdate_progress class instance.
@return [Lua] The connection handle (mostly for information), or,
- * in case of errors, nil plus an error message.
+ * in case of errors, nil plus an error message.
*/
static int progress_connect(lua_State *L) {
struct prog_obj *p = (struct prog_obj *) auxiliar_checkclass(L, "swupdate_progress", 1);
@@ -301,37 +338,207 @@ static int progress_connect(lua_State *L) {
p->socket = connfd;
p->status = IDLE;
- lua_pop(L, 1);
lua_pushnumber(L, connfd);
- lua_pushnil(L);
- return 2;
+ return 1;
}
static int progress_close(lua_State __attribute__ ((__unused__)) *L) {
return 1;
}
-static int progress_receive(lua_State *L) {
+
+# define ALL_PROGRESS_LABELS(MACRO)\
+ MACRO (apiversion)\
+ MACRO (status)\
+ MACRO (download)\
+ MACRO (source)\
+ MACRO (nsteps)\
+ MACRO (step)\
+ MACRO (percent)\
+ MACRO (artifact)\
+ MACRO (handler)\
+ MACRO (info)\
+ MACRO (status_label)\
+ MACRO (source_label)\
+
+static struct progress_field {
+ char const *name;
+ size_t len;
+} const progress_field [] = {
+
+# define PROGRESS_FIELD_STRING(LABEL)\
+ { #LABEL, sizeof #LABEL - sizeof "" },
+
+ ALL_PROGRESS_LABELS (PROGRESS_FIELD_STRING)
+
+# undef PROGRESS_FIELD_STRING
+};
+
+enum progress_field_enum {
+
+# define PROGRESS_FIELD_ENUM(LABEL)\
+ progress_field_ ## LABEL,
+
+ ALL_PROGRESS_LABELS (PROGRESS_FIELD_ENUM)
+
+# undef PROGRESS_FIELD_ENUM
+
+ progress_field_enum_last
+
+};
+
+/**
+ * @brief Retrieve the label of a mesasge field and place it at top of stack
+ *
+ * @param<in> field_key the message field's light user data
+ */
+static void progress_get_label (lua_State *const L,
+ struct progress_field const *const field_key)
+{
+ int type;
+
+ /* */
+ PR_LUA_PUSHLIGHTUSERDATA (L, field_key);
+ /* luf */
+ if (LUA_TSTRING != (type = PR_LUA_RAWGET (L, LUA_REGISTRYINDEX)))
+ luaL_error (L, "Expecting a string, got a %s",
+ lua_typename (L, type));
+ /* fs */
+}
+
+static void progress_set_integer (lua_State *const L,
+ struct progress_field const *const field_key,
+ lua_Integer const value)
+{
+ /* pt */
+ progress_get_label (L, field_key);
+ /* pt fs */
+ lua_pushinteger (L, value);
+ /* pt fs i */
+ lua_rawset (L, -3);
+ /* pt */
+}
+
+static void progress_set_string (lua_State *const L,
+ struct progress_field const *const field_key,
+ char const *const value)
+{
+ /* pt */
+ progress_get_label (L, field_key);
+ /* pt fs */
+ lua_pushstring (L, value);
+ /* pt fs s */
+ lua_rawset (L, -3);
+ /* pt */
+}
+
+static void progress_set_lstring (lua_State *const L,
+ struct progress_field const *const field_key,
+ char const *const value,
+ size_t const len)
+{
+ /* pt */
+ progress_get_label (L, field_key);
+ /* pt fs */
+ lua_pushlstring (L, value, len);
+ /* pt fs s */
+ lua_rawset (L, -3);
+ /* pt */
+}
+
+static void progress_set_label (lua_State *const L,
+ struct progress_field const *const field_key,
+ int const *const lu_anchor,
+ lua_Integer const label)
+{
+ int type;
+ /* pt */
+ progress_get_label (L, field_key);
+ /* pt fs */
+ PR_LUA_PUSHLIGHTUSERDATA (L, lu_anchor);
+ /* pt fs lu */
+ if (LUA_TTABLE != (type = PR_LUA_RAWGET (L, LUA_REGISTRYINDEX)))
+ luaL_error (L, "Expecting a table, got a %s",
+ lua_typename (L, type));
+ /* pt fs t */
+ if (LUA_TSTRING != (type = PR_LUA_RAWGETI (L, -1, label)))
+ luaL_error (L, "Expecting a string, got a %s",
+ lua_typename (L, type));
+ /* pt fs t s */
+ lua_remove (L, -2);
+ /* pt fs s */
+ lua_rawset (L, -3);
+ /* pt */
+}
+
+/* light user data anchors -- otherwise unused.
+ * note that (the address of) string constants cannot be used to anchor
+ * light user data, as the linker is allowed to overlay these constants with
+ * others to optimize space */
+static int const recovery_table, source_type_table;
+
+static int progress_receive(lua_State *const L)
+{
struct prog_obj *p = (struct prog_obj *) auxiliar_checkclass(L, "swupdate_progress", 1);
int connfd = p->socket;
if (progress_ipc_receive(&connfd, &p->msg) <= 0) {
- lua_pushnil(L);
- return 2;
+ lua_pushnil(L);
+ return 1;
};
lua_newtable(L);
- LUA_PUSH_INT("apiversion", p->msg.apiversion);
- LUA_PUSH_INT("status", p->msg.status);
- LUA_PUSH_INT("download", p->msg.dwl_percent);
- LUA_PUSH_INT("source", p->msg.source);
- LUA_PUSH_INT("nsteps", p->msg.nsteps);
- LUA_PUSH_INT("step", p->msg.cur_step);
- LUA_PUSH_INT("percent", p->msg.cur_percent);
- LUA_PUSH_STRING("artifact", p->msg.cur_image);
- LUA_PUSH_STRING("handler", p->msg.hnd_name);
+ /* pt */
+
+# define PROGRESS_SET_INTEGER(FIELD, MSG_FIELD)\
+ do {\
+ progress_set_integer (L,\
+ progress_field + progress_field_ ## FIELD,\
+ p->msg.MSG_FIELD);\
+ } while (0)
+
+# define PROGRESS_SET_STRING(FIELD, MSG_FIELD)\
+ do {\
+ progress_set_string (L,\
+ progress_field + progress_field_ ## FIELD,\
+ p->msg.MSG_FIELD);\
+ } while (0)
+
+# define PROGRESS_SET_LSTRING(FIELD, MSG_FIELD, LEN)\
+ do {\
+ progress_set_lstring (L,\
+ progress_field + progress_field_ ## FIELD,\
+ p->msg.MSG_FIELD, LEN);\
+ } while (0)
+
+# define PROGRESS_SET_LABEL(FIELD, LABEL_TABLE, MSG_FIELD)\
+ do {\
+ progress_set_label (L,\
+ progress_field + progress_field_ ## FIELD ## _label,\
+ LABEL_TABLE,\
+ p->msg.MSG_FIELD);\
+ } while (0)
+
+ PROGRESS_SET_INTEGER (apiversion, apiversion);
+ PROGRESS_SET_INTEGER (status, status);
+ PROGRESS_SET_INTEGER (download, dwl_percent);
+ PROGRESS_SET_INTEGER (source, source);
+ PROGRESS_SET_INTEGER (nsteps, nsteps);
+ PROGRESS_SET_INTEGER (step, cur_step);
+ PROGRESS_SET_INTEGER (percent, cur_percent);
+ PROGRESS_SET_STRING (artifact, cur_image);
+ PROGRESS_SET_STRING (handler, hnd_name);
if (p->msg.infolen)
- LUA_PUSH_STRING("info", p->
msg.info);
+ PROGRESS_SET_LSTRING (info, info, p->msg.infolen);
+
+ PROGRESS_SET_LABEL (status, &recovery_table, status);
+ PROGRESS_SET_LABEL (source, &source_type_table, source);
+
+# undef PROGRESS_SET_LABEL
+# undef PROGRESS_SET_STRING
+# undef PROGRESS_SET_INTEGER
+
+ /* pt */
p->status = p->msg.status;
@@ -354,39 +561,136 @@ static const luaL_Reg lua_swupdate[] = {
{"progress", progress},
{"control", ctrl},
{"ipv4", netif},
- {NULL, NULL}
+ {}
};
/*
* Initialization of C module
*/
-int luaopen_lua_swupdate(lua_State *L){
- luaL_newlib(L, lua_swupdate);
- /* Export the RECOVERY_STATUS enum */
- lua_pushstring(L, "RECOVERY_STATUS");
+struct label_init {
+ int cardinal;
+ char const *label;
+ size_t label_len;
+};
+
+/**
+ * @brief Initialize label mapping table and its inverse.
+ *
+ * The lua module table must be at the top of the Lua stack.
+ * On return, the stack has the same form.
+ *
+ * @param<in> table_name: the table's name
+ * @param<in> table_name_len: the table name's length
+ * @param<in> anchor: a unique address used to anchor the inverse table as Lua lightuserdata
+ * @param<in> li: the start of the list of labels to be stored in the table and its inverse
+ * @param<in> li_end: the end of the list of labels to be stored in the table and its inverse
+ */
+static void process_labels (lua_State *const L,
+ char const *const table_name,
+ size_t const table_name_len,
+ int const *const anchor,
+ struct label_init const *li,
+ struct label_init const *const li_end)
+{
+ /* mt */
+# if 0
+ char const *const table_lua =
+# endif
+ lua_pushlstring (L, table_name, table_name_len);
+ /* mt tn */
lua_newtable (L);
- LUA_PUSH_INT("IDLE", IDLE);
- LUA_PUSH_INT("START", START);
- LUA_PUSH_INT("RUN", RUN);
- LUA_PUSH_INT("SUCCESS", SUCCESS);
- LUA_PUSH_INT("FAILURE", FAILURE);
- LUA_PUSH_INT("DOWNLOAD", DOWNLOAD);
- LUA_PUSH_INT("DONE", DONE);
- LUA_PUSH_INT("SUBPROCESS", SUBPROCESS);
- LUA_PUSH_INT("PROGRESS", PROGRESS);
- lua_settable(L, -3);
+ /* mt tn t */
+ lua_newtable (L);
+ /* mt tn t t_inv */
+ PR_LUA_PUSHLIGHTUSERDATA (L, anchor);
+ /* mt tn t t_inv lu_a */
+ lua_pushvalue (L, -2);
+ /* mt tn t t_inv lu_a_inv t_inv */
+ lua_rawset (L, LUA_REGISTRYINDEX);
+ /* mt tn t t_inv */
+
+ for (; li_end > li; ++li) {
+ /* mt tn t t_inv */
+# if 0
+ char const *const label_lua =
+# endif
+ lua_pushlstring (L, li->label, li->label_len);
+ /* mt tn t t_inv s */
+ lua_pushinteger (L, li->cardinal);
+ /* mt tn t t_inv s i */
+ lua_pushvalue (L, -2);
+ /* mt tn t t_inv s i s */
+ lua_rawseti (L, -4, li->cardinal);
+ /* mt tn t t_inv s i */
+ lua_rawset (L, -4);
+ /* mt tn t t_inv */
+ }
+ /* mt tn t t_inv */
+ lua_pop (L, 1);
+ /* mt tn t */
+ lua_rawset (L, -3);
+ /* mt */
+}
+
+# pragma GCC diagnostic ignored "-Wmissing-prototypes"
+int
+luaopen_lua_swupdate(lua_State *L)
+{
+ luaL_newlib(L, lua_swupdate);
+ /* mt */
+
+# define LABEL_INIT(X)\
+ { X, #X, sizeof #X - sizeof "" },
+ /* Export the RECOVERY_STATUS enum */
+ static struct label_init const recovery_labels [] = {
+ LABEL_INIT (IDLE)
+ LABEL_INIT (START)
+ LABEL_INIT (RUN)
+ LABEL_INIT (SUCCESS)
+ LABEL_INIT (FAILURE)
+ LABEL_INIT (DOWNLOAD)
+ LABEL_INIT (DONE)
+ LABEL_INIT (SUBPROCESS)
+ LABEL_INIT (PROGRESS)
+ };
+ static char const recovery_status [] = "RECOVERY_STATUS";
+ process_labels (L, recovery_status, sizeof recovery_status - sizeof "",
+ &recovery_table,
+ recovery_labels, 1 [&recovery_labels]);
+ /* mt */
/* Export the sourcetype enum */
- lua_pushstring(L, "sourcetype");
- lua_newtable (L);
- LUA_PUSH_INT("SOURCE_UNKNOWN", SOURCE_UNKNOWN);
- LUA_PUSH_INT("SOURCE_WEBSERVER", SOURCE_WEBSERVER);
- LUA_PUSH_INT("SOURCE_SURICATTA", SOURCE_SURICATTA);
- LUA_PUSH_INT("SOURCE_DOWNLOADER", SOURCE_DOWNLOADER);
- LUA_PUSH_INT("SOURCE_LOCAL", SOURCE_LOCAL);
- LUA_PUSH_INT("SOURCE_CHUNKS_DOWNLOADER", SOURCE_CHUNKS_DOWNLOADER);
- lua_settable(L, -3);
+ static struct label_init const source_labels [] = {
+
+# define SOURCE_LABEL_INIT(S)\
+ LABEL_INIT (SOURCE_ ## S)
+
+ SOURCE_LABEL_INIT (UNKNOWN)
+ SOURCE_LABEL_INIT (WEBSERVER)
+ SOURCE_LABEL_INIT (SURICATTA)
+ SOURCE_LABEL_INIT (DOWNLOADER)
+ SOURCE_LABEL_INIT (LOCAL)
+ SOURCE_LABEL_INIT (CHUNKS_DOWNLOADER)
+ };
+ static char const source_type [] = "sourcetype";
+ process_labels (L, source_type, sizeof source_type - sizeof "",
+ &source_type_table,
+ source_labels, 1 [&source_labels]);
+ /* mt */
+
+ struct progress_field const *f;
+ for (f = progress_field; 1 [&progress_field] > f; ++f) {
+ /* mt */
+ PR_LUA_PUSHLIGHTUSERDATA (L, f);
+ /* mt luf */
+ lua_pushlstring (L, f->name, f->len);
+ /* mt luf s */
+ lua_rawset (L, LUA_REGISTRYINDEX);
+ /* mt */
+ }
+
+ /* mt */
auxiliar_newclass(L, "swupdate_progress", progress_methods);
auxiliar_newclass(L, "swupdate_control", ctrl_methods);
diff --git a/bindings/lua_swupdate.lua b/bindings/lua_swupdate.lua
index b21294b5..f3470189 100644
--- a/bindings/lua_swupdate.lua
+++ b/bindings/lua_swupdate.lua
@@ -43,7 +43,7 @@ lua_swupdate.RECOVERY_STATUS = {
DOWNLOAD = 5,
DONE = 6,
SUBPROCESS = 7,
- PROGRESS = 8
+ PROGRESS = 8,
}
@@ -55,7 +55,7 @@ lua_swupdate.sourcetype = {
SOURCE_SURICATTA = 2,
SOURCE_DOWNLOADER = 3,
SOURCE_LOCAL = 4,
- SOURCE_CHUNKS_DOWNLOADER = 5
+ SOURCE_CHUNKS_DOWNLOADER = 5,
}
@@ -70,7 +70,11 @@ lua_swupdate.sourcetype = {
--- @field percent number Percentage done in current step
--- @field artifact string Name of image to be installed
--- @field handler string Name of running Handler
---- @field info string Additional information about installation
+--- @field info string Additional information about installation, optional
+---
+--- @field status_label string The (string) label corresponding to field status
+--- @field source_label string The (string) label corresponding to field source
+
--- SWUpdate progress socket binding.
--
2.47.3