diff mbox series

[V2] suricatta: Suricatta module run-time selection

Message ID 20230517074326.10649-1-christian.storm@siemens.com
State Accepted
Headers show
Series [V2] suricatta: Suricatta module run-time selection | expand

Commit Message

Storm, Christian May 17, 2023, 7:43 a.m. UTC
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 <christian.storm@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(-)
diff mbox series

Patch

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 <sbabic@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..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[])
 			}
 		}
 
-		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);
 		}