diff mbox series

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

Message ID 20230509080726.23738-1-christian.storm@siemens.com
State Changes Requested
Headers show
Series [1/3] suricatta: Suricatta module run-time selection | expand

Commit Message

Storm, Christian May 9, 2023, 8:07 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>
---
 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(-)

Comments

Stefano Babic May 9, 2023, 8:55 a.m. UTC | #1
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 !

> 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>
> ---
>   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 <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..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;
> +}

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

> +
> +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);
>   		}
>
Storm, Christian May 9, 2023, 11:41 a.m. UTC | #2
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!

> > [...]
> > 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;
> > +}
> 
> 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.

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
Stefano Babic May 9, 2023, 12:25 p.m. UTC | #3
Hi Christian,

On 09.05.23 13:41, 'Christian Storm' via swupdate wrote:
> 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!
> 
>>> [...]
>>> 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;
>>> +}
>>
>> 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.
> 
> 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.

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
>
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..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);
 		}