[PATCH 1/3] suricatta: Suricatta module run-time selection

Yametazamwa mara 15
Ruka hadi kwenye ujumbe wa kwanza ambao haujasomwa

Christian Storm

hayajasomwa,
9 Mei 2023, 04:05:5609/05/2023
kwa swup...@googlegroups.com,Christian Storm
Allow to compile-in multiple suricatta modules and choose one
at run-time with sane fallback to a single compiled-in one.
Defaults to hawkBit for backwards compatibility.

This feature enables distributions to ship SWUpdate with
multiple suricatta modules enabled, shifting the selection
to run-time rather than compile-time.

Signed-off-by: Christian Storm <christi...@siemens.com>
---
Makefile | 4 +-
include/suricatta/server.h | 24 ++--------
suricatta/Config.in | 30 +++++--------
suricatta/Makefile | 11 +++--
suricatta/suricatta.c | 89 ++++++++++++++++++++++++++++++++++----
5 files changed, 104 insertions(+), 54 deletions(-)

diff --git a/Makefile b/Makefile
index 9f6a33a6..41abd303 100644
--- a/Makefile
+++ b/Makefile
@@ -362,8 +362,8 @@ include $(srctree)/Makefile.flags
# This allow a user to issue only 'make' to build a kernel including modules
# Defaults to vmlinux, but the arch makefile usually adds further targets

-objs-y := core handlers bootloader
-libs-y := corelib mongoose parser suricatta fs
+objs-y := core handlers bootloader suricatta
+libs-y := corelib mongoose parser fs
bindings-y := bindings
tools-y := tools

diff --git a/include/suricatta/server.h b/include/suricatta/server.h
index 07981df6..451211e9 100644
--- a/include/suricatta/server.h
+++ b/include/suricatta/server.h
@@ -16,17 +16,7 @@
* Cf. `server_hawkbit.c` for an example implementation targeted towards the
* [hawkBit](https://projects.eclipse.org/projects/iot.hawkbit) server.
*/
-
-extern server_op_res_t server_has_pending_action(int *action_id);
-extern server_op_res_t server_install_update(void);
-extern server_op_res_t server_send_target_data(void);
-extern unsigned int server_get_polling_interval(void);
-extern server_op_res_t server_start(const char *cfgfname, int argc, char *argv[]);
-extern server_op_res_t server_stop(void);
-extern server_op_res_t server_ipc(ipc_message *msg);
-extern void server_print_help(void);
-
-static struct server_t {
+typedef struct {
server_op_res_t (*has_pending_action)(int *action_id);
server_op_res_t (*install_update)(void);
server_op_res_t (*send_target_data)(void);
@@ -35,12 +25,6 @@ static struct server_t {
server_op_res_t (*stop)(void);
server_op_res_t (*ipc)(ipc_message *msg);
void (*help)(void);
-} server = {.has_pending_action = &server_has_pending_action,
- .install_update = &server_install_update,
- .send_target_data = &server_send_target_data,
- .get_polling_interval = &server_get_polling_interval,
- .start = &server_start,
- .stop = &server_stop,
- .ipc = &server_ipc,
- .help = &server_print_help,
-};
+} server_t;
+
+bool register_server(const char *name, server_t *server);
diff --git a/suricatta/Config.in b/suricatta/Config.in
index 29dc7386..0fde4401 100644
--- a/suricatta/Config.in
+++ b/suricatta/Config.in
@@ -5,6 +5,7 @@
menuconfig SURICATTA
bool "Suricatta"
depends on HAVE_LIBCURL
+ select CHANNEL_CURL
help
Suricatta is a daemon mode of SWUpdate.

@@ -34,17 +35,10 @@ endmenu

menu "Server"

-choice
- prompt "Server Type"
- default SURICATTA_HAWKBIT
- help
- Choose the server type. Currently, just hawkBit is
- supported.
-
config SURICATTA_HAWKBIT
bool "hawkBit support"
+ default y
depends on HAVE_JSON_C
- select CHANNEL_CURL
select JSON
help
Support for hawkBit server.
@@ -55,9 +49,10 @@ comment "hawkBit support needs json-c"

config SURICATTA_LUA
bool "Suricatta Lua module"
- depends on HAVE_LIBCURL
depends on HAVE_LUA
- select CHANNEL_CURL
+ depends on HAVE_JSON_C
+ select LUA
+ select JSON
help
Support for Suricatta modules in Lua.

@@ -65,7 +60,10 @@ config SURICATTA_LUA
provides JSON as Lua Tables to the Lua realm and
enables channel result parsing to JSON per default.
To enable, select 'libjson' in 'Parser Features'.
-
+
+comment "Suricatta Lua module support needs Lua and json-c"
+ depends on !HAVE_LUA || !HAVE_JSON_C
+
config EMBEDDED_SURICATTA_LUA
bool "Embed Suricatta Lua module in SWUpdate binary"
depends on SURICATTA_LUA
@@ -90,23 +88,17 @@ config EMBEDDED_SURICATTA_LUA_SOURCE
Path to the Suricatta Lua module source code file to
be embedded into the SWUpdate binary.

-comment "Suricatta Lua module support needs libcurl and Lua"
- depends on !HAVE_LIBCURL || !HAVE_LUA
-
-comment "Suricatta Lua module JSON support needs json-c"
- depends on SURICATTA_LUA && !JSON
-
config SURICATTA_GENERAL
bool "General HTTP support"
depends on HAVE_JSON_C
- select CHANNEL_CURL
select JSON
help
Support for Simple HTTP coded server
The server uses HTTP return codes to detect if an update
is available. See documentation for more details.

-endchoice
+comment "General HTTP support needs json-c"
+ depends on !HAVE_JSON_C

endmenu

diff --git a/suricatta/Makefile b/suricatta/Makefile
index 8fbb1ce9..f5b2bbdf 100644
--- a/suricatta/Makefile
+++ b/suricatta/Makefile
@@ -1,11 +1,14 @@
# Copyright (C) 2014-2018 Stefano Babic <sba...@denx.de>
#
# SPDX-License-Identifier: GPL-2.0-only
-lib-$(CONFIG_SURICATTA) += suricatta.o common.o
+obj-$(CONFIG_SURICATTA) += suricatta.o common.o
ifneq ($(CONFIG_SURICATTA_HAWKBIT),)
-lib-$(CONFIG_SURICATTA) += server_hawkbit.o
+obj-$(CONFIG_SURICATTA) += server_hawkbit.o
endif
ifneq ($(CONFIG_SURICATTA_LUA),)
-lib-$(CONFIG_SURICATTA) += server_lua.o
+obj-$(CONFIG_SURICATTA) += server_lua.o
endif
-lib-$(CONFIG_SURICATTA_GENERAL) += server_general.o
+ifneq ($(CONFIG_SURICATTA_GENERAL),)
+obj-$(CONFIG_SURICATTA_GENERAL) += server_general.o
+endif
+
diff --git a/suricatta/suricatta.c b/suricatta/suricatta.c
index b10da933..394019f3 100644
--- a/suricatta/suricatta.c
+++ b/suricatta/suricatta.c
@@ -30,9 +30,44 @@ static bool trigger = false;
static struct option long_options[] = {
{"enable", no_argument, NULL, 'e'},
{"disable", no_argument, NULL, 'd'},
+ {"server", required_argument, NULL, 'S'},
{NULL, 0, NULL, 0}};
static sem_t suricatta_enable_sema;

+typedef struct {
+ const char *name;
+ server_t *funcs;
+} server_entry;
+
+static int servers_count = 0;
+static server_entry *servers = NULL;
+static server_t *server = NULL;
+
+bool register_server(const char *name, server_t *srv)
+{
+ server_entry *tmp = realloc(servers,
+ (servers_count + 1) * sizeof(server_entry));
+ if (!tmp) {
+ return false;
+ }
+ tmp[servers_count].name = name;
+ tmp[servers_count].funcs = srv;
+ servers_count++;
+ servers = tmp;
+ return true;
+}
+
+static bool set_server(const char *name)
+{
+ for (unsigned int i = 0; i < servers_count; i++) {
+ if (strcmp(name, servers[i].name) == 0) {
+ server = servers[i].funcs;
+ return true;
+ }
+ }
+ return false;
+}
+
void suricatta_print_help(void)
{
fprintf(
@@ -40,8 +75,17 @@ void suricatta_print_help(void)
"\tsuricatta arguments (mandatory arguments are marked with '*'):\n"
"\t -e, --enable Daemon enabled at startup (default).\n"
"\t -d, --disable Daemon disabled at startup.\n"
+ "\t -S, --server Suricatta module to run.\n"
);
- server.help();
+ if (servers_count == 0) {
+ fprintf(stdout, "\tNo compiled-in suricatta modules!\n");
+ return;
+ }
+ for (unsigned int i = 0; i < servers_count; i++) {
+ fprintf(stdout, "\tOptions for suricatta module '%s':\n",
+ servers[i].name);
+ (servers[i].funcs)->help();
+ }
}

static server_op_res_t suricatta_enable(ipc_message *msg)
@@ -103,7 +147,7 @@ static server_op_res_t suricatta_ipc(int fd)
result = suricatta_enable(&msg);
break;
default:
- result = server.ipc(&msg);
+ result = server->ipc(&msg);
break;
}

@@ -120,6 +164,12 @@ static int suricatta_settings(void *elem, void __attribute__ ((__unused__)) *da
get_field(LIBCFG_PARSER, elem, "enable",
&enable);

+ char cfg_server[128];
+ GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "server", cfg_server);
+ if (strlen(cfg_server) && set_server(cfg_server)) {
+ TRACE("Suricatta module '%s' selected by configuration file.", cfg_server);
+ }
+
return 0;
}

@@ -227,9 +277,16 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
optind = 1;
opterr = 0;

- while ((choice = getopt_long(argc, argv, "de",
+ while ((choice = getopt_long(argc, argv, "deS:",
long_options, NULL)) != -1) {
switch (choice) {
+ case 'S':
+ if (!set_server(optarg)) {
+ ERROR("Suricatta module '%s' not registered.", optarg);
+ exit(EXIT_FAILURE);
+ }
+ TRACE("Suricatta module '%s' selected by command line option.", optarg);
+ break;
case 'e':
enable = true;
break;
@@ -240,6 +297,20 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
break;
}
}
+ if (!server) {
+ if (servers_count == 0) {
+ ERROR("No compiled-in suricatta modules!");
+ exit(EXIT_FAILURE);
+ }
+ if (servers_count == 1) {
+ (void)set_server(servers[0].name);
+ TRACE("Default suricatta module '%s' selected.", servers[0].name);
+
+ } else {
+ ERROR("Multiple suricatta modules available but none selected.");
+ exit(EXIT_FAILURE);
+ }
+ }

if (sem_init(&suricatta_enable_sema, 0, 0)) {
ERROR("Initialising suricatta enable semaphore failed");
@@ -254,7 +325,7 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
/*
* Now start a specific implementation of the server
*/
- if (server.start(cfgfname, argc, serverargv) != SERVER_OK) {
+ if (server->start(cfgfname, argc, serverargv) != SERVER_OK) {
exit(EXIT_FAILURE);
}
free(serverargv);
@@ -263,13 +334,13 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
while (true) {
if (enable || trigger) {
trigger = false;
- switch (server.has_pending_action(&action_id)) {
+ switch (server->has_pending_action(&action_id)) {
case SERVER_UPDATE_AVAILABLE:
DEBUG("About to process available update.");
- server.install_update();
+ server->install_update();
break;
case SERVER_ID_REQUESTED:
- server.send_target_data();
+ server->send_target_data();
trigger = true;
break;
case SERVER_EINIT:
@@ -281,9 +352,9 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
}
}

- for (int wait_seconds = server.get_polling_interval();
+ for (int wait_seconds = server->get_polling_interval();
wait_seconds > 0;
- wait_seconds = min(wait_seconds, (int)server.get_polling_interval())) {
+ wait_seconds = min(wait_seconds, (int)server->get_polling_interval())) {
wait_seconds = suricatta_wait(wait_seconds);
}

--
2.40.1

Christian Storm

hayajasomwa,
9 Mei 2023, 04:06:1809/05/2023
kwa swup...@googlegroups.com,Christian Storm
Adapt suricatta modules to run-time selection.

Signed-off-by: Christian Storm <christi...@siemens.com>
---
suricatta/server_general.c | 50 ++++++++++++++++++++-------------
suricatta/server_general.h | 1 -
suricatta/server_hawkbit.c | 57 +++++++++++++++++++++++---------------
suricatta/server_hawkbit.h | 3 ++
suricatta/server_lua.c | 47 +++++++++++++++++--------------
test/test_server_hawkbit.c | 15 +++++-----
6 files changed, 101 insertions(+), 72 deletions(-)

diff --git a/suricatta/server_general.c b/suricatta/server_general.c
index e7a8c301..1c8e777e 100644
--- a/suricatta/server_general.c
+++ b/suricatta/server_general.c
@@ -25,6 +25,7 @@
#include <sys/time.h>
#include <swupdate_status.h>
#include "suricatta/suricatta.h"
+#include "suricatta/server.h"
#include "suricatta_private.h"
#include "parselib.h"
#include "channel.h"
@@ -39,16 +40,6 @@
#include <pctl.h>
#include <pthread.h>

-/* Prototypes for "public" functions */
-void server_print_help(void);
-server_op_res_t server_has_pending_action(int *action_id);
-server_op_res_t server_stop(void);
-server_op_res_t server_ipc(ipc_message *msg);
-server_op_res_t server_start(char *fname, int argc, char *argv[]);
-server_op_res_t server_install_update(void);
-server_op_res_t server_send_target_data(void);
-unsigned int server_get_polling_interval(void);
-
/*
* This is a "specialized" map_http_retcode() because
* the return codes are interpreted
@@ -66,6 +57,7 @@ static struct option long_options[] = {
{"retrywait", required_argument, NULL, 'w'},
{"cache", required_argument, NULL, '2'},
{"max-download-speed", required_argument, NULL, 'n'},
+ {"server", required_argument, NULL, 'S'},
{NULL, 0, NULL, 0}};

static unsigned short mandatory_argument_count = 0;
@@ -483,7 +475,7 @@ static server_op_res_t server_get_deployment_info(channel_t *channel, channel_da
return result;
}

-server_op_res_t server_has_pending_action(int *action_id)
+static server_op_res_t server_has_pending_action(int *action_id)
{
*action_id = 0;

@@ -497,17 +489,17 @@ server_op_res_t server_has_pending_action(int *action_id)
&channel_data);
}

-server_op_res_t server_send_target_data(void)
+static server_op_res_t server_send_target_data(void)
{
return SERVER_OK;
}

-unsigned int server_get_polling_interval(void)
+static unsigned int server_get_polling_interval(void)
{
return server_general.polling_interval;
}

-void server_print_help(void)
+static void server_print_help(void)
{
fprintf(
stdout,
@@ -529,7 +521,7 @@ void server_print_help(void)
CHANNEL_DEFAULT_RESUME_DELAY);
}

-server_op_res_t server_install_update(void)
+static server_op_res_t server_install_update(void)
{
channel_data_t channel_data = channel_data_defaults;
server_op_res_t result = SERVER_OK;
@@ -605,7 +597,7 @@ static int server_general_settings(void *elem, void __attribute__ ((__unused__)
return 0;
}

-server_op_res_t server_start(char *fname, int argc, char *argv[])
+static server_op_res_t server_start(const char *fname, int argc, char *argv[])
{
int choice = 0;

@@ -629,7 +621,7 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
/* reset to optind=1 to parse suricatta's argument vector */
optind = 1;
opterr = 0;
- while ((choice = getopt_long(argc, argv, "u:l:r:w:p:2:a:n",
+ while ((choice = getopt_long(argc, argv, "u:l:r:w:p:2:a:nS:",
long_options, NULL)) != -1) {
switch (choice) {
case 'u':
@@ -671,6 +663,9 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
(unsigned int)ustrtoull(optarg, NULL, 10);
break;

+ /* Ignore the --server option which is already parsed by the caller. */
+ case 'S':
+ break;
case '?':
/* Ignore not recognized options, they can be already parsed by the caller */
default:
@@ -713,7 +708,7 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
return SERVER_OK;
}

-server_op_res_t server_stop(void)
+static server_op_res_t server_stop(void)
{
(void)server_general.channel->close(server_general.channel);
free(server_general.channel);
@@ -721,7 +716,24 @@ server_op_res_t server_stop(void)
return SERVER_OK;
}

-server_op_res_t server_ipc(ipc_message __attribute__ ((__unused__)) *msg)
+static server_op_res_t server_ipc(ipc_message __attribute__ ((__unused__)) *msg)
{
return SERVER_OK;
}
+
+static server_t server = {
+ .has_pending_action = &server_has_pending_action,
+ .install_update = &server_install_update,
+ .send_target_data = &server_send_target_data,
+ .get_polling_interval = &server_get_polling_interval,
+ .start = &server_start,
+ .stop = &server_stop,
+ .ipc = &server_ipc,
+ .help = &server_print_help,
+};
+
+__attribute__((constructor))
+static void register_server_general(void)
+{
+ register_server("general", &server);
+}
diff --git a/suricatta/server_general.h b/suricatta/server_general.h
index d5d8d48f..3a3ef4f7 100644
--- a/suricatta/server_general.h
+++ b/suricatta/server_general.h
@@ -29,4 +29,3 @@ typedef struct {
channel_t *channel;
} server_general_t;

-extern server_general_t server_general;
diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c
index 5a456e11..726ef191 100644
--- a/suricatta/server_hawkbit.c
+++ b/suricatta/server_hawkbit.c
@@ -21,6 +21,7 @@
#include <swupdate_status.h>
#include <pthread.h>
#include "suricatta/suricatta.h"
+#include "suricatta/server.h"
#include "suricatta_private.h"
#include "parselib.h"
#include "channel.h"
@@ -51,6 +52,7 @@ static struct option long_options[] = {
{"disable-token-for-dwl", no_argument, NULL, '1'},
{"cache", required_argument, NULL, '2'},
{"initial-report-resend-period", required_argument, NULL, 'm'},
+ {"server", required_argument, NULL, 'S'},
{"connection-timeout", required_argument, NULL, 's'},
{"custom-http-header", required_argument, NULL, 'a'},
{"max-download-speed", required_argument, NULL, 'l'},
@@ -90,19 +92,17 @@ static hawkbit_enums_t hawkbit_enums[] = {

extern channel_op_res_t channel_curl_init(void);
/* Prototypes for "internal" functions */
-/* Note that they're not `static` so that they're callable from unit tests. */
-server_op_res_t server_handle_initial_state(update_state_t stateovrrd);
+/* Note that the non-`static` functions are called from unit tests. */
+static server_op_res_t server_handle_initial_state(update_state_t stateovrrd);
static int server_update_status_callback(ipc_message *msg);
-int server_update_done_callback(RECOVERY_STATUS status);
server_op_res_t server_process_update_artifact(int action_id,
json_object *json_data_artifact,
const char *update_action,
const char *part,
const char *version,
const char *name);
-void server_print_help(void);
server_op_res_t server_set_polling_interval_json(json_object *json_root);
-server_op_res_t server_set_config_data(json_object *json_root);
+static server_op_res_t server_set_config_data(json_object *json_root);
server_op_res_t
server_send_deployment_reply(channel_t *channel,
const int action_id, const int job_cnt_max,
@@ -146,15 +146,6 @@ static channel_data_t channel_data_defaults = {.debug = false,

static struct timeval server_time;

-/* Prototypes for "public" functions */
-server_op_res_t server_has_pending_action(int *action_id);
-server_op_res_t server_stop(void);
-server_op_res_t server_ipc(ipc_message *msg);
-server_op_res_t server_start(char *fname, int argc, char *argv[]);
-server_op_res_t server_install_update(void);
-server_op_res_t server_send_target_data(void);
-unsigned int server_get_polling_interval(void);
-
/*
* Just called once to setup the tokens
*/
@@ -503,7 +494,7 @@ server_op_res_t server_set_polling_interval_json(json_object *json_root)
return SERVER_OK;
}

-unsigned int server_get_polling_interval(void)
+static unsigned int server_get_polling_interval(void)
{
return server_hawkbit.polling_interval;
}
@@ -741,7 +732,7 @@ static size_t server_check_during_dwl(char __attribute__ ((__unused__)) *stream
return ret;
}

-server_op_res_t server_has_pending_action(int *action_id)
+static server_op_res_t server_has_pending_action(int *action_id)
{

channel_data_t channel_data = channel_data_defaults;
@@ -1285,7 +1276,7 @@ cleanup:
return result;
}

-server_op_res_t server_install_update(void)
+static server_op_res_t server_install_update(void)
{
int action_id;
channel_data_t channel_data = channel_data_defaults;
@@ -1503,7 +1494,7 @@ int get_target_data_length(bool locked)
return len;
}

-server_op_res_t server_send_target_data(void)
+static server_op_res_t server_send_target_data(void)
{
channel_t *channel = server_hawkbit.channel;
struct dict_entry *entry;
@@ -1619,7 +1610,7 @@ cleanup:
return result;
}

-void server_print_help(void)
+static void server_print_help(void)
{
fprintf(
stdout,
@@ -1701,7 +1692,7 @@ static int server_hawkbit_settings(void *elem, void __attribute__ ((__unused__)

}

-server_op_res_t server_start(char *fname, int argc, char *argv[])
+static server_op_res_t server_start(const char *fname, int argc, char *argv[])
{
update_state_t update_state = STATE_NOT_AVAILABLE;
int choice = 0;
@@ -1743,7 +1734,7 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
/* reset to optind=1 to parse suricatta's argument vector */
optind = 1;
opterr = 0;
- while ((choice = getopt_long(argc, argv, "t:i:c:u:p:xr:y::w:k:g:f:2:m:s:a:n:",
+ while ((choice = getopt_long(argc, argv, "t:i:c:u:p:xr:y::w:k:g:f:2:m:s:a:n:S:",
long_options, NULL)) != -1) {
switch (choice) {
case 't':
@@ -1853,6 +1844,9 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
/* Ignore not recognized options, they can be already parsed by the caller */
case '?':
break;
+ /* Ignore the --server option which is already parsed by the caller. */
+ case 'S':
+ break;
}
}

@@ -1932,7 +1926,7 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
return SERVER_OK;
}

-server_op_res_t server_stop(void)
+static server_op_res_t server_stop(void)
{
(void)server_hawkbit.channel->close(server_hawkbit.channel);
free(server_hawkbit.channel);
@@ -2122,7 +2116,7 @@ static server_op_res_t server_status_ipc(ipc_message *msg)
return SERVER_OK;
}

-server_op_res_t server_ipc(ipc_message *msg)
+static server_op_res_t server_ipc(ipc_message *msg)
{
server_op_res_t result = SERVER_OK;

@@ -2150,3 +2144,20 @@ server_op_res_t server_ipc(ipc_message *msg)

return SERVER_OK;
}
+
+server_t server_hawkbit_funcs = {
+ .has_pending_action = &server_has_pending_action,
+ .install_update = &server_install_update,
+ .send_target_data = &server_send_target_data,
+ .get_polling_interval = &server_get_polling_interval,
+ .start = &server_start,
+ .stop = &server_stop,
+ .ipc = &server_ipc,
+ .help = &server_print_help,
+};
+
+__attribute__((constructor))
+static void register_server_hawkbit(void)
+{
+ register_server("hawkbit", &server_hawkbit_funcs);
+}
diff --git a/suricatta/server_hawkbit.h b/suricatta/server_hawkbit.h
index 86627ee2..5f995aca 100644
--- a/suricatta/server_hawkbit.h
+++ b/suricatta/server_hawkbit.h
@@ -10,6 +10,7 @@
#include <state.h>
#include <swupdate_dict.h>
#include <channel.h>
+#include "suricatta/server.h"

/* Have a queue for errors reported by installer */
#define HAWKBIT_MAX_REPORTED_ERRORS 10
@@ -51,6 +52,8 @@ typedef struct {

extern server_hawkbit_t server_hawkbit;

+extern server_t server_hawkbit_funcs;
+
static const struct {
const char *closed;
const char *proceeding;
diff --git a/suricatta/server_lua.c b/suricatta/server_lua.c
index 7209120e..579ddc70 100644
--- a/suricatta/server_lua.c
+++ b/suricatta/server_lua.c
@@ -34,6 +34,7 @@
#include <swupdate_settings.h>
#include <swupdate_dict.h>
#include "suricatta_private.h"
+#include "suricatta/server.h"

#define CONFIG_SECTION "suricatta"

@@ -77,19 +78,6 @@ static channel_data_t channel_data_defaults = {
.source = SOURCE_SURICATTA,
};

-/*
- * Prototypes for "public" functions implementing the server
- * interface specified in include/suricatta/server.h.
- */
-void server_print_help(void);
-unsigned int server_get_polling_interval(void);
-server_op_res_t server_has_pending_action(int *action_id);
-server_op_res_t server_start(char *fname, int argc, char *argv[]);
-server_op_res_t server_stop(void);
-server_op_res_t server_install_update(void);
-server_op_res_t server_ipc(ipc_message *msg);
-server_op_res_t server_send_target_data(void);
-
/* Global Lua state for this Suricatta Lua module implementation. */
static lua_State *gL = NULL;

@@ -1827,7 +1815,7 @@ static int config_section_to_table(void *setting, void *data)
* @param argv The array of arguments.
* @return SERVER_OK, or, in case of errors, SERVER_EINIT or SERVER_EERR.
*/
-server_op_res_t server_start(char *fname, int argc, char *argv[])
+static server_op_res_t server_start(const char *fname, int argc, char *argv[])
{
if (suricatta_lua_create() != SERVER_OK) {
suricatta_lua_destroy();
@@ -1873,7 +1861,7 @@ server_op_res_t server_start(char *fname, int argc, char *argv[])
*
* @return SERVER_OK or, in case of errors, any other from server_op_res_t.
*/
-server_op_res_t server_stop(void)
+static server_op_res_t server_stop(void)
{
server_op_res_t result = map_lua_result(
call_lua_func(gL, SURICATTA_FUNC_SERVER_STOP, 0));
@@ -1885,7 +1873,7 @@ server_op_res_t server_stop(void)
/**
* @brief Print the Suricatta Lua module's help text.
*/
-void server_print_help(void)
+static void server_print_help(void)
{
if (suricatta_lua_create() != SERVER_OK) {
fprintf(stderr, "Error loading Suricatta Lua module.\n");
@@ -1908,7 +1896,7 @@ void server_print_help(void)
*
* @return Polling interval in seconds.
*/
-unsigned int server_get_polling_interval(void)
+static unsigned int server_get_polling_interval(void)
{
int result = call_lua_func(gL, SURICATTA_FUNC_GET_POLLING_INTERVAL, 0);
return result >= 0 ? (unsigned int)result : CHANNEL_DEFAULT_POLLING_INTERVAL;
@@ -1923,7 +1911,7 @@ unsigned int server_get_polling_interval(void)
* in suricatta/suricatta.c, the others result in suricatta
* sleeping again.
*/
-server_op_res_t server_has_pending_action(int *action_id)
+static server_op_res_t server_has_pending_action(int *action_id)
{
lua_pushnumber(gL, *action_id);
server_op_res_t result = map_lua_result(
@@ -1945,7 +1933,7 @@ server_op_res_t server_has_pending_action(int *action_id)
*
* @return SERVER_OK or, in case of errors, any other from server_op_res_t.
*/
-server_op_res_t server_install_update(void)
+static server_op_res_t server_install_update(void)
{
return map_lua_result(call_lua_func(gL, SURICATTA_FUNC_INSTALL_UPDATE, 0));
}
@@ -1956,7 +1944,7 @@ server_op_res_t server_install_update(void)
*
* @return SERVER_OK or, in case of errors, any other from server_op_res_t.
*/
-server_op_res_t server_send_target_data(void)
+static server_op_res_t server_send_target_data(void)
{
return map_lua_result(call_lua_func(gL, SURICATTA_FUNC_SEND_TARGET_DATA, 0));
}
@@ -1968,7 +1956,7 @@ server_op_res_t server_send_target_data(void)
* @param msg IPC message.
* @return SERVER_OK or, in case of errors, any other from server_op_res_t.
*/
-server_op_res_t server_ipc(ipc_message *msg)
+static server_op_res_t server_ipc(ipc_message *msg)
{
lua_newtable(gL);
push_to_table(gL, "magic", msg->magic);
@@ -2014,3 +2002,20 @@ server_op_res_t server_ipc(ipc_message *msg)
}
return result;
}
+
+static server_t server = {
+ .has_pending_action = &server_has_pending_action,
+ .install_update = &server_install_update,
+ .send_target_data = &server_send_target_data,
+ .get_polling_interval = &server_get_polling_interval,
+ .start = &server_start,
+ .stop = &server_stop,
+ .ipc = &server_ipc,
+ .help = &server_print_help,
+};
+
+__attribute__((constructor))
+static void register_server_lua(void)
+{
+ register_server("lua", &server);
+}
diff --git a/test/test_server_hawkbit.c b/test/test_server_hawkbit.c
index c5f558be..fc4bb8f5 100644
--- a/test/test_server_hawkbit.c
+++ b/test/test_server_hawkbit.c
@@ -118,7 +118,6 @@ update_state_t __wrap_get_state(void)
return mock_type(update_state_t);
}

-extern server_op_res_t server_has_pending_action(int *action_id);
static void test_server_has_pending_action(void **state)
{
(void)state;
@@ -194,7 +193,7 @@ static void test_server_has_pending_action(void **state)
json_tokener_parse(json_reply_no_update));
will_return(__wrap_channel_get, CHANNEL_OK);
assert_int_equal(SERVER_NO_UPDATE_AVAILABLE,
- server_has_pending_action(&action_id));
+ server_hawkbit_funcs.has_pending_action(&action_id));

/* Test Case: Update Action available && !STATE_INSTALLED. */
will_return(__wrap_channel_get,
@@ -205,7 +204,7 @@ static void test_server_has_pending_action(void **state)
will_return(__wrap_channel_get, CHANNEL_OK);
will_return(__wrap_get_state, STATE_NOT_AVAILABLE);
assert_int_equal(SERVER_UPDATE_AVAILABLE,
- server_has_pending_action(&action_id));
+ server_hawkbit_funcs.has_pending_action(&action_id));

/* Test Case: Update Action available && STATE_INSTALLED. */
will_return(__wrap_channel_get,
@@ -216,7 +215,7 @@ static void test_server_has_pending_action(void **state)
will_return(__wrap_channel_get, CHANNEL_OK);
will_return(__wrap_get_state, STATE_INSTALLED);
assert_int_equal(SERVER_NO_UPDATE_AVAILABLE,
- server_has_pending_action(&action_id));
+ server_hawkbit_funcs.has_pending_action(&action_id));

/* Test Case: Cancel Action available. */
will_return(__wrap_channel_get,
@@ -227,7 +226,8 @@ static void test_server_has_pending_action(void **state)
will_return(__wrap_channel_get, CHANNEL_OK);
will_return(__wrap_channel_put, CHANNEL_OK);
will_return(__wrap_save_state, 0);
- assert_int_equal(SERVER_OK, server_has_pending_action(&action_id));
+ assert_int_equal(SERVER_OK,
+ server_hawkbit_funcs.has_pending_action(&action_id));
}

extern server_op_res_t server_set_polling_interval_json(json_object *json_root);
@@ -374,7 +374,6 @@ static void test_server_process_update_artifact(void **state)
#endif
}

-extern server_op_res_t server_install_update(void);
static void test_server_install_update(void **state)
{
(void)state;
@@ -496,7 +495,7 @@ static void test_server_install_update(void **state)
will_return(__wrap_channel_get, CHANNEL_OK);
will_return(__wrap_channel_put, CHANNEL_OK);
will_return(__wrap_channel_put, CHANNEL_OK);
- server_install_update();
+ server_hawkbit_funcs.install_update();

/* Test Case: Update works. */
json_data_update_available =
@@ -523,7 +522,7 @@ static void test_server_install_update(void **state)
will_return(__wrap_channel_put, CHANNEL_OK);
//will_return(__wrap_save_state, SERVER_OK);
will_return(__wrap_channel_put, CHANNEL_OK);
- assert_int_equal(SERVER_OK, server_install_update());
+ assert_int_equal(SERVER_OK, server_hawkbit_funcs.install_update());
}

static int server_hawkbit_setup(void **state)
--
2.40.1

Christian Storm

hayajasomwa,
9 Mei 2023, 04:06:2309/05/2023
kwa swup...@googlegroups.com,Christian Storm
Adapt documentation to hint to suricatta module
run-time selection.

Signed-off-by: Christian Storm <christi...@siemens.com>
---
doc/source/suricatta.rst | 37 +++++++++++++++++++++++++------------
1 file changed, 25 insertions(+), 12 deletions(-)

diff --git a/doc/source/suricatta.rst b/doc/source/suricatta.rst
index 7d2aa10b..01102c3f 100644
--- a/doc/source/suricatta.rst
+++ b/doc/source/suricatta.rst
@@ -36,7 +36,7 @@ Running suricatta
-----------------

After having configured and compiled SWUpdate with enabled suricatta
-support,
+support for hawkBit,

.. code::

@@ -53,6 +53,11 @@ runs SWUpdate in suricatta daemon mode with log-level ``TRACE``, polling
a hawkBit instance at ``http://10.0.0.2:8080`` with tenant ``default``
and device ID ``25``.

+If multiple server support is compiled in, the ``-S`` / ``--server``
+option or a ``server`` entry in the configuration file's ``[suricatta]``
+section selects the one to use at run-time. For convenience, when having
+support for just one server compiled-in, this is chosen automatically.
+

Note that on startup when having installed an update, suricatta
tries to report the update status to its upstream server, e.g.,
@@ -112,13 +117,23 @@ to implement:

.. code:: c

- server_op_res_t server_has_pending_action(int *action_id);
- server_op_res_t server_install_update(void);
- server_op_res_t server_send_target_data(void);
- unsigned int server_get_polling_interval(void);
- server_op_res_t server_start(const char *cfgfname, int argc, char *argv[]);
- server_op_res_t server_stop(void);
- server_op_res_t server_ipc(int fd);
+ typedef struct {
+ server_op_res_t has_pending_action(int *action_id);
+ server_op_res_t install_update(void);
+ server_op_res_t send_target_data(void);
+ unsigned int get_polling_interval(void);
+ server_op_res_t start(const char *cfgfname, int argc, char *argv[]);
+ server_op_res_t stop(void);
+ server_op_res_t ipc(int fd);
+ void (*help)(void);
+ } server_t;
+
+These functions constituting a particular suricatta server implementation
+have to be registered for being selectable at run-time by calling
+``register_server()`` (see ``include/suricatta/server.h``) with
+a name and a ``server_t`` struct pointer implemented in a
+``__attribute__((constructor))`` marked function, see
+``suricatta/server_hawkbit.c`` as example.

The type ``server_op_res_t`` is defined in ``include/suricatta/suricatta.h``.
It represents the valid function return codes for a server's implementation.
@@ -133,10 +148,8 @@ one for hawkBit into the ``menu "Server"`` section is sufficient.

config SURICATTA_HAWKBIT
bool "hawkBit support"
- depends on HAVE_LIBCURL
- depends on HAVE_JSON_C
+ default y
select JSON
- select CURL
help
Support for hawkBit server.
https://projects.eclipse.org/projects/iot.hawkbit
@@ -150,7 +163,7 @@ if ``SURICATTA_HAWKBIT`` was selected while configuring SWUpdate.
.. code:: bash

ifneq ($(CONFIG_SURICATTA_HAWKBIT),)
- lib-$(CONFIG_SURICATTA) += server_hawkbit.o
+ obj-$(CONFIG_SURICATTA) += server_hawkbit.o
endif


--
2.40.1

Stefano Babic

hayajasomwa,
9 Mei 2023, 04:55:1309/05/2023
kwa Christian Storm,swup...@googlegroups.com
Hallo Christian,

On 09.05.23 10:07, 'Christian Storm' via swupdate wrote:
> Allow to compile-in multiple suricatta modules and choose one
> at run-time with sane fallback to a single compiled-in one.
> Defaults to hawkBit for backwards compatibility.
>

This was definetly on the TODO list - even if most projects just selects
one server, ther eare use case (not only with packaged SWUpdate) where
this is useful - thanks !
Ok - for me this cange is straightforward, it reuses the same method
(constructor) to build up the internal list (like handlers are doing),
so I do not see issues. I am expecting that some users will get an issue
if they do not adjust their swupdate.cfg or command line in the startup
scripts, I will note to write this in the changelog for the next release.

Best regards,
Stefano
=====================================================================
DENX Software Engineering GmbH, Managing Director: Erika Unter
HRB 165235 Munich, Office: Kirchenstr.5, 82194 Groebenzell, Germany
Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sba...@denx.de
=====================================================================

Christian Storm

hayajasomwa,
9 Mei 2023, 07:39:5709/05/2023
kwa swup...@googlegroups.com
Hi Stefano,


> > Allow to compile-in multiple suricatta modules and choose one
> > at run-time with sane fallback to a single compiled-in one.
> > Defaults to hawkBit for backwards compatibility.
> >
>
> This was definetly on the TODO list - even if most projects just selects one
> server, ther eare use case (not only with packaged SWUpdate) where this is
> useful - thanks !

You're welcome!

> > [...]
I tried to be as conservative as possible while I thought about how to
implement this...

So, existing kconfig'urations have none or only one suricatta module
enabled and this patch falls back to use that single built-in one if no
option (configuration file or cmdline) is given ― which is the case for
existing deployments, so they don't break.

If you start a new kconfig'uration from scratch, hawkBit is selected as
default, assuming that's the most widely used suricatta module.

If you chose to build-in multiple suricatta modules, you have to select
one at run-time, else SWUpdate bails out with an according error.
This is new behavior but I guess OK so: I thought about also falling
back to hawkBit then but this may cause confusion as well.
Hence, distributions need to ship a modified/default configuration file
or a default service supervisor configuration that pre-selects one.
This case is indeed a good thing for a changelog and/or a heads-up for
distribution packagers...



Kind regards,
Christian

--
Dr. Christian Storm
Siemens AG, Technology, T CED SES-DE
Otto-Hahn-Ring 6, 81739 München, Germany

Stefano Babic

hayajasomwa,
9 Mei 2023, 08:25:2809/05/2023
kwa swup...@googlegroups.com
Hi Christian,
Yes, I find good.

>
> If you start a new kconfig'uration from scratch, hawkBit is selected as
> default, assuming that's the most widely used suricatta module.

It is ok, too.

>
> If you chose to build-in multiple suricatta modules, you have to select
> one at run-time, else SWUpdate bails out with an according error.
> This is new behavior but I guess OK so: I thought about also falling
> back to hawkBit then but this may cause confusion as well.

Yes, I think it is ok and it is better als falling back.

> Hence, distributions need to ship a modified/default configuration file
> or a default service supervisor configuration that pre-selects one.
> This case is indeed a good thing for a changelog and/or a heads-up for
> distribution packagers...
>

Best regards,
Stefano

>
>
> Kind regards,
> Christian

Christian Storm

hayajasomwa,
17 Mei 2023, 03:43:2117/05/2023
kwa swup...@googlegroups.com,Christian Storm
Allow to compile-in multiple suricatta modules and choose one
at run-time with sane fallback to a single compiled-in one.
Defaults to hawkBit for backwards compatibility.

This feature enables distributions to ship SWUpdate with
multiple suricatta modules enabled, shifting the selection
to run-time rather than compile-time.

Signed-off-by: Christian Storm <christi...@siemens.com>
---
Changes in V2:
* Added "See swupdate --help for options." to fatal exit in case
multiple suricatta modules are available but none selected.
* Fixed Coverity false positive CID 453970 in V1's suricatta.c:328

Makefile | 4 +-
include/suricatta/server.h | 24 ++-------
suricatta/Config.in | 30 ++++-------
suricatta/Makefile | 11 ++--
suricatta/suricatta.c | 100 +++++++++++++++++++++++++++++++++----
5 files changed, 115 insertions(+), 54 deletions(-)
index b10da933..32a044ee 100644
--- a/suricatta/suricatta.c
+++ b/suricatta/suricatta.c
@@ -30,9 +30,50 @@ static bool trigger = false;
static struct option long_options[] = {
{"enable", no_argument, NULL, 'e'},
{"disable", no_argument, NULL, 'd'},
+ {"server", required_argument, NULL, 'S'},
{NULL, 0, NULL, 0}};
static sem_t suricatta_enable_sema;

+typedef struct {
+ const char *name;
+ server_t *funcs;
+} server_entry;
+
+static int servers_count = 0;
+static server_entry *servers = NULL;
+static server_t *server = NULL;
+
+bool register_server(const char *name, server_t *srv)
+{
+ if (!name || !srv) {
+ return false;
+ }
+ server_entry *tmp = realloc(servers,
+ (servers_count + 1) * sizeof(server_entry));
+ if (!tmp) {
+ return false;
+ }
+ tmp[servers_count].name = name;
+ tmp[servers_count].funcs = srv;
+ servers_count++;
+ servers = tmp;
+ return true;
+}
+
+static bool set_server(const char *name)
+{
+ if (!name || !strlen(name)) {
+ return false;
+ }
+ for (unsigned int i = 0; i < servers_count; i++) {
+ if (strcmp(name, servers[i].name) == 0) {
+ server = servers[i].funcs;
+ return true;
+ }
+ }
+ return false;
+}
+
void suricatta_print_help(void)
{
fprintf(
@@ -40,8 +81,17 @@ void suricatta_print_help(void)
"\tsuricatta arguments (mandatory arguments are marked with '*'):\n"
"\t -e, --enable Daemon enabled at startup (default).\n"
"\t -d, --disable Daemon disabled at startup.\n"
+ "\t -S, --server Suricatta module to run.\n"
);
- server.help();
+ if (servers_count == 0) {
+ fprintf(stdout, "\tNo compiled-in suricatta modules!\n");
+ return;
+ }
+ for (unsigned int i = 0; i < servers_count; i++) {
+ fprintf(stdout, "\tOptions for suricatta module '%s':\n",
+ servers[i].name);
+ (servers[i].funcs)->help();
+ }
}

static server_op_res_t suricatta_enable(ipc_message *msg)
@@ -103,7 +153,7 @@ static server_op_res_t suricatta_ipc(int fd)
result = suricatta_enable(&msg);
break;
default:
- result = server.ipc(&msg);
+ result = server->ipc(&msg);
break;
}

@@ -120,6 +170,12 @@ static int suricatta_settings(void *elem, void __attribute__ ((__unused__)) *da
get_field(LIBCFG_PARSER, elem, "enable",
&enable);

+ char cfg_server[128];
+ GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "server", cfg_server);
+ if (strlen(cfg_server) && set_server(cfg_server)) {
+ TRACE("Suricatta module '%s' selected by configuration file.", cfg_server);
+ }
+
return 0;
}

@@ -227,9 +283,16 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
optind = 1;
opterr = 0;

- while ((choice = getopt_long(argc, argv, "de",
+ while ((choice = getopt_long(argc, argv, "deS:",
long_options, NULL)) != -1) {
switch (choice) {
+ case 'S':
+ if (!set_server(optarg)) {
+ ERROR("Suricatta module '%s' not registered.", optarg);
+ exit(EXIT_FAILURE);
+ }
+ TRACE("Suricatta module '%s' selected by command line option.", optarg);
+ break;
case 'e':
enable = true;
break;
@@ -240,6 +303,25 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
break;
}
}
+ if (!server) {
+ if (servers_count == 0) {
+ ERROR("No compiled-in suricatta modules!");
+ exit(EXIT_FAILURE);
+ }
+ if (servers_count == 1) {
+ if (!set_server(servers[0].name)) {
+ ERROR("Internal Error: One suricatta module "
+ "available but not found?!");
+ exit(EXIT_FAILURE);
+ }
+ TRACE("Default suricatta module '%s' selected.", servers[0].name);
+
+ } else {
+ ERROR("Multiple suricatta modules available but none "
+ "selected. See swupdate --help for options.");
+ exit(EXIT_FAILURE);
+ }
+ }

if (sem_init(&suricatta_enable_sema, 0, 0)) {
ERROR("Initialising suricatta enable semaphore failed");
@@ -254,7 +336,7 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
/*
* Now start a specific implementation of the server
*/
- if (server.start(cfgfname, argc, serverargv) != SERVER_OK) {
+ if (server->start(cfgfname, argc, serverargv) != SERVER_OK) {
exit(EXIT_FAILURE);
}
free(serverargv);
@@ -263,13 +345,13 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
while (true) {
if (enable || trigger) {
trigger = false;
- switch (server.has_pending_action(&action_id)) {
+ switch (server->has_pending_action(&action_id)) {
case SERVER_UPDATE_AVAILABLE:
DEBUG("About to process available update.");
- server.install_update();
+ server->install_update();
break;
case SERVER_ID_REQUESTED:
- server.send_target_data();
+ server->send_target_data();
trigger = true;
break;
case SERVER_EINIT:
@@ -281,9 +363,9 @@ int start_suricatta(const char *cfgfname, int argc, char *argv[])
Jibu wote
Mjibu mchapishaji
Sambaza
Ujumbe 0 mpya