commit 90f5ad21f12c9056d23cbf554f91fc0f7d21ba52
parent d6461e9ba0dd97d59e51d06358319d496429ffde
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 15 Jun 2025 20:01:27 +0200
fix #10024: allow inheriting multiple sockets from systemd
Diffstat:
10 files changed, 756 insertions(+), 442 deletions(-)
diff --git a/src/auditor/taler-auditor-httpd.c b/src/auditor/taler-auditor-httpd.c
@@ -139,9 +139,9 @@ static int global_ret;
static int disable_auth;
/**
- * Port to run the daemon on.
+ * True if we started any HTTP daemon.
*/
-static uint16_t serve_port;
+static bool have_daemons;
/**
* Our currency.
@@ -1248,13 +1248,10 @@ auditor_serve_process_config (void)
static void
do_shutdown (void *cls)
{
- struct MHD_Daemon *mhd;
(void) cls;
-
- mhd = TALER_MHD_daemon_stop ();
+ TALER_MHD_daemons_halt ();
TEAH_DEPOSIT_CONFIRMATION_done ();
- if (NULL != mhd)
- MHD_stop_daemon (mhd);
+ TALER_MHD_daemons_destroy ();
if (NULL != TAH_plugin)
{
TALER_AUDITORDB_plugin_unload (TAH_plugin);
@@ -1269,6 +1266,51 @@ do_shutdown (void *cls)
/**
+ * Callback invoked on every listen socket to start the
+ * respective MHD HTTP daemon.
+ *
+ * @param cls unused
+ * @param lsock the listen socket
+ */
+static void
+start_daemon (void *cls,
+ int lsock)
+{
+ struct MHD_Daemon *mhd;
+
+ (void) cls;
+ GNUNET_assert (-1 != lsock);
+ mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
+ | MHD_USE_PIPE_FOR_SHUTDOWN
+ | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
+ | MHD_USE_TCP_FASTOPEN,
+ 0,
+ NULL, NULL,
+ &handle_mhd_request, NULL,
+ MHD_OPTION_LISTEN_SOCKET,
+ lsock,
+ MHD_OPTION_EXTERNAL_LOGGER,
+ &TALER_MHD_handle_logs,
+ NULL,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback,
+ NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT,
+ connection_timeout,
+ MHD_OPTION_END);
+ if (NULL == mhd)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to launch HTTP daemon.\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ have_daemons = true;
+ TALER_MHD_daemon_start (mhd);
+}
+
+
+/**
* Main function that will be run by the scheduler.
*
* @param cls closure
@@ -1284,7 +1326,7 @@ run (void *cls,
const struct GNUNET_CONFIGURATION_Handle *config)
{
enum TALER_MHD_GlobalOptions go;
- int fh;
+ enum GNUNET_GenericReturnValue ret;
(void) cls;
(void) args;
@@ -1337,48 +1379,30 @@ run (void *cls,
return;
}
TEAH_DEPOSIT_CONFIRMATION_init ();
- fh = TALER_MHD_bind (cfg,
- "auditor",
- &serve_port);
- if ( (0 == serve_port) &&
- (-1 == fh) )
+ ret = TALER_MHD_listen_bind (cfg,
+ "auditor",
+ &start_daemon,
+ NULL);
+ switch (ret)
{
+ case GNUNET_SYSERR:
+ global_ret = EXIT_NOTCONFIGURED;
GNUNET_SCHEDULER_shutdown ();
return;
- }
- {
- struct MHD_Daemon *mhd;
-
- mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
- | MHD_USE_PIPE_FOR_SHUTDOWN
- | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
- | MHD_USE_TCP_FASTOPEN,
- (-1 == fh) ? serve_port : 0,
- NULL, NULL,
- &handle_mhd_request, NULL,
- MHD_OPTION_LISTEN_BACKLOG_SIZE,
- (unsigned int) 1024,
- MHD_OPTION_LISTEN_SOCKET,
- fh,
- MHD_OPTION_EXTERNAL_LOGGER,
- &TALER_MHD_handle_logs,
- NULL,
- MHD_OPTION_NOTIFY_COMPLETED,
- &handle_mhd_completion_callback,
- NULL,
- MHD_OPTION_CONNECTION_TIMEOUT,
- connection_timeout,
- MHD_OPTION_END);
- if (NULL == mhd)
+ case GNUNET_NO:
+ if (! have_daemons)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to launch HTTP service. Is the port in use?\n");
+ global_ret = EXIT_NOTCONFIGURED;
GNUNET_SCHEDULER_shutdown ();
return;
}
- global_ret = EXIT_SUCCESS;
- TALER_MHD_daemon_start (mhd);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Could not open all configured listen sockets\n");
+ break;
+ case GNUNET_OK:
+ break;
}
+ global_ret = EXIT_SUCCESS;
}
diff --git a/src/auditordb/auditordb_plugin.c b/src/auditordb/auditordb_plugin.c
@@ -52,7 +52,9 @@ TALER_AUDITORDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
(void *) cfg);
if (NULL == plugin)
{
- abort ();
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to load `%s'\n",
+ lib_name);
GNUNET_free (lib_name);
return NULL;
}
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
@@ -127,9 +127,9 @@ bool TEH_age_restriction_enabled = false;
struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0};
/**
- * Handle to the HTTP server.
+ * Set to true if we started *any* HTTP daemons.
*/
-static struct MHD_Daemon *mhd;
+static bool have_daemons;
/**
* How long is caching /keys allowed at most? (global)
@@ -268,11 +268,6 @@ bool TEH_extensions_signed = false;
static int global_ret;
/**
- * Port to run the daemon on.
- */
-static uint16_t serve_port;
-
-/**
* Counter for the number of requests this HTTP has processed so far.
*/
static unsigned long long req_count;
@@ -969,7 +964,6 @@ handle_post_purses (struct TEH_RequestContext *rc,
static void
check_suicide (void)
{
- int fd;
pid_t chld;
unsigned long long cnt;
@@ -980,8 +974,7 @@ check_suicide (void)
"Restarting exchange service after %llu requests\n",
cnt);
/* Stop accepting new connections */
- fd = MHD_quiesce_daemon (mhd);
- GNUNET_break (0 == close (fd));
+ TALER_MHD_daemons_quiesce ();
/* Continue handling existing connections in child,
so that this process can die and be replaced by
systemd with a fresh one */
@@ -2486,11 +2479,24 @@ static char *input_filename;
static pid_t
run_fake_client (void)
{
+ unsigned long long serve_port;
pid_t cld;
char ports[6];
int fd;
bool use_stdin;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (TEH_cfg,
+ "exchange",
+ "PORT",
+ &serve_port))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "PORT",
+ "port number required (for nc)");
+ return -1;
+ }
/* Duping to STDIN and fork() mess up gcc's analysis
badly, disable diagnostics. */
#pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
@@ -2517,7 +2523,7 @@ run_fake_client (void)
GNUNET_snprintf (ports,
sizeof (ports),
"%u",
- serve_port);
+ (unsigned int) serve_port);
if (0 == (cld = fork ()))
{
if (! use_stdin)
@@ -2655,12 +2661,10 @@ connection_done (void *cls,
static void
do_shutdown (void *cls)
{
- struct MHD_Daemon *my_mhd;
(void) cls;
-
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Shutdown of taler-exchange-httpd\n");
- my_mhd = TALER_MHD_daemon_stop ();
+ TALER_MHD_daemons_halt ();
TEH_resume_keys_requests (true);
TEH_batch_deposit_cleanup ();
TEH_withdraw_cleanup ();
@@ -2679,11 +2683,7 @@ do_shutdown (void *cls)
TEH_kyc_start_cleanup ();
TEH_aml_decision_cleanup ();
TALER_KYCLOGIC_kyc_done ();
- if (NULL != my_mhd)
- {
- MHD_stop_daemon (my_mhd);
- my_mhd = NULL;
- }
+ TALER_MHD_daemons_destroy ();
TEH_wire_done ();
TEH_extensions_done ();
TEH_keys_finished ();
@@ -2712,6 +2712,58 @@ do_shutdown (void *cls)
/**
+ * Callback invoked on every listen socket to start the
+ * respective MHD HTTP daemon.
+ *
+ * @param cls unused
+ * @param lsock the listen socket
+ */
+static void
+start_daemon (void *cls,
+ int lsock)
+{
+ struct MHD_Daemon *mhd;
+
+ (void) cls;
+ GNUNET_assert (-1 != lsock);
+ mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
+ | MHD_USE_PIPE_FOR_SHUTDOWN
+ | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
+ | MHD_USE_TCP_FASTOPEN,
+ 0, /* already bound */
+ NULL, NULL,
+ &handle_mhd_request, NULL,
+ MHD_OPTION_LISTEN_SOCKET,
+ lsock,
+ MHD_OPTION_EXTERNAL_LOGGER,
+ &TALER_MHD_handle_logs,
+ NULL,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback,
+ NULL,
+ MHD_OPTION_NOTIFY_CONNECTION,
+ &connection_done,
+ NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT,
+ connection_timeout,
+ (0 == allow_address_reuse)
+ ? MHD_OPTION_END
+ : MHD_OPTION_LISTENING_ADDRESS_REUSE,
+ (unsigned int) allow_address_reuse,
+ MHD_OPTION_END);
+ if (NULL == mhd)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to launch HTTP service!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ have_daemons = true;
+ TALER_MHD_daemon_start (mhd);
+}
+
+
+/**
* Main function that will be run by the scheduler.
*
* @param cls closure
@@ -2727,7 +2779,7 @@ run (void *cls,
const struct GNUNET_CONFIGURATION_Handle *config)
{
enum TALER_MHD_GlobalOptions go;
- int fh;
+ enum GNUNET_GenericReturnValue ret;
(void) cls;
(void) args;
@@ -2805,51 +2857,30 @@ run (void *cls,
exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEH_curl_ctx);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
- fh = TALER_MHD_bind (TEH_cfg,
- "exchange",
- &serve_port);
- if ( (0 == serve_port) &&
- (-1 == fh) )
- {
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
- mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
- | MHD_USE_PIPE_FOR_SHUTDOWN
- | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
- | MHD_USE_TCP_FASTOPEN,
- (-1 == fh) ? serve_port : 0,
- NULL, NULL,
- &handle_mhd_request, NULL,
- MHD_OPTION_LISTEN_BACKLOG_SIZE,
- (unsigned int) 1024,
- MHD_OPTION_LISTEN_SOCKET,
- fh,
- MHD_OPTION_EXTERNAL_LOGGER,
- &TALER_MHD_handle_logs,
- NULL,
- MHD_OPTION_NOTIFY_COMPLETED,
- &handle_mhd_completion_callback,
- NULL,
- MHD_OPTION_NOTIFY_CONNECTION,
- &connection_done,
- NULL,
- MHD_OPTION_CONNECTION_TIMEOUT,
- connection_timeout,
- (0 == allow_address_reuse)
- ? MHD_OPTION_END
- : MHD_OPTION_LISTENING_ADDRESS_REUSE,
- (unsigned int) allow_address_reuse,
- MHD_OPTION_END);
- if (NULL == mhd)
+ ret = TALER_MHD_listen_bind (TEH_cfg,
+ "exchange",
+ &start_daemon,
+ NULL);
+ switch (ret)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to launch HTTP service. Is the port in use?\n");
+ case GNUNET_SYSERR:
+ global_ret = EXIT_NOTCONFIGURED;
GNUNET_SCHEDULER_shutdown ();
return;
+ case GNUNET_NO:
+ if (! have_daemons)
+ {
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Could not open all configured listen sockets\n");
+ break;
+ case GNUNET_OK:
+ break;
}
global_ret = EXIT_SUCCESS;
- TALER_MHD_daemon_start (mhd);
atexit (&write_stats);
#if HAVE_DEVELOPER
if (NULL != input_filename)
diff --git a/src/include/taler_mhd2_lib.h b/src/include/taler_mhd2_lib.h
@@ -876,25 +876,6 @@ TALER_MHD2_check_content_length_ (struct MHD_Request *request,
/**
- * Parse the configuration to determine on which port
- * or UNIX domain path we should run an HTTP service.
- *
- * @param cfg configuration to parse
- * @param section section of the configuration to parse (usually "exchange")
- * @param[out] rport set to the port number, or 0 for none
- * @param[out] unix_path set to the UNIX path, or NULL for none
- * @param[out] unix_mode set to the mode to be used for @a unix_path
- * @return #GNUNET_OK on success
- */
-enum GNUNET_GenericReturnValue
-TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *section,
- uint16_t *rport,
- char **unix_path,
- mode_t *unix_mode);
-
-
-/**
* Function called for logging by MHD.
*
* @param cls closure, NULL
@@ -908,34 +889,39 @@ TALER_MHD_handle_logs (void *cls,
/**
- * Open UNIX domain socket for listining at @a unix_path with
- * permissions @a unix_mode.
+ * Function called on each successfully bound listen
+ * socket by #TALER_MHD_listen_bind().
*
- * @param unix_path where to listen
- * @param unix_mode access permissions to set
- * @return -1 on error, otherwise the listen socket
+ * @param cls closure
+ * @param fd bound listen socket (must be used and eventually
+ * closed by the callee). Never -1.
*/
-int
-TALER_MHD_open_unix_path (const char *unix_path,
- mode_t unix_mode);
+typedef void
+(*TALER_MHD_ListenSocketCallback)(void *cls,
+ int fd);
/**
- * Bind a listen socket to the UNIX domain path or the TCP port and IP address
- * as specified in @a cfg in section @a section. IF only a port was
- * specified, set @a port and return -1. Otherwise, return the bound file
- * descriptor.
+ * Bind a listen socket to the UNIX domain path,
+ * or the TCP port(s) and IP address(es) configured,
+ * or return to @a cb the inherited sockets from systemd,
+ * all depending on what was specified in @a cfg in
+ * the section named @a section.
*
* @param cfg configuration to parse
* @param section configuration section to use
- * @param[out] port port to set, if TCP without BINDTO
- * @return -1 and a port of zero on error, otherwise
- * either -1 and a port, or a bound stream socket
+ * @param cb function to call with each bound socket
+ * @param cb_cls closure for @a cb
+ * @return #GNUNET_OK on success (all configured sockets were bound)
+ * #GNUNET_NO if some configured binding failed but the config is OK,
+ * note that some listen sockets may have been created
+ * #GNUNET_SYSERR if the configuration is invalid
*/
-int
-TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *section,
- uint16_t *port);
+enum GNUNET_GenericReturnValue
+TALER_MHD_listen_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section,
+ TALER_MHD_ListenSocketCallback cb,
+ void *cb_cls);
/**
@@ -950,21 +936,30 @@ TALER_MHD2_daemon_start (struct MHD_Daemon *daemon);
/**
- * Stop running the event loop for MHD.
- *
- * @return the daemon that we were previously running,
- * or NULL if none was active
+ * Stop running the event loops of all MHD daemons.
*/
-struct MHD_Daemon *
-TALER_MHD2_daemon_stop (void);
+void
+TALER_MHD2_daemons_halt (void);
+
+/**
+ * Stop accepting new connections on all MHD daemons
+ * (and close the listen sockets).
+ */
+void
+TALER_MHD2_daemons_quiesce (void);
+
+/**
+ * Wake up all daemons.
+ */
+void
+TALER_MHD2_daemons_trigger (void);
/**
- * Trigger MHD daemon that is running. Needed when
- * a request was resumed.
+ * Destroy all state associated with all MHD daemons.
*/
void
-TALER_MHD2_daemon_trigger (void);
+TALER_MHD2_daemons_destroy (void);
/**
diff --git a/src/include/taler_mhd_lib.h b/src/include/taler_mhd_lib.h
@@ -889,21 +889,39 @@ TALER_MHD_handle_logs (void *cls,
/**
- * Bind a listen socket to the UNIX domain path or the TCP port and IP address
- * as specified in @a cfg in section @a section. IF only a port was
- * specified, set @a port and return -1. Otherwise, return the bound file
- * descriptor.
+ * Function called on each successfully bound listen
+ * socket by #TALER_MHD_listen_bind().
+ *
+ * @param cls closure
+ * @param fd bound listen socket (must be used and eventually
+ * closed by the callee). Never -1.
+ */
+typedef void
+(*TALER_MHD_ListenSocketCallback)(void *cls,
+ int fd);
+
+
+/**
+ * Bind a listen socket to the UNIX domain path,
+ * or the TCP port(s) and IP address(es) configured,
+ * or return to @a cb the inherited sockets from systemd,
+ * all depending on what was specified in @a cfg in
+ * the section named @a section.
*
* @param cfg configuration to parse
* @param section configuration section to use
- * @param[out] port port to set, if TCP without BINDTO
- * @return -1 and a port of zero on error, otherwise
- * either -1 and a port, or a bound stream socket
+ * @param cb function to call with each bound socket
+ * @param cb_cls closure for @a cb
+ * @return #GNUNET_OK on success (all configured sockets were bound)
+ * #GNUNET_NO if some configured binding failed but the config is OK,
+ * note that some listen sockets may have been created
+ * #GNUNET_SYSERR if the configuration is invalid
*/
-int
-TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *section,
- uint16_t *port);
+enum GNUNET_GenericReturnValue
+TALER_MHD_listen_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section,
+ TALER_MHD_ListenSocketCallback cb,
+ void *cb_cls);
/**
@@ -918,17 +936,28 @@ TALER_MHD_daemon_start (struct MHD_Daemon *daemon);
/**
- * Stop running the event loop for MHD.
- *
- * @return the daemon that we were previously running,
- * or NULL if none was active
+ * Stop running the event loops of all MHD daemons.
*/
-struct MHD_Daemon *
-TALER_MHD_daemon_stop (void);
+void
+TALER_MHD_daemons_halt (void);
+
+/**
+ * Stop accepting new connections on all MHD daemons
+ * (and close the listen sockets).
+ */
+void
+TALER_MHD_daemons_quiesce (void);
+
+
+/**
+ * Destroy all state associated with all MHD daemons.
+ */
+void
+TALER_MHD_daemons_destroy (void);
/**
- * Trigger MHD daemon that is running. Needed when
+ * Trigger all MHD daemons that were running. Needed when
* a connection was resumed.
*/
void
diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c
@@ -228,11 +228,6 @@ static struct ProofRequestState *rs_tail;
static const struct GNUNET_CONFIGURATION_Handle *TEKT_cfg;
/**
- * Handle to the HTTP server.
- */
-static struct MHD_Daemon *mhd;
-
-/**
* Our base URL.
*/
static char *TEKT_base_url;
@@ -314,9 +309,9 @@ static struct TALER_KYCLOGIC_InitiateHandle *ih;
static struct TALER_KYCLOGIC_Plugin *ih_logic;
/**
- * Port to run the daemon on.
+ * True if we started any daemon.
*/
-static uint16_t serve_port;
+static bool have_daemons; uint16_t serve_port;
/**
* Context for all CURL operations (useful to the event loop)
@@ -1340,7 +1335,6 @@ exchange_serve_process_config (void)
static void
do_shutdown (void *cls)
{
- struct MHD_Daemon *my_mhd;
struct ProofRequestState *rs;
(void) cls;
@@ -1360,9 +1354,8 @@ do_shutdown (void *cls)
}
kyc_webhook_cleanup ();
TALER_KYCLOGIC_kyc_done ();
- my_mhd = TALER_MHD_daemon_stop ();
- if (NULL != my_mhd)
- MHD_stop_daemon (my_mhd);
+ TALER_MHD_daemons_halt ();
+ TALER_MHD_daemons_destroy ();
if (NULL != TEKT_curl_ctx)
{
GNUNET_CURL_fini (TEKT_curl_ctx);
@@ -1485,6 +1478,50 @@ amount_iterator (
/**
+ * Callback invoked on every listen socket to start the
+ * respective MHD HTTP daemon.
+ *
+ * @param cls unused
+ * @param lsock the listen socket
+ */
+static void
+start_daemon (void *cls,
+ int lsock)
+{
+ struct MHD_Daemon *mhd;
+
+ (void) cls;
+ GNUNET_assert (-1 != lsock);
+ mhd = MHD_start_daemon (
+ MHD_USE_SUSPEND_RESUME
+ | MHD_USE_PIPE_FOR_SHUTDOWN
+ | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
+ | MHD_USE_TCP_FASTOPEN,
+ 0,
+ NULL, NULL,
+ &handle_mhd_request, NULL,
+ MHD_OPTION_LISTEN_SOCKET,
+ lsock,
+ MHD_OPTION_EXTERNAL_LOGGER,
+ &TALER_MHD_handle_logs,
+ NULL,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback,
+ NULL,
+ MHD_OPTION_END);
+ if (NULL == mhd)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to launch HTTP service. Is the port in use?\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ have_daemons = true;
+ TALER_MHD_daemon_start (mhd);
+}
+
+
+/**
* Main function that will be run by the scheduler.
*
* @param cls closure
@@ -1502,7 +1539,6 @@ run (void *cls,
enum TALER_KYCLOGIC_KycTriggerEvent event;
struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL;
const struct TALER_KYCLOGIC_KycRule *rule = NULL;
- int fh;
(void) cls;
(void) args;
@@ -1711,6 +1747,8 @@ run (void *cls,
}
if (run_webservice)
{
+ enum GNUNET_GenericReturnValue ret;
+
TEKT_curl_ctx
= GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&exchange_curl_rc);
@@ -1722,43 +1760,29 @@ run (void *cls,
return;
}
exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEKT_curl_ctx);
- fh = TALER_MHD_bind (TEKT_cfg,
- "exchange",
- &serve_port);
- if ( (0 == serve_port) &&
- (-1 == fh) )
- {
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Starting daemon on port %u\n",
- (unsigned int) serve_port);
- mhd = MHD_start_daemon (
- MHD_USE_SUSPEND_RESUME
- | MHD_USE_PIPE_FOR_SHUTDOWN
- | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
- | MHD_USE_TCP_FASTOPEN,
- (-1 == fh) ? serve_port : 0,
- NULL, NULL,
- &handle_mhd_request, NULL,
- MHD_OPTION_LISTEN_SOCKET,
- fh,
- MHD_OPTION_EXTERNAL_LOGGER,
- &TALER_MHD_handle_logs,
- NULL,
- MHD_OPTION_NOTIFY_COMPLETED,
- &handle_mhd_completion_callback,
- NULL,
- MHD_OPTION_END);
- if (NULL == mhd)
+ ret = TALER_MHD_listen_bind (TEKT_cfg,
+ "exchange",
+ &start_daemon,
+ NULL);
+ switch (ret)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to launch HTTP service. Is the port in use?\n");
+ case GNUNET_SYSERR:
+ global_ret = EXIT_NOTCONFIGURED;
GNUNET_SCHEDULER_shutdown ();
return;
+ case GNUNET_NO:
+ if (! have_daemons)
+ {
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Could not open all configured listen sockets\n");
+ break;
+ case GNUNET_OK:
+ break;
}
- TALER_MHD_daemon_start (mhd);
}
}
diff --git a/src/mhd/mhd2_run.c b/src/mhd/mhd2_run.c
@@ -51,73 +51,112 @@ struct SocketContext
* Socket to watch for.
*/
struct GNUNET_NETWORK_Handle *fd;
+
+ /**
+ * Daemon this socket is about.
+ */
+ struct DaemonEntry *de;
};
/**
- * Set to true if we should immediately MHD_run() again.
+ * Entry in list of HTTP servers we are running.
*/
-static bool triggered;
+struct DaemonEntry
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct DaemonEntry *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct DaemonEntry *prev;
+
+ /**
+ * The actual daemon.
+ */
+ struct MHD_Daemon *mhd;
+
+ /**
+ * Task running the HTTP server.
+ */
+ struct GNUNET_SCHEDULER_Task *mhd_task;
+
+ /**
+ * Task waiting for timeout on the HTTP server.
+ */
+ struct GNUNET_SCHEDULER_Task *timeout_task;
+
+ /**
+ * Set to true if we should immediately MHD_run() again.
+ */
+ bool triggered;
+
+};
+
/**
- * Task running the HTTP server.
+ * Head of list of HTTP servers.
*/
-static struct GNUNET_SCHEDULER_Task *mhd_task;
+static struct DaemonEntry *mhd_head;
/**
- * The MHD daemon we are running.
+ * Tail of list of HTTP servers.
*/
-static struct MHD_Daemon *mhd;
+static struct DaemonEntry *mhd_tail;
/**
* Function that queries MHD's select sets and
* starts the task waiting for them.
+ *
+ * @param[in,out] de daemon to start tasks for
*/
-static struct GNUNET_SCHEDULER_Task *
-prepare_daemon (void);
+static void
+prepare_daemon (struct DaemonEntry *de);
/**
* Trigger MHD on timeout.
*
- * @param cls not used
+ * @param cls a `struct DaemonEntry`
*/
static void
handle_timeout (void *cls)
{
- (void) cls;
- mhd_task = prepare_daemon ();
+ struct DaemonEntry *de = cls;
+
+ de->timeout_task = NULL;
+ prepare_daemon (de);
}
-/**
- * Function that queries MHD's select sets and
- * starts the task waiting for them.
- */
-static struct GNUNET_SCHEDULER_Task *
-prepare_daemon (void)
+static void
+prepare_daemon (struct DaemonEntry *de)
{
uint_fast64_t next_max_wait;
enum MHD_StatusCode sc;
- sc = MHD_daemon_process_reg_events (mhd,
+ sc = MHD_daemon_process_reg_events (de->mhd,
&next_max_wait);
if (MHD_SC_OK != sc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"MHD_daemon_process_reg_events failed: %d\n",
(int) sc);
- return NULL;
+ return;
}
if (MHD_WAIT_INDEFINITELY == next_max_wait)
- return NULL;
- return GNUNET_SCHEDULER_add_delayed (
- GNUNET_TIME_relative_multiply (
- GNUNET_TIME_UNIT_MICROSECONDS,
- next_max_wait),
- &handle_timeout,
- NULL);
+ return;
+ de->timeout_task
+ = GNUNET_SCHEDULER_add_delayed (
+ GNUNET_TIME_relative_multiply (
+ GNUNET_TIME_UNIT_MICROSECONDS,
+ next_max_wait),
+ &handle_timeout,
+ de);
}
@@ -125,13 +164,15 @@ prepare_daemon (void)
* Call MHD to process pending requests and then go back
* and schedule the next run.
*
- * @param cls NULL
+ * @param cls our `struct DaemonEntry *`
*/
static void
run_daemon (void *cls)
{
- (void) cls;
- mhd_task = prepare_daemon ();
+ struct DaemonEntry *de = cls;
+
+ de->mhd_task = NULL;
+ prepare_daemon (de);
}
@@ -144,15 +185,16 @@ static void
mhd_rready (void *cls)
{
struct SocketContext *sc = cls;
+ struct DaemonEntry *de = sc->de;
sc->mhd_rtask = NULL;
- MHD_daemon_event_update (mhd,
+ MHD_daemon_event_update (de->mhd,
sc->ecb_cntx,
MHD_FD_STATE_RECV);
- if (NULL != mhd_task)
- GNUNET_SCHEDULER_cancel (mhd_task);
- mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
- NULL);
+ if (NULL != de->mhd_task)
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
+ de);
}
@@ -165,22 +207,23 @@ static void
mhd_wready (void *cls)
{
struct SocketContext *sc = cls;
+ struct DaemonEntry *de = sc->de;
sc->mhd_wtask = NULL;
- MHD_daemon_event_update (mhd,
+ MHD_daemon_event_update (de->mhd,
sc->ecb_cntx,
MHD_FD_STATE_SEND);
- if (NULL != mhd_task)
- GNUNET_SCHEDULER_cancel (mhd_task);
- mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
- NULL);
+ if (NULL != de->mhd_task)
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
+ de);
}
/**
* Callback for registration/de-registration of the sockets to watch.
*
- * @param cls the closure
+ * @param cls our `struct DaemonEntry`
* @param fd the socket to watch
* @param watch_for the states of the @a fd to watch, if set to
* #MHD_FD_STATE_NONE the socket must be de-registred
@@ -199,12 +242,14 @@ socket_registration_update (
MHD_APP_SOCKET_CNTX_TYPE *app_cntx_old,
struct MHD_EventUpdateContext *ecb_cntx)
{
- (void) cls;
+ struct DaemonEntry *de = cls;
+
if (NULL == app_cntx_old)
{
app_cntx_old = GNUNET_new (struct SocketContext);
app_cntx_old->ecb_cntx = ecb_cntx;
app_cntx_old->fd = GNUNET_NETWORK_socket_box_native (fd);
+ app_cntx_old->de = de;
}
if (MHD_FD_STATE_NONE == watch_for)
{
@@ -253,57 +298,116 @@ socket_registration_update (
void
TALER_MHD2_daemon_start (struct MHD_Daemon *daemon)
{
+ struct DaemonEntry *de;
enum MHD_StatusCode sc;
- GNUNET_assert (NULL == mhd);
+ de = GNUNET_new (struct DaemonEntry);
GNUNET_assert (MHD_SC_OK ==
MHD_DAEMON_SET_OPTIONS (
daemon,
MHD_D_OPTION_REREGISTER_ALL (true),
MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL (
&socket_registration_update,
- NULL)));
- mhd = daemon;
- sc = MHD_daemon_start (mhd);
+ de)));
+ de->mhd = daemon;
+ sc = MHD_daemon_start (de->mhd);
if (MHD_SC_OK != sc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"MHD_daemon_start failed: %d\n",
(int) sc);
+ GNUNET_free (de);
return;
}
- mhd_task = prepare_daemon ();
+ GNUNET_CONTAINER_DLL_insert (mhd_head,
+ mhd_tail,
+ de);
+ prepare_daemon (de);
}
-struct MHD_Daemon *
-TALER_MHD2_daemon_stop (void)
+void
+TALER_MHD2_daemons_halt (void)
{
- struct MHD_Daemon *ret;
+ for (struct DaemonEntry *de = mhd_head;
+ NULL != de;
+ de = de->next)
+ {
+ if (NULL != de->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = NULL;
+ }
+ de->triggered = false;
+ }
+}
- if (NULL != mhd_task)
+
+void
+TALER_MHD2_daemons_quiesce (void)
+{
+ for (struct DaemonEntry *de = mhd_head;
+ NULL != de;
+ de = de->next)
{
- GNUNET_SCHEDULER_cancel (mhd_task);
- mhd_task = NULL;
+#if FIXME_MHD2
+ int fd;
+#endif
+
+ if (NULL != de->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = NULL;
+ }
+ de->triggered = false;
+#if FIXME_MHD2
+ fd = MHD_daemon_quiesce (de->mhd);
+ GNUNET_break (0 == close (fd));
+#endif
}
- ret = mhd;
- mhd = NULL;
- return ret;
}
void
-TALER_MHD2_daemon_trigger (void)
+TALER_MHD2_daemons_destroy (void)
{
- if (NULL != mhd_task)
+ struct DaemonEntry *de;
+
+ while (NULL != (de = mhd_head))
{
- GNUNET_SCHEDULER_cancel (mhd_task);
- mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
- NULL);
+ struct MHD_Daemon *mhd = de->mhd;
+
+ if (NULL != de->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = NULL;
+ }
+ MHD_daemon_destroy (mhd);
+ GNUNET_CONTAINER_DLL_remove (mhd_head,
+ mhd_tail,
+ de);
+ GNUNET_free (de);
}
- else
+}
+
+
+void
+TALER_MHD2_daemons_trigger (void)
+{
+ for (struct DaemonEntry *de = mhd_head;
+ NULL != de;
+ de = de->next)
{
- triggered = true;
+ if (NULL != de->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
+ de);
+ }
+ else
+ {
+ de->triggered = true;
+ }
}
}
diff --git a/src/mhd/mhd_config.c b/src/mhd/mhd_config.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014--2020 Taler Systems SA
+ Copyright (C) 2014--2025 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -26,9 +26,9 @@
/**
- * Backlog for listen operation on UNIX domain sockets.
+ * Backlog for listen operation.
*/
-#define UNIX_BACKLOG 500
+#define LISTEN_BACKLOG 500
/**
@@ -141,7 +141,7 @@ open_unix_path (const char *unix_path,
GNUNET_free (un);
if (GNUNET_OK !=
GNUNET_NETWORK_socket_listen (nh,
- UNIX_BACKLOG))
+ LISTEN_BACKLOG))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"listen");
@@ -173,22 +173,11 @@ open_unix_path (const char *unix_path,
}
-/**
- * Bind a listen socket to the UNIX domain path or the TCP port and IP address
- * as specified in @a cfg in section @a section. IF only a port was
- * specified, set @a port and return -1. Otherwise, return the bound file
- * descriptor.
- *
- * @param cfg configuration to parse
- * @param section configuration section to use
- * @param[out] port port to set, if TCP without BINDTO
- * @return -1 and a port of zero on error, otherwise
- * either -1 and a port, or a bound stream socket
- */
-int
-TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *section,
- uint16_t *port)
+enum GNUNET_GenericReturnValue
+TALER_MHD_listen_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section,
+ TALER_MHD_ListenSocketCallback cb,
+ void *cb_cls)
{
const char *choices[] = {
"tcp",
@@ -197,8 +186,8 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
NULL
};
const char *serve_type;
+ enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
- *port = 0;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_choice (cfg,
section,
@@ -218,54 +207,75 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
{
const char *listen_pid;
const char *listen_fds;
+ const char *listen_fdn;
/* check for systemd-style FD passing */
- listen_pid = getenv ("LISTEN_PID");
- listen_fds = getenv ("LISTEN_FDS");
- if ( (NULL != listen_pid) &&
- (NULL != listen_fds) )
+ if ( (NULL != (listen_pid = getenv ("LISTEN_PID"))) &&
+ (getpid () ==
+ strtol (listen_pid,
+ NULL,
+ 10)) &&
+ (NULL != (listen_fds = getenv ("LISTEN_FDS"))) &&
+ (NULL != (listen_fdn = getenv ("LISTEN_FDNAMES"))) )
{
+ char *fdns;
+
if (0 != strcmp (serve_type,
"systemd"))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Using systemd activation, due to environment variables. You should set SERVE=systemd in your configuration!\n");
+ "Using systemd activation due to environment variables. Set SERVE=systemd to disable this warning!\n");
+ }
+ else
+ {
+ ret = GNUNET_NO;
}
- if ( (getpid () ==
- strtol (listen_pid,
- NULL,
- 10)) &&
- (1 == strtoul (listen_fds,
- NULL,
- 10)) )
+ fdns = GNUNET_strdup (listen_fdn);
+ for (const char *tok = strtok (fdns,
+ ":");
+ NULL != tok;
+ tok = strtok (NULL,
+ ":"))
{
+ char dummy;
int fh;
int flags;
- fh = 3;
+ if ( (1 != sscanf (tok,
+ "%d%c",
+ &fh,
+ &dummy)) ||
+ (fh < 0) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Bad listen socket `%s' passed, ignored\n",
+ tok);
+ continue;
+ }
flags = fcntl (fh,
F_GETFD);
if ( (-1 == flags) &&
(EBADF == errno) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Bad listen socket passed, ignored\n");
- fh = -1;
+ "Bad listen socket %d passed, ignored\n",
+ fh);
+ continue;
}
flags |= FD_CLOEXEC;
- if ( (-1 != fh) &&
- (0 != fcntl (fh,
- F_SETFD,
- flags)) )
+ if (0 != fcntl (fh,
+ F_SETFD,
+ flags))
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
"fcntl");
- if (-1 != fh)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Successfully obtained listen socket from hypervisor\n");
- return fh;
- }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Successfully obtained listen socket %d from hypervisor\n",
+ fh);
+ cb (cb_cls,
+ fh);
+ ret = GNUNET_OK;
}
+ GNUNET_free (fdns);
}
}
@@ -278,7 +288,6 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
struct sockaddr_un s_un;
char *modestring;
- *port = 0;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (cfg,
section,
@@ -289,7 +298,7 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
section,
"UNIXPATH",
"UNIXPATH value required");
- return -1;
+ return GNUNET_SYSERR;
}
if (strlen (serve_unixpath) >= sizeof (s_un.sun_path))
{
@@ -297,7 +306,7 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
"unixpath `%s' is too long\n",
serve_unixpath);
GNUNET_free (serve_unixpath);
- return -1;
+ return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@@ -310,7 +319,7 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
section,
"UNIXPATH_MODE");
GNUNET_free (serve_unixpath);
- return -1;
+ return GNUNET_SYSERR;
}
errno = 0;
unixpath_mode = (mode_t) strtoul (modestring,
@@ -324,17 +333,21 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
"must be octal number");
GNUNET_free (modestring);
GNUNET_free (serve_unixpath);
- return -1;
+ return GNUNET_SYSERR;
}
GNUNET_free (modestring);
{
- int ret;
+ int fd;
- ret = open_unix_path (serve_unixpath,
- unixpath_mode);
+ fd = open_unix_path (serve_unixpath,
+ unixpath_mode);
GNUNET_free (serve_unixpath);
- return ret;
+ if (-1 == fd)
+ return GNUNET_NO;
+ cb (cb_cls,
+ fd);
+ return GNUNET_OK;
}
}
@@ -355,7 +368,7 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
section,
"PORT",
"port number required");
- return -1;
+ return GNUNET_SYSERR;
}
if ( (0 == lport) ||
@@ -365,9 +378,8 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
section,
"PORT",
"port number not in [1,65535]");
- return -1;
+ return GNUNET_SYSERR;
}
- *port = (uint16_t) lport;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
@@ -386,7 +398,7 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
GNUNET_snprintf (port_str,
sizeof (port_str),
"%u",
- (unsigned int) *port);
+ (unsigned int) lport);
memset (&hints,
0,
sizeof (hints));
@@ -410,62 +422,73 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
bind_to,
gai_strerror (ec));
GNUNET_free (bind_to);
- return -1;
+ return GNUNET_SYSERR;
}
GNUNET_free (bind_to);
-
- if (NULL == (nh = GNUNET_NETWORK_socket_create (res->ai_family,
- res->ai_socktype,
- res->ai_protocol)))
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
- "socket");
- freeaddrinfo (res);
- return -1;
- }
+ for (struct addrinfo *ai = res;
+ NULL != ai;
+ ai = ai->ai_next)
{
- const int on = 1;
-
+ if (NULL == (nh = GNUNET_NETWORK_socket_create (ai->ai_family,
+ ai->ai_socktype,
+ ai->ai_protocol)))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "socket");
+ freeaddrinfo (res);
+ return GNUNET_NO;
+ }
+ {
+ const int on = 1;
+
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_setsockopt (nh,
+ SOL_SOCKET,
+ SO_REUSEPORT,
+ &on,
+ sizeof(on)))
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "setsockopt");
+ }
if (GNUNET_OK !=
- GNUNET_NETWORK_socket_setsockopt (nh,
- SOL_SOCKET,
- SO_REUSEPORT,
- &on,
- sizeof(on)))
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
- "setsockopt");
- }
- if (GNUNET_OK !=
- GNUNET_NETWORK_socket_bind (nh,
- res->ai_addr,
- res->ai_addrlen))
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
- "bind");
- freeaddrinfo (res);
- return -1;
- }
- freeaddrinfo (res);
- }
-
- if (GNUNET_OK !=
- GNUNET_NETWORK_socket_listen (nh,
- UNIX_BACKLOG))
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
- "listen");
- GNUNET_SCHEDULER_shutdown ();
- return -1;
- }
+ GNUNET_NETWORK_socket_bind (nh,
+ ai->ai_addr,
+ ai->ai_addrlen))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "bind");
+ freeaddrinfo (res);
+ return GNUNET_NO;
+ }
- /* extract and return actual socket handle from 'nh' */
- {
- int fh;
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_listen (nh,
+ LISTEN_BACKLOG))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "listen");
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_NO;
+ }
- fh = GNUNET_NETWORK_get_fd (nh);
- GNUNET_NETWORK_socket_free_memory_only_ (nh);
- return fh;
- }
- }
- return -1;
+ /* extract and return actual socket handle from 'nh' */
+ {
+ int fh;
+
+ fh = GNUNET_NETWORK_get_fd (nh);
+ GNUNET_NETWORK_socket_free_memory_only_ (nh);
+ if (-1 == fh)
+ {
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+ cb (cb_cls,
+ fh);
+ }
+ } /* for all addrinfos */
+ freeaddrinfo (res);
+ return GNUNET_OK;
+ } /* bind data scope */
+ } /* tcp */
+ return ret;
}
diff --git a/src/mhd/mhd_run.c b/src/mhd/mhd_run.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2019-2021 Taler Systems SA
+ Copyright (C) 2019-2025 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -29,59 +29,83 @@
/**
- * Set to true if we should immediately MHD_run() again.
+ * Entry in list of HTTP servers we are running.
*/
-static bool triggered;
+struct DaemonEntry
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct DaemonEntry *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct DaemonEntry *prev;
+
+ /**
+ * The actual daemon.
+ */
+ struct MHD_Daemon *mhd;
+
+ /**
+ * Task running the HTTP server.
+ */
+ struct GNUNET_SCHEDULER_Task *mhd_task;
+
+ /**
+ * Set to true if we should immediately MHD_run() again.
+ */
+ bool triggered;
+
+};
+
/**
- * Task running the HTTP server.
+ * Head of list of HTTP servers.
*/
-static struct GNUNET_SCHEDULER_Task *mhd_task;
+static struct DaemonEntry *mhd_head;
/**
- * The MHD daemon we are running.
+ * Tail of list of HTTP servers.
*/
-static struct MHD_Daemon *mhd;
+static struct DaemonEntry *mhd_tail;
/**
* Function that queries MHD's select sets and
* starts the task waiting for them.
+ *
+ * @param[in,out] de daemon to start tasks for
*/
-static struct GNUNET_SCHEDULER_Task *
-prepare_daemon (void);
+static void
+prepare_daemon (struct DaemonEntry *de);
/**
* Call MHD to process pending requests and then go back
* and schedule the next run.
*
- * @param cls NULL
+ * @param cls our `struct DaemonEntry *`
*/
static void
run_daemon (void *cls)
{
- (void) cls;
- mhd_task = NULL;
+ struct DaemonEntry *de = cls;
+
+ de->mhd_task = NULL;
do {
- triggered = false;
+ de->triggered = false;
GNUNET_assert (MHD_YES ==
- MHD_run (mhd));
- } while (triggered);
- mhd_task = prepare_daemon ();
+ MHD_run (de->mhd));
+ } while (de->triggered);
+ prepare_daemon (de);
}
-/**
- * Function that queries MHD's select sets and starts the task waiting for
- * them.
- *
- * @return task handle for the MHD task.
- */
-static struct GNUNET_SCHEDULER_Task *
-prepare_daemon (void)
+static void
+prepare_daemon (struct DaemonEntry *de)
{
- struct GNUNET_SCHEDULER_Task *ret;
fd_set rs;
fd_set ws;
fd_set es;
@@ -99,12 +123,12 @@ prepare_daemon (void)
wws = GNUNET_NETWORK_fdset_create ();
max = -1;
GNUNET_assert (MHD_YES ==
- MHD_get_fdset (mhd,
+ MHD_get_fdset (de->mhd,
&rs,
&ws,
&es,
&max));
- haveto = MHD_get_timeout (mhd,
+ haveto = MHD_get_timeout (de->mhd,
&timeout);
if (haveto == MHD_YES)
tv = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
@@ -119,55 +143,110 @@ prepare_daemon (void)
max + 1);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Adding run_daemon select task\n");
- ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
- tv,
- wrs,
- wws,
- &run_daemon,
- NULL);
+ de->mhd_task
+ = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
+ tv,
+ wrs,
+ wws,
+ &run_daemon,
+ de);
GNUNET_NETWORK_fdset_destroy (wrs);
GNUNET_NETWORK_fdset_destroy (wws);
- return ret;
}
void
TALER_MHD_daemon_start (struct MHD_Daemon *daemon)
{
- GNUNET_assert (NULL == mhd);
- mhd = daemon;
- mhd_task = prepare_daemon ();
+ struct DaemonEntry *de;
+
+ de = GNUNET_new (struct DaemonEntry);
+ de->mhd = daemon;
+ GNUNET_CONTAINER_DLL_insert (mhd_head,
+ mhd_tail,
+ de);
+ prepare_daemon (de);
}
-struct MHD_Daemon *
-TALER_MHD_daemon_stop (void)
+void
+TALER_MHD_daemons_halt (void)
{
- struct MHD_Daemon *ret;
+ for (struct DaemonEntry *de = mhd_head;
+ NULL != de;
+ de = de->next)
+ {
+ if (NULL != de->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = NULL;
+ }
+ de->triggered = false;
+ }
+}
+
- if (NULL != mhd_task)
+void
+TALER_MHD_daemons_quiesce (void)
+{
+ for (struct DaemonEntry *de = mhd_head;
+ NULL != de;
+ de = de->next)
{
- GNUNET_SCHEDULER_cancel (mhd_task);
- mhd_task = NULL;
+ int fd;
+
+ if (NULL != de->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = NULL;
+ }
+ de->triggered = false;
+ fd = MHD_quiesce_daemon (de->mhd);
+ GNUNET_break (0 == close (fd));
}
- ret = mhd;
- mhd = NULL;
- return ret;
}
void
-TALER_MHD_daemon_trigger (void)
+TALER_MHD_daemons_destroy (void)
{
- if (NULL != mhd_task)
+ struct DaemonEntry *de;
+
+ while (NULL != (de = mhd_head))
{
- GNUNET_SCHEDULER_cancel (mhd_task);
- mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
- NULL);
+ struct MHD_Daemon *mhd = de->mhd;
+
+ if (NULL != de->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = NULL;
+ }
+ MHD_stop_daemon (mhd);
+ GNUNET_CONTAINER_DLL_remove (mhd_head,
+ mhd_tail,
+ de);
+ GNUNET_free (de);
}
- else
+}
+
+
+void
+TALER_MHD_daemon_trigger (void)
+{
+ for (struct DaemonEntry *de = mhd_head;
+ NULL != de;
+ de = de->next)
{
- triggered = true;
+ if (NULL != de->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (de->mhd_task);
+ de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
+ de);
+ }
+ else
+ {
+ de->triggered = true;
+ }
}
}
diff --git a/src/testing/taler-unified-setup.sh b/src/testing/taler-unified-setup.sh
@@ -258,8 +258,8 @@ fi
if [ "1" = "$START_NEXUS" ]
then
- echo -n "Testing for libeufin-cli"
- libeufin-cli --help >/dev/null </dev/null || exit_skip " MISSING"
+ echo -n "Testing for libeufin-nexus"
+ libeufin-nexus --help >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"
fi
@@ -268,6 +268,9 @@ then
echo -n "Testing for libeufin-bank"
libeufin-bank --help >/dev/null </dev/null || exit_skip " MISSING"
echo " FOUND"
+ echo -n "Testing for libeufin-cli"
+ libeufin-cli --help >/dev/null </dev/null || exit_skip " MISSING"
+ echo " FOUND"
fi
STAGE="config"
@@ -425,7 +428,7 @@ then
echo " OK"
fi
-if [[ "1" = "$START_NEXUS" || "1" = "$START_FAKEBANK" ]]
+if [[ "1" = "$START_BANK" || "1" = "$START_FAKEBANK" ]]
then
echo -n "Waiting for the bank"
# Wait for bank to be available (usually the slowest)