exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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:
Msrc/auditor/taler-auditor-httpd.c | 108++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/auditordb/auditordb_plugin.c | 4+++-
Msrc/exchange/taler-exchange-httpd.c | 151++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/include/taler_mhd2_lib.h | 89+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/include/taler_mhd_lib.h | 65+++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/kyclogic/taler-exchange-kyc-tester.c | 114++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/mhd/mhd2_run.c | 228+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/mhd/mhd_config.c | 245+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/mhd/mhd_run.c | 185++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/testing/taler-unified-setup.sh | 9++++++---
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)