From 6cd71bebbf143b62c8b99adcc51e34898fd11512 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 13 Jun 2016 16:36:10 +0200 Subject: [PATCH] implement #4569 --- ChangeLog | 5 + src/benchmark/taler-exchange-benchmark.c | 7 +- src/exchange-lib/exchange_api_admin.c | 5 +- src/exchange-lib/exchange_api_handle.c | 26 +- src/exchange-lib/exchange_api_handle.h | 11 + src/exchange-lib/test_exchange_api.c | 1 + src/exchange/exchange.conf | 38 +- src/exchange/taler-exchange-httpd.c | 619 +++++++++++++++-------- src/include/taler_exchange_service.h | 2 + 9 files changed, 496 insertions(+), 218 deletions(-) diff --git a/ChangeLog b/ChangeLog index a40af772e..af960b86f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Mon Jun 13 16:35:09 CEST 2016 + Splitting HTTP service to run on different listen + sockets for /admin and /test-APIs and the normal public + APIs (#4569). -CG + Thu Jun 9 19:17:02 CEST 2016 Rename /deposit/wtid to /track/transaction and /wire/deposits to /track/transfer. -CG diff --git a/src/benchmark/taler-exchange-benchmark.c b/src/benchmark/taler-exchange-benchmark.c index 761cc03bd..3c8ca5e26 100644 --- a/src/benchmark/taler-exchange-benchmark.c +++ b/src/benchmark/taler-exchange-benchmark.c @@ -52,7 +52,7 @@ static unsigned int nreserves; /** * How many coins are in `coins` array. This is needed - * as the number of coins is not always nreserves * COINS_PER_RESERVE + * as the number of coins is not always nreserves * COINS_PER_RESERVE * due to refresh operations */ unsigned int ncoins; @@ -423,7 +423,7 @@ reveal_cb (void *cls, struct Coin fresh_coin; struct TALER_Amount amount; char *refresh_denom; - + GNUNET_asprintf (&refresh_denom, "%s:%s", currency, @@ -794,6 +794,7 @@ benchmark_run (void *cls) &reserve_pub.eddsa_pub); reserves[i].aih = TALER_EXCHANGE_admin_add_incoming (exchange, + "http://localhost:18080/", &reserve_pub, &reserve_amount, execution_date, @@ -815,7 +816,7 @@ benchmark_run (void *cls) * since that is the only amount refreshed so far by the benchmark * * @param NULL-terminated array of value.fraction pairs - * @return GNUNET_OK if the array is correctly built, GNUNET_SYSERR + * @return #GNUNET_OK if the array is correctly built, #GNUNET_SYSERR * otherwise */ static unsigned int diff --git a/src/exchange-lib/exchange_api_admin.c b/src/exchange-lib/exchange_api_admin.c index 0452a9546..0d3129186 100644 --- a/src/exchange-lib/exchange_api_admin.c +++ b/src/exchange-lib/exchange_api_admin.c @@ -141,6 +141,7 @@ handle_admin_add_incoming_finished (void *cls, * to the operators of the exchange. * * @param exchange the exchange handle; the exchange must be ready to operate + * @param admin_url URL of the administrative interface of the exchange * @param reserve_pub public key of the reserve * @param amount amount that was deposited * @param execution_date when did we receive the amount @@ -156,6 +157,7 @@ handle_admin_add_incoming_finished (void *cls, */ struct TALER_EXCHANGE_AdminAddIncomingHandle * TALER_EXCHANGE_admin_add_incoming (struct TALER_EXCHANGE_Handle *exchange, + const char *admin_url, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *amount, struct GNUNET_TIME_Absolute execution_date, @@ -188,7 +190,8 @@ TALER_EXCHANGE_admin_add_incoming (struct TALER_EXCHANGE_Handle *exchange, aai->exchange = exchange; aai->cb = res_cb; aai->cb_cls = res_cb_cls; - aai->url = MAH_path_to_url (exchange, "/admin/add/incoming"); + aai->url = MAH_path_to_url2 (admin_url, + "/admin/add/incoming"); eh = curl_easy_init (); GNUNET_assert (NULL != (aai->json_enc = diff --git a/src/exchange-lib/exchange_api_handle.c b/src/exchange-lib/exchange_api_handle.c index b9104869e..318fd9e6b 100644 --- a/src/exchange-lib/exchange_api_handle.c +++ b/src/exchange-lib/exchange_api_handle.c @@ -779,23 +779,39 @@ MAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h) /** * Obtain the URL to use for an API request. * - * @param h the exchange handle to query + * @param base_url base URL of the exchange (i.e. "http://exchange/") * @param path Taler API path (i.e. "/reserve/withdraw") * @return the full URI to use with cURL */ char * -MAH_path_to_url (struct TALER_EXCHANGE_Handle *h, +MAH_path_to_url (struct TALER_EXCHANGE_Handle *exchange, const char *path) +{ + return MAH_path_to_url2 (exchange->url, + path); +} + + +/** + * Obtain the URL to use for an API request. + * + * @param base_url base URL of the exchange (i.e. "http://exchange/") + * @param path Taler API path (i.e. "/reserve/withdraw") + * @return the full URI to use with cURL + */ +char * +MAH_path_to_url2 (const char *base_url, + const char *path) { char *url; if ( ('/' == path[0]) && - (0 < strlen (h->url)) && - ('/' == h->url[strlen (h->url) - 1]) ) + (0 < strlen (base_url)) && + ('/' == base_url[strlen (base_url) - 1]) ) path++; /* avoid generating URL with "//" from concat */ GNUNET_asprintf (&url, "%s%s", - h->url, + base_url, path); return url; } diff --git a/src/exchange-lib/exchange_api_handle.h b/src/exchange-lib/exchange_api_handle.h index 2cf2166d0..6a5661508 100644 --- a/src/exchange-lib/exchange_api_handle.h +++ b/src/exchange-lib/exchange_api_handle.h @@ -55,5 +55,16 @@ char * MAH_path_to_url (struct TALER_EXCHANGE_Handle *h, const char *path); +/** + * Obtain the URL to use for an API request. + * + * @param base_url base URL of the exchange (i.e. "http://exchange/") + * @param path Taler API path (i.e. "/reserve/withdraw") + * @return the full URI to use with cURL + */ +char * +MAH_path_to_url2 (const char *base_url, + const char *path); + /* end of exchange_api_handle.h */ diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index 337fd297b..d0e2632e0 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -1753,6 +1753,7 @@ interpreter_run (void *cls) GNUNET_TIME_round_abs (&execution_date); cmd->details.admin_add_incoming.aih = TALER_EXCHANGE_admin_add_incoming (exchange, + "http://localhost:18080/", &reserve_pub, &amount, execution_date, diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf index afb5b8fae..333e9a58a 100644 --- a/src/exchange/exchange.conf +++ b/src/exchange/exchange.conf @@ -11,17 +11,6 @@ KEYDIR = ${TALER_DATA_HOME}/exchange/live-keys/ # the actual coin operations. # WIREFORMAT = test -# serve via tcp socket (on PORT) -SERVE = tcp - -# Unix domain socket to listen on, -# only effective with "SERVE = unix" -UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http -UNIXPATH_MODE = 660 - -# HTTP port the exchange listens to -PORT = 8081 - # Master public key used to sign the exchange's various keys # MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG @@ -34,3 +23,30 @@ DB = postgres # Where do we store the offline master private key of the exchange? MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv + +# Network configuration for the normal API/service HTTP server +# serve via tcp socket (on PORT) +SERVE = tcp + +# Unix domain socket to listen on, +# only effective with "SERVE = unix" +UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http +UNIXPATH_MODE = 660 + +# HTTP port the exchange listens to +PORT = 8081 + + +[exchange-admin] +# Network configuration for the /admin HTTP server + +# serve via tcp socket (on PORT) +SERVE = tcp + +# Unix domain socket to listen on, +# only effective with "SERVE = unix" +UNIXPATH = ${TALER_RUNTIME_DIR}/exchange-admin.http +UNIXPATH_MODE = 660 + +# HTTP port the exchange listens to +PORT = 18080 diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index ff18fb862..4b269a392 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -88,24 +88,50 @@ static unsigned int connection_timeout = 30; /** * The HTTP Daemon. */ -static struct MHD_Daemon *mydaemon; +static struct MHD_Daemon *mhd; + +/** + * The HTTP Daemon for /admin-requests. + */ +static struct MHD_Daemon *mhd_admin; + +/** + * Do not offer /admin API. + */ +static int no_admin; /** * Port to run the daemon on. */ static uint16_t serve_port; +/** + * Port to run the admin daemon on. + */ +static uint16_t serve_admin_port; + /** * Path for the unix domain-socket * to run the daemon on. */ static char *serve_unixpath; +/** + * Path for the unix domain-socket + * to run the admin daemon on. + */ +static char *serve_admin_unixpath; + /** * File mode for unix-domain socket. */ static mode_t unixpath_mode; +/** + * File mode for unix-domain socket. + */ +static mode_t unixpath_admin_mode; + /** * Function called whenever MHD is done with a request. If the @@ -251,14 +277,6 @@ handle_mhd_request (void *cls, "Only GET is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, - /* FIXME: maybe conditionally compile these? */ - { "/admin/add/incoming", MHD_HTTP_METHOD_POST, "application/json", - NULL, 0, - &TMH_ADMIN_handler_admin_add_incoming, MHD_HTTP_OK }, - { "/admin/add/incoming", NULL, "text/plain", - "Only POST is allowed", 0, - &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, - { "/track/transfer", MHD_HTTP_METHOD_GET, "application/json", NULL, 0, &TMH_TRACKING_handler_track_transfer, MHD_HTTP_OK }, @@ -272,6 +290,74 @@ handle_mhd_request (void *cls, "Only POST is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + { NULL, NULL, NULL, NULL, 0, 0 } + }; + static struct TMH_RequestHandler h404 = + { + "", NULL, "text/html", + "404: not found", 0, + &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND + }; + struct TMH_RequestHandler *rh; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Handling request for URL '%s'\n", + url); + for (i=0;NULL != handlers[i].url;i++) + { + rh = &handlers[i]; + if ( (0 == strcasecmp (url, + rh->url)) && + ( (NULL == rh->method) || + (0 == strcasecmp (method, + rh->method)) ) ) + return rh->handler (rh, + connection, + con_cls, + upload_data, + upload_data_size); + } + return TMH_MHD_handler_static_response (&h404, + connection, + con_cls, + upload_data, + upload_data_size); +} + + +/** + * Handle incoming administrative HTTP request. + * + * @param cls closure for MHD daemon (unused) + * @param connection the connection + * @param url the requested url + * @param method the method (POST, GET, ...) + * @param version HTTP version (ignored) + * @param upload_data request data + * @param upload_data_size size of @a upload_data in bytes + * @param con_cls closure for request (a `struct Buffer *`) + * @return MHD result code + */ +static int +handle_mhd_admin_request (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **con_cls) +{ + static struct TMH_RequestHandler handlers[] = + { + { "/admin/add/incoming", MHD_HTTP_METHOD_POST, "application/json", + NULL, 0, + &TMH_ADMIN_handler_admin_add_incoming, MHD_HTTP_OK }, + { "/admin/add/incoming", NULL, "text/plain", + "Only POST is allowed", 0, + &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + #if HAVE_DEVELOPER /* Client crypto-interoperability test functions */ { "/test", MHD_HTTP_METHOD_POST, "application/json", @@ -374,6 +460,123 @@ handle_mhd_request (void *cls, } +/** + * Parse the configuration to determine on which port + * or UNIX domain path we should run an HTTP service. + * + * @param type section of the configuration to parse ("exchange" or "exchange-admin") + * @param[out] port 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 + */ +static int +parse_port_config (const char *section, + uint16_t *rport, + char **unix_path, + mode_t *unix_mode) +{ + const char *choices[] = {"tcp", "unix"}; + const char *serve_type; + unsigned long long port; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_choice (cfg, + section, + "serve", + choices, + &serve_type)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "serve", + "serve type required"); + return GNUNET_SYSERR; + } + + if (0 == strcmp (serve_type, "tcp")) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + section, + "port", + &port)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "port", + "port number required"); + return GNUNET_SYSERR; + } + + if ( (0 == port) || + (port > UINT16_MAX) ) + { + fprintf (stderr, + "Invalid configuration (value out of range): %llu is not a valid port\n", + port); + return GNUNET_SYSERR; + } + *rport = (uint16_t) port; + *unix_path = NULL; + return GNUNET_OK; + } + if (0 == strcmp (serve_type, "unix")) + { + struct sockaddr_un s_un; + char *modestring; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + section, + "unixpath", + unix_path)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "unixpath", + "unixpath required"); + return GNUNET_SYSERR; + } + if (strlen (*unix_path) >= sizeof (s_un.sun_path)) + { + fprintf (stderr, + "Invalid configuration: unix path too long\n"); + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + section, + "unixpath_mode", + &modestring)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "unixpath_mode", + "unixpath_mode required"); + return GNUNET_SYSERR; + } + errno = 0; + *unix_mode = (mode_t) strtoul (modestring, NULL, 8); + if (0 != errno) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "unixpath_mode", + "unixpath_mode required"); + GNUNET_free (modestring); + return GNUNET_SYSERR; + } + GNUNET_free (modestring); + return GNUNET_OK; + } + /* not reached */ + GNUNET_assert (0); + return GNUNET_SYSERR; +} + + /** * Load configuration parameters for the exchange * server into the corresponding global variables. @@ -384,7 +587,6 @@ handle_mhd_request (void *cls, static int exchange_serve_process_config () { - unsigned long long port; char *TMH_master_public_key_str; if (GNUNET_OK != @@ -464,115 +666,24 @@ exchange_serve_process_config () TMH_plugin->create_tables (TMH_plugin->cls); } + if (GNUNET_OK != + parse_port_config ("exchange", + &serve_port, + &serve_unixpath, + &unixpath_mode)) { - const char *choices[] = {"tcp", "unix"}; - const char *serve_type; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_choice (cfg, - "exchange", - "serve", - choices, - &serve_type)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "serve", - "serve type required"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - - if (0 == strcmp (serve_type, "tcp")) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - "exchange", - "port", - &port)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "port", - "port number required"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - - if ( (0 == port) || - (port > UINT16_MAX) ) - { - fprintf (stderr, - "Invalid configuration (value out of range): %llu is not a valid port\n", - port); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - serve_port = (uint16_t) port; - } - else if (0 == strcmp (serve_type, "unix")) - { - struct sockaddr_un s_un; - char *modestring; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - "exchange", - "unixpath", - &serve_unixpath)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "unixpath", - "unixpath required"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - - if (strlen (serve_unixpath) >= sizeof (s_un.sun_path)) - { - fprintf (stderr, - "Invalid configuration: unix path too long\n"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "unixpath_mode", - &modestring)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "unixpath_mode", - "unixpath_mode required"); - TMH_VALIDATION_done (); - return GNUNET_SYSERR; - } - errno = 0; - unixpath_mode = (mode_t) strtoul (modestring, NULL, 8); - if (0 != errno) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "unixpath_mode", - "unixpath_mode required"); - TMH_VALIDATION_done (); - GNUNET_free (modestring); - return GNUNET_SYSERR; - } - GNUNET_free (modestring); - - } - else - { - // not reached - GNUNET_assert (0); - } + TMH_VALIDATION_done (); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + parse_port_config ("exchange-admin", + &serve_admin_port, + &serve_admin_unixpath, + &unixpath_admin_mode)) + { + TMH_VALIDATION_done (); + return GNUNET_SYSERR; } - - return GNUNET_OK; } @@ -712,6 +823,99 @@ handle_mhd_logs (void *cls, } +/** + * Open UNIX domain socket for listining at @a unix_path with + * permissions @a unix_mode. + * + * @param unix_path where to listen + * @param unix_mode access permissions to set + * @return -1 on error, otherwise the listen socket + */ +static int +open_unix_path (const char *unix_path, + mode_t unix_mode) +{ + struct GNUNET_NETWORK_Handle *nh; + struct sockaddr_un *un; + int fd; + + if (sizeof (un->sun_path) <= strlen (unix_path)) + { + fprintf (stderr, + "unixpath `%s' too long\n", + unix_path); + return -1; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Creating listen socket '%s' with mode %o\n", + unix_path, + unix_mode); + + if (GNUNET_OK != + GNUNET_DISK_directory_create_for_file (unix_path)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "mkdir", + unix_path); + } + + un = GNUNET_new (struct sockaddr_un); + un->sun_family = AF_UNIX; + strncpy (un->sun_path, + unix_path, + sizeof (un->sun_path) - 1); + GNUNET_NETWORK_unix_precheck (un); + + if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, + SOCK_STREAM, + 0))) + { + fprintf (stderr, + "create failed for AF_UNIX\n"); + GNUNET_free (un); + return -1; + } + if (GNUNET_OK != + GNUNET_NETWORK_socket_bind (nh, + (void *) un, + sizeof (struct sockaddr_un))) + { + fprintf (stderr, + "bind failed for AF_UNIX\n"); + GNUNET_free (un); + GNUNET_NETWORK_socket_close (nh); + return -1; + } + GNUNET_free (un); + if (GNUNET_OK != + GNUNET_NETWORK_socket_listen (nh, + UNIX_BACKLOG)) + { + fprintf (stderr, + "listen failed for AF_UNIX\n"); + GNUNET_NETWORK_socket_close (nh); + return -1; + } + + if (0 != chmod (serve_unixpath, + unix_mode)) + { + fprintf (stderr, + "chmod failed: %s\n", + strerror (errno)); + GNUNET_NETWORK_socket_close (nh); + return -1; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "set socket '%s' to mode %o\n", + unix_path, + unix_mode); + fd = GNUNET_NETWORK_get_fd (nh); + GNUNET_NETWORK_socket_free_memory_only_ (nh); + return fd; +} + + /** * The main function of the taler-exchange-httpd server ("the exchange"). * @@ -729,6 +933,9 @@ main (int argc, "force HTTP connections to be closed after each request", 0, &GNUNET_GETOPT_set_one, &TMH_exchange_connection_close}, GNUNET_GETOPT_OPTION_CFG_FILE (&cfgfile), + {'D', "disable-admin", NULL, + "do not run the /admin-HTTP server", 0, + &GNUNET_GETOPT_set_one, &no_admin}, {'t', "timeout", "SECONDS", "after how long do connections timeout by default (in seconds)", 1, &GNUNET_GETOPT_set_uint, &connection_timeout}, @@ -745,6 +952,7 @@ main (int argc, const char *listen_pid; const char *listen_fds; int fh = -1; + int fh_admin = -1; GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-exchange-httpd", @@ -778,7 +986,8 @@ main (int argc, if ( (NULL != listen_pid) && (NULL != listen_fds) && (getpid() == strtol (listen_pid, NULL, 10)) && - (1 == strtoul (listen_fds, NULL, 10)) /* we support only 1 socket */) + ( (1 == strtoul (listen_fds, NULL, 10)) || + (2 == strtoul (listen_fds, NULL, 10)) ) ) { int flags; @@ -794,97 +1003,90 @@ main (int argc, if (0 != fcntl (fh, F_SETFD, flags)) GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fcntl"); + + if (2 == strtoul (listen_fds, NULL, 10)) + { + fh_admin = 4; + flags = fcntl (fh_admin, F_GETFD); + if ( (-1 == flags) && (EBADF == errno) ) + { + fprintf (stderr, + "Bad listen socket passed, ignored\n"); + fh_admin = -1; + } + flags |= FD_CLOEXEC; + if (0 != fcntl (fh_admin, F_SETFD, flags)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "fcntl"); + } } /* consider unix path */ if ( (-1 == fh) && (NULL != serve_unixpath) ) { - struct GNUNET_NETWORK_Handle *nh; - struct sockaddr_un *un; - - if (sizeof (un->sun_path) <= strlen (serve_unixpath)) + fh = open_unix_path (serve_unixpath, + unixpath_mode); + if (-1 == fh) + return 1; + } + if ( (-1 == fh_admin) && + (0 == no_admin) && + (NULL != serve_admin_unixpath) ) + { + fh_admin = open_unix_path (serve_admin_unixpath, + unixpath_admin_mode); + if (-1 == fh_admin) { - fprintf (stderr, "unixpath too long\n"); + GNUNET_break (0 == close (fh)); return 1; } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Creating listen socket '%s' with mode %o\n", - serve_unixpath, - unixpath_mode); - - if (GNUNET_OK != - GNUNET_DISK_directory_create_for_file (serve_unixpath)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "mkdir", - serve_unixpath); - } - - un = GNUNET_new (struct sockaddr_un); - un->sun_family = AF_UNIX; - strncpy (un->sun_path, - serve_unixpath, - sizeof (un->sun_path) - 1); - - GNUNET_NETWORK_unix_precheck (un); - - if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0))) - { - fprintf (stderr, - "create failed for AF_UNIX\n"); - return 1; - } - if (GNUNET_OK != GNUNET_NETWORK_socket_bind (nh, (void *) un, sizeof (struct sockaddr_un))) - { - fprintf (stderr, - "bind failed for AF_UNIX\n"); - return 1; - } - if (GNUNET_OK != GNUNET_NETWORK_socket_listen (nh, UNIX_BACKLOG)) - { - fprintf (stderr, - "listen failed for AF_UNIX\n"); - return 1; - } - - fh = GNUNET_NETWORK_get_fd (nh); - - if (0 != chmod (serve_unixpath, unixpath_mode)) - { - fprintf (stderr, - "chmod failed: %s\n", - strerror (errno)); - return 1; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "set socket '%s' to mode %o\n", - serve_unixpath, - unixpath_mode); - GNUNET_NETWORK_socket_free_memory_only_ (nh); } - - mydaemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG, - (-1 == fh) ? serve_port : 0, - NULL, NULL, - &handle_mhd_request, NULL, - MHD_OPTION_LISTEN_SOCKET, fh, - MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, - MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, + mhd + = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG, + (-1 == fh) ? serve_port : 0, + NULL, NULL, + &handle_mhd_request, NULL, + MHD_OPTION_LISTEN_SOCKET, fh, + MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, #if HAVE_DEVELOPER - MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, + MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, #endif - MHD_OPTION_END); - - if (NULL == mydaemon) + MHD_OPTION_END); + if (NULL == mhd) { fprintf (stderr, "Failed to start HTTP server.\n"); return 1; } + + if (0 == no_admin) + { + mhd_admin + = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG, + (-1 == fh) ? serve_admin_port : 0, + NULL, NULL, + &handle_mhd_admin_request, NULL, + MHD_OPTION_LISTEN_SOCKET, fh_admin, + MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout, +#if HAVE_DEVELOPER + MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, +#endif + MHD_OPTION_END); + if (NULL == mhd_admin) + { + fprintf (stderr, + "Failed to start administrative HTTP server.\n"); + MHD_stop_daemon (mhd); + return 1; + } + } + #if HAVE_DEVELOPER if (NULL != input_filename) { @@ -905,15 +1107,20 @@ main (int argc, { case GNUNET_OK: case GNUNET_SYSERR: - MHD_stop_daemon (mydaemon); + MHD_stop_daemon (mhd); + if (NULL != mhd_admin) + MHD_stop_daemon (mhd_admin); break; case GNUNET_NO: { - MHD_socket sock = MHD_quiesce_daemon (mydaemon); + MHD_socket sock = MHD_quiesce_daemon (mhd); + MHD_socket admin_sock; pid_t chld; int flags; /* Set flags to make 'sock' inherited by child */ + if (NULL != mhd_admin) + admin_sock = MHD_quiesce_daemon (mhd_admin); flags = fcntl (sock, F_GETFD); GNUNET_assert (-1 != flags); flags &= ~FD_CLOEXEC; @@ -937,13 +1144,20 @@ main (int argc, "dup2"); _exit (1); } + if ( (NULL != mhd_admin) && + (4 != dup2 (admin_sock, 4)) ) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "dup2"); + _exit (1); + } /* Tell the child that it is the desired recipient for FD #3 */ GNUNET_snprintf (pids, sizeof (pids), "%u", getpid ()); setenv ("LISTEN_PID", pids, 1); - setenv ("LISTEN_FDS", "1", 1); + setenv ("LISTEN_FDS", (NULL != mhd_admin) ? "2" : "1", 1); /* Finally, exec the (presumably) more recent exchange binary */ execvp ("taler-exchange-httpd", argv); @@ -955,16 +1169,25 @@ main (int argc, before exiting; as the listen socket is no longer used, close it here */ GNUNET_break (0 == close (sock)); - while (0 != MHD_get_daemon_info (mydaemon, - MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) + if (NULL != mhd_admin) + GNUNET_break (0 == close (admin_sock)); + while ( (0 != MHD_get_daemon_info (mhd, + MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) || + ( (NULL != mhd_admin) && + (0 != MHD_get_daemon_info (mhd_admin, + MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) ) ) sleep (1); /* Now we're really done, practice clean shutdown */ - MHD_stop_daemon (mydaemon); + MHD_stop_daemon (mhd); + if (NULL != mhd_admin) + MHD_stop_daemon (mhd_admin); } break; default: GNUNET_break (0); - MHD_stop_daemon (mydaemon); + MHD_stop_daemon (mhd); + if (NULL != mhd_admin) + MHD_stop_daemon (mhd_admin); break; } TALER_EXCHANGEDB_plugin_unload (TMH_plugin); diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index ecfbc7253..4e8b0b375 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -1059,6 +1059,7 @@ typedef void * to the operators of the exchange. * * @param exchange the exchange handle; the exchange must be ready to operate + * @param admin_url URL of the administrative interface of the exchange * @param reserve_pub public key of the reserve * @param amount amount that was deposited * @param execution_date when did we receive the amount @@ -1074,6 +1075,7 @@ typedef void */ struct TALER_EXCHANGE_AdminAddIncomingHandle * TALER_EXCHANGE_admin_add_incoming (struct TALER_EXCHANGE_Handle *exchange, + const char *admin_url, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *amount, struct GNUNET_TIME_Absolute execution_date,