LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 105 311 33.8 %
Date: 2017-11-25 11:31:41 Functions: 7 10 70.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : 
      17             : /**
      18             :  * @file taler-exchange-httpd.c
      19             :  * @brief Serve the HTTP interface of the exchange
      20             :  * @author Florian Dold
      21             :  * @author Benedikt Mueller
      22             :  * @author Christian Grothoff
      23             :  */
      24             : #include "platform.h"
      25             : #include <gnunet/gnunet_util_lib.h>
      26             : #include <jansson.h>
      27             : #include <microhttpd.h>
      28             : #include <pthread.h>
      29             : #include "taler-exchange-httpd_parsing.h"
      30             : #include "taler-exchange-httpd_mhd.h"
      31             : #include "taler-exchange-httpd_admin.h"
      32             : #include "taler-exchange-httpd_deposit.h"
      33             : #include "taler-exchange-httpd_refund.h"
      34             : #include "taler-exchange-httpd_reserve_status.h"
      35             : #include "taler-exchange-httpd_reserve_withdraw.h"
      36             : #include "taler-exchange-httpd_payback.h"
      37             : #include "taler-exchange-httpd_refresh_link.h"
      38             : #include "taler-exchange-httpd_refresh_melt.h"
      39             : #include "taler-exchange-httpd_refresh_reveal.h"
      40             : #include "taler-exchange-httpd_track_transfer.h"
      41             : #include "taler-exchange-httpd_track_transaction.h"
      42             : #include "taler-exchange-httpd_keystate.h"
      43             : #include "taler-exchange-httpd_wire.h"
      44             : #if HAVE_DEVELOPER
      45             : #include "taler-exchange-httpd_test.h"
      46             : #endif
      47             : #include "taler_exchangedb_plugin.h"
      48             : #include "taler-exchange-httpd_validation.h"
      49             : 
      50             : 
      51             : /**
      52             :  * Backlog for listen operation on unix
      53             :  * domain sockets.
      54             :  */
      55             : #define UNIX_BACKLOG 500
      56             : 
      57             : /**
      58             :  * Which currency is used by this exchange?
      59             :  */
      60             : char *TEH_exchange_currency_string;
      61             : 
      62             : /**
      63             :  * Should we return "Connection: close" in each response?
      64             :  */
      65             : int TEH_exchange_connection_close;
      66             : 
      67             : /**
      68             :  * Base directory of the exchange (global)
      69             :  */
      70             : char *TEH_exchange_directory;
      71             : 
      72             : /**
      73             :  * The exchange's configuration (global)
      74             :  */
      75             : struct GNUNET_CONFIGURATION_Handle *cfg;
      76             : 
      77             : /**
      78             :  * Master public key (according to the
      79             :  * configuration in the exchange directory).
      80             :  */
      81             : struct TALER_MasterPublicKeyP TEH_master_public_key;
      82             : 
      83             : /**
      84             :  * Our DB plugin.
      85             :  */
      86             : struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
      87             : 
      88             : /**
      89             :  * Default timeout in seconds for HTTP requests.
      90             :  */
      91             : static unsigned int connection_timeout = 30;
      92             : 
      93             : /**
      94             :  * The HTTP Daemon.
      95             :  */
      96             : static struct MHD_Daemon *mhd;
      97             : 
      98             : /**
      99             :  * The HTTP Daemon for /admin-requests.
     100             :  */
     101             : static struct MHD_Daemon *mhd_admin;
     102             : 
     103             : /**
     104             :  * Do not offer /admin API.
     105             :  */
     106             : static int no_admin;
     107             : 
     108             : /**
     109             :  * Initialize the database by creating tables and indices.
     110             :  */
     111             : static int init_db;
     112             : 
     113             : /**
     114             :  * Port to run the daemon on.
     115             :  */
     116             : static uint16_t serve_port;
     117             : 
     118             : /**
     119             :  * Port to run the admin daemon on.
     120             :  */
     121             : static uint16_t serve_admin_port;
     122             : 
     123             : /**
     124             :  * Path for the unix domain-socket
     125             :  * to run the daemon on.
     126             :  */
     127             : static char *serve_unixpath;
     128             : 
     129             : /**
     130             :  * Path for the unix domain-socket
     131             :  * to run the admin daemon on.
     132             :  */
     133             : static char *serve_admin_unixpath;
     134             : 
     135             : /**
     136             :  * File mode for unix-domain socket.
     137             :  */
     138             : static mode_t unixpath_mode;
     139             : 
     140             : /**
     141             :  * File mode for unix-domain socket.
     142             :  */
     143             : static mode_t unixpath_admin_mode;
     144             : 
     145             : 
     146             : /**
     147             :  * Function called whenever MHD is done with a request.  If the
     148             :  * request was a POST, we may have stored a `struct Buffer *` in the
     149             :  * @a con_cls that might still need to be cleaned up.  Call the
     150             :  * respective function to free the memory.
     151             :  *
     152             :  * @param cls client-defined closure
     153             :  * @param connection connection handle
     154             :  * @param con_cls value as set by the last call to
     155             :  *        the #MHD_AccessHandlerCallback
     156             :  * @param toe reason for request termination
     157             :  * @see #MHD_OPTION_NOTIFY_COMPLETED
     158             :  * @ingroup request
     159             :  */
     160             : static void
     161          51 : handle_mhd_completion_callback (void *cls,
     162             :                                 struct MHD_Connection *connection,
     163             :                                 void **con_cls,
     164             :                                 enum MHD_RequestTerminationCode toe)
     165             : {
     166          51 :   if (NULL == *con_cls)
     167          51 :     return;
     168           0 :   TEH_PARSE_post_cleanup_callback (*con_cls);
     169           0 :   *con_cls = NULL;
     170             : }
     171             : 
     172             : 
     173             : /**
     174             :  * Handle incoming HTTP request.
     175             :  *
     176             :  * @param cls closure for MHD daemon (unused)
     177             :  * @param connection the connection
     178             :  * @param url the requested url
     179             :  * @param method the method (POST, GET, ...)
     180             :  * @param version HTTP version (ignored)
     181             :  * @param upload_data request data
     182             :  * @param upload_data_size size of @a upload_data in bytes
     183             :  * @param con_cls closure for request (a `struct Buffer *`)
     184             :  * @return MHD result code
     185             :  */
     186             : static int
     187         111 : handle_mhd_request (void *cls,
     188             :                     struct MHD_Connection *connection,
     189             :                     const char *url,
     190             :                     const char *method,
     191             :                     const char *version,
     192             :                     const char *upload_data,
     193             :                     size_t *upload_data_size,
     194             :                     void **con_cls)
     195             : {
     196             :   static struct TEH_RequestHandler handlers[] =
     197             :     {
     198             :       /* Landing page, tell humans to go away. */
     199             :       { "/", MHD_HTTP_METHOD_GET, "text/plain",
     200             :         "Hello, I'm the Taler exchange. This HTTP server is not for humans.\n", 0,
     201             :         &TEH_MHD_handler_static_response, MHD_HTTP_OK },
     202             :       /* /robots.txt: disallow everything */
     203             :       { "/robots.txt", MHD_HTTP_METHOD_GET, "text/plain",
     204             :         "User-agent: *\nDisallow: /\n", 0,
     205             :         &TEH_MHD_handler_static_response, MHD_HTTP_OK },
     206             :       /* AGPL licensing page, redirect to source. As per the AGPL-license,
     207             :          every deployment is required to offer the user a download of the
     208             :          source. We make this easy by including a redirect to the source
     209             :          here. */
     210             :       { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
     211             :         NULL, 0,
     212             :         &TEH_MHD_handler_agpl_redirect, MHD_HTTP_FOUND },
     213             : 
     214             :       /* Return key material and fundamental properties for this exchange */
     215             :       { "/keys", MHD_HTTP_METHOD_GET, "application/json",
     216             :         NULL, 0,
     217             :         &TEH_KS_handler_keys, MHD_HTTP_OK },
     218             :       { "/keys", NULL, "text/plain",
     219             :         "Only GET is allowed", 0,
     220             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     221             : 
     222             :       /* Requests for wiring information */
     223             :       { "/wire", MHD_HTTP_METHOD_GET, "application/json",
     224             :         NULL, 0,
     225             :         &TEH_WIRE_handler_wire, MHD_HTTP_OK },
     226             :       { "/wire", NULL, "text/plain",
     227             :         "Only GET is allowed", 0,
     228             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     229             : 
     230             :       /* Withdrawing coins / interaction with reserves */
     231             :       { "/reserve/status", MHD_HTTP_METHOD_GET, "application/json",
     232             :         NULL, 0,
     233             :         &TEH_RESERVE_handler_reserve_status, MHD_HTTP_OK },
     234             :       { "/reserve/status", NULL, "text/plain",
     235             :         "Only GET is allowed", 0,
     236             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     237             : 
     238             :       { "/reserve/withdraw", MHD_HTTP_METHOD_POST, "application/json",
     239             :         NULL, 0,
     240             :         &TEH_RESERVE_handler_reserve_withdraw, MHD_HTTP_OK },
     241             :       { "/reserve/withdraw", NULL, "text/plain",
     242             :         "Only POST is allowed", 0,
     243             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     244             : 
     245             :       /* Depositing coins */
     246             :       { "/deposit", MHD_HTTP_METHOD_POST, "application/json",
     247             :         NULL, 0,
     248             :         &TEH_DEPOSIT_handler_deposit, MHD_HTTP_OK },
     249             :       { "/deposit", NULL, "text/plain",
     250             :         "Only POST is allowed", 0,
     251             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     252             : 
     253             :       /* Refunding coins */
     254             :       { "/refund", MHD_HTTP_METHOD_POST, "application/json",
     255             :         NULL, 0,
     256             :         &TEH_REFUND_handler_refund, MHD_HTTP_OK },
     257             :       { "/refund", NULL, "text/plain",
     258             :         "Only POST is allowed", 0,
     259             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     260             : 
     261             :       /* Dealing with change */
     262             :       { "/refresh/melt", MHD_HTTP_METHOD_POST, "application/json",
     263             :         NULL, 0,
     264             :         &TEH_REFRESH_handler_refresh_melt, MHD_HTTP_OK },
     265             :       { "/refresh/melt", NULL, "text/plain",
     266             :         "Only POST is allowed", 0,
     267             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     268             : 
     269             :       { "/refresh/reveal", MHD_HTTP_METHOD_POST, "application/json",
     270             :         NULL, 0,
     271             :         &TEH_REFRESH_handler_refresh_reveal, MHD_HTTP_OK },
     272             :       { "/refresh/reveal", NULL, "text/plain",
     273             :         "Only POST is allowed", 0,
     274             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     275             : 
     276             :       { "/refresh/reveal", MHD_HTTP_METHOD_POST, "application/json",
     277             :         NULL, 0,
     278             :         &TEH_REFRESH_handler_refresh_reveal, MHD_HTTP_OK },
     279             :       { "/refresh/reveal", NULL, "text/plain",
     280             :         "Only POST is allowed", 0,
     281             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     282             : 
     283             :       { "/refresh/link", MHD_HTTP_METHOD_GET, "application/json",
     284             :         NULL, 0,
     285             :         &TEH_REFRESH_handler_refresh_link, MHD_HTTP_OK },
     286             :       { "/refresh/link", NULL, "text/plain",
     287             :         "Only GET is allowed", 0,
     288             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     289             : 
     290             :       { "/track/transfer", MHD_HTTP_METHOD_GET, "application/json",
     291             :         NULL, 0,
     292             :         &TEH_TRACKING_handler_track_transfer, MHD_HTTP_OK },
     293             :       { "/track/transfer", NULL, "text/plain",
     294             :         "Only GET is allowed", 0,
     295             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     296             :       { "/track/transaction", MHD_HTTP_METHOD_POST, "application/json",
     297             :         NULL, 0,
     298             :         &TEH_TRACKING_handler_track_transaction, MHD_HTTP_OK },
     299             :       { "/track/transaction", NULL, "text/plain",
     300             :         "Only POST is allowed", 0,
     301             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     302             : 
     303             :       { "/payback", MHD_HTTP_METHOD_POST, "application/json",
     304             :         NULL, 0,
     305             :         &TEH_PAYBACK_handler_payback, MHD_HTTP_OK },
     306             :       { "/refresh/link", NULL, "text/plain",
     307             :         "Only GET is allowed", 0,
     308             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     309             : 
     310             :       { NULL, NULL, NULL, NULL, 0, 0 }
     311             :     };
     312             :   static struct TEH_RequestHandler h404 =
     313             :     {
     314             :       "", NULL, "text/html",
     315             :       "<html><title>404: not found</title></html>", 0,
     316             :       &TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
     317             :     };
     318             :   struct TEH_RequestHandler *rh;
     319             : 
     320         111 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     321             :               "Handling request for URL '%s'\n",
     322             :               url);
     323         111 :   if (0 == strcasecmp (method,
     324             :                        MHD_HTTP_METHOD_HEAD))
     325           0 :     method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
     326        1620 :   for (unsigned int i=0;NULL != handlers[i].url;i++)
     327             :   {
     328        1620 :     rh = &handlers[i];
     329        1620 :     if ( (0 == strcasecmp (url,
     330         111 :                            rh->url)) &&
     331         222 :          ( (NULL == rh->method) ||
     332         111 :            (0 == strcasecmp (method,
     333             :                              rh->method)) ) )
     334         111 :       return rh->handler (rh,
     335             :                           connection,
     336             :                           con_cls,
     337             :                           upload_data,
     338             :                           upload_data_size);
     339             :   }
     340           0 :   return TEH_MHD_handler_static_response (&h404,
     341             :                                           connection,
     342             :                                           con_cls,
     343             :                                           upload_data,
     344             :                                           upload_data_size);
     345             : }
     346             : 
     347             : 
     348             : /**
     349             :  * Handle incoming administrative HTTP request.
     350             :  *
     351             :  * @param cls closure for MHD daemon (unused)
     352             :  * @param connection the connection
     353             :  * @param url the requested url
     354             :  * @param method the method (POST, GET, ...)
     355             :  * @param version HTTP version (ignored)
     356             :  * @param upload_data request data
     357             :  * @param upload_data_size size of @a upload_data in bytes
     358             :  * @param con_cls closure for request (a `struct Buffer *`)
     359             :  * @return MHD result code
     360             :  */
     361             : static int
     362          18 : handle_mhd_admin_request (void *cls,
     363             :                           struct MHD_Connection *connection,
     364             :                           const char *url,
     365             :                           const char *method,
     366             :                           const char *version,
     367             :                           const char *upload_data,
     368             :                           size_t *upload_data_size,
     369             :                           void **con_cls)
     370             : {
     371             :   static struct TEH_RequestHandler handlers[] =
     372             :     {
     373             :       { "/admin/add/incoming", MHD_HTTP_METHOD_POST, "application/json",
     374             :         NULL, 0,
     375             :         &TEH_ADMIN_handler_admin_add_incoming, MHD_HTTP_OK },
     376             :       { "/admin/add/incoming", NULL, "text/plain",
     377             :         "Only POST is allowed", 0,
     378             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     379             : 
     380             : #if HAVE_DEVELOPER
     381             :       /* Client crypto-interoperability test functions */
     382             :       { "/test", MHD_HTTP_METHOD_POST, "application/json",
     383             :         NULL, 0,
     384             :         &TEH_TEST_handler_test, MHD_HTTP_OK },
     385             :       { "/test", NULL, "text/plain",
     386             :         "Only POST is allowed", 0,
     387             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     388             : 
     389             :       { "/test/base32", MHD_HTTP_METHOD_POST, "application/json",
     390             :         NULL, 0,
     391             :         &TEH_TEST_handler_test_base32, MHD_HTTP_OK },
     392             :       { "/test/base32", NULL, "text/plain",
     393             :         "Only POST is allowed", 0,
     394             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     395             : 
     396             :       { "/test/encrypt", MHD_HTTP_METHOD_POST, "application/json",
     397             :         NULL, 0,
     398             :         &TEH_TEST_handler_test_encrypt, MHD_HTTP_OK },
     399             :       { "/test/encrypt", NULL, "text/plain",
     400             :         "Only POST is allowed", 0,
     401             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     402             : 
     403             :       { "/test/hkdf", MHD_HTTP_METHOD_POST, "application/json",
     404             :         NULL, 0,
     405             :         &TEH_TEST_handler_test_hkdf, MHD_HTTP_OK },
     406             :       { "/test/hkdf", NULL, "text/plain",
     407             :         "Only POST is allowed", 0,
     408             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     409             : 
     410             :       { "/test/ecdhe", MHD_HTTP_METHOD_POST, "application/json",
     411             :         NULL, 0,
     412             :         &TEH_TEST_handler_test_ecdhe, MHD_HTTP_OK },
     413             :       { "/test/ecdhe", NULL, "text/plain",
     414             :         "Only POST is allowed", 0,
     415             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     416             : 
     417             :       { "/test/eddsa", MHD_HTTP_METHOD_POST, "application/json",
     418             :         NULL, 0,
     419             :         &TEH_TEST_handler_test_eddsa, MHD_HTTP_OK },
     420             :       { "/test/eddsa", NULL, "text/plain",
     421             :         "Only POST is allowed", 0,
     422             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     423             : 
     424             :       { "/test/rsa/get", MHD_HTTP_METHOD_GET, "application/json",
     425             :         NULL, 0,
     426             :         &TEH_TEST_handler_test_rsa_get, MHD_HTTP_OK },
     427             :       { "/test/rsa/get", NULL, "text/plain",
     428             :         "Only GET is allowed", 0,
     429             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     430             : 
     431             :       { "/test/rsa/sign", MHD_HTTP_METHOD_POST, "application/json",
     432             :         NULL, 0,
     433             :         &TEH_TEST_handler_test_rsa_sign, MHD_HTTP_OK },
     434             :       { "/test/rsa/sign", NULL, "text/plain",
     435             :         "Only POST is allowed", 0,
     436             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     437             : 
     438             :       { "/test/transfer", MHD_HTTP_METHOD_POST, "application/json",
     439             :         NULL, 0,
     440             :         &TEH_TEST_handler_test_transfer, MHD_HTTP_OK },
     441             :       { "/test/transfer", NULL, "text/plain",
     442             :         "Only POST is allowed", 0,
     443             :         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
     444             : #endif
     445             : 
     446             :       { NULL, NULL, NULL, NULL, 0, 0 }
     447             :     };
     448             :   static struct TEH_RequestHandler h404 =
     449             :     {
     450             :       "", NULL, "text/html",
     451             :       "<html><title>404: not found</title></html>", 0,
     452             :       &TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
     453             :     };
     454             :   struct TEH_RequestHandler *rh;
     455             :   unsigned int i;
     456             : 
     457          18 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     458             :               "Handling request for URL '%s'\n",
     459             :               url);
     460          18 :   for (i=0;NULL != handlers[i].url;i++)
     461             :   {
     462          18 :     rh = &handlers[i];
     463          18 :     if ( (0 == strcasecmp (url,
     464          18 :                            rh->url)) &&
     465          36 :          ( (NULL == rh->method) ||
     466          18 :            (0 == strcasecmp (method,
     467             :                              rh->method)) ) )
     468          18 :       return rh->handler (rh,
     469             :                           connection,
     470             :                           con_cls,
     471             :                           upload_data,
     472             :                           upload_data_size);
     473             :   }
     474           0 :   return TEH_MHD_handler_static_response (&h404,
     475             :                                           connection,
     476             :                                           con_cls,
     477             :                                           upload_data,
     478             :                                           upload_data_size);
     479             : }
     480             : 
     481             : 
     482             : /**
     483             :  * Parse the configuration to determine on which port
     484             :  * or UNIX domain path we should run an HTTP service.
     485             :  *
     486             :  * @param section section of the configuration to parse ("exchange" or "exchange-admin")
     487             :  * @param[out] rport set to the port number, or 0 for none
     488             :  * @param[out] unix_path set to the UNIX path, or NULL for none
     489             :  * @param[out] unix_mode set to the mode to be used for @a unix_path
     490             :  * @return #GNUNET_OK on success
     491             :  */
     492             : static int
     493           4 : parse_port_config (const char *section,
     494             :                    uint16_t *rport,
     495             :                    char **unix_path,
     496             :                    mode_t *unix_mode)
     497             : {
     498           4 :   const char *choices[] = {"tcp", "unix"};
     499             :   const char *serve_type;
     500             :   unsigned long long port;
     501             : 
     502           4 :   if (GNUNET_OK !=
     503           4 :       GNUNET_CONFIGURATION_get_value_choice (cfg,
     504             :                                              section,
     505             :                                              "serve",
     506             :                                              choices,
     507             :                                              &serve_type))
     508             :   {
     509           0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     510             :                                section,
     511             :                                "serve",
     512             :                                "serve type required");
     513           0 :     return GNUNET_SYSERR;
     514             :   }
     515             : 
     516           4 :   if (0 == strcmp (serve_type, "tcp"))
     517             :   {
     518           4 :     if (GNUNET_OK !=
     519           4 :         GNUNET_CONFIGURATION_get_value_number (cfg,
     520             :                                                section,
     521             :                                                "port",
     522             :                                                &port))
     523             :     {
     524           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     525             :                                  section,
     526             :                                  "port",
     527             :                                  "port number required");
     528           0 :       return GNUNET_SYSERR;
     529             :     }
     530             : 
     531           8 :     if ( (0 == port) ||
     532           4 :          (port > UINT16_MAX) )
     533             :     {
     534           0 :       fprintf (stderr,
     535             :                "Invalid configuration (value out of range): %llu is not a valid port\n",
     536             :                port);
     537           0 :       return GNUNET_SYSERR;
     538             :     }
     539           4 :     *rport = (uint16_t) port;
     540           4 :     *unix_path = NULL;
     541           4 :     return GNUNET_OK;
     542             :   }
     543           0 :   if (0 == strcmp (serve_type, "unix"))
     544             :   {
     545             :     struct sockaddr_un s_un;
     546             :     char *modestring;
     547             : 
     548           0 :     if (GNUNET_OK !=
     549           0 :         GNUNET_CONFIGURATION_get_value_filename (cfg,
     550             :                                                  section,
     551             :                                                  "unixpath",
     552             :                                                  unix_path))
     553             :     {
     554           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     555             :                                  section,
     556             :                                  "unixpath",
     557             :                                  "unixpath required");
     558           0 :       return GNUNET_SYSERR;
     559             :     }
     560           0 :     if (strlen (*unix_path) >= sizeof (s_un.sun_path))
     561             :     {
     562           0 :       fprintf (stderr,
     563             :                "Invalid configuration: unix path too long\n");
     564           0 :       return GNUNET_SYSERR;
     565             :     }
     566             : 
     567           0 :     if (GNUNET_OK !=
     568           0 :         GNUNET_CONFIGURATION_get_value_string (cfg,
     569             :                                                section,
     570             :                                                "unixpath_mode",
     571             :                                                &modestring))
     572             :     {
     573           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     574             :                                  section,
     575             :                                  "unixpath_mode",
     576             :                                  "unixpath_mode required");
     577           0 :       return GNUNET_SYSERR;
     578             :     }
     579           0 :     errno = 0;
     580           0 :     *unix_mode = (mode_t) strtoul (modestring, NULL, 8);
     581           0 :     if (0 != errno)
     582             :     {
     583           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     584             :                                  section,
     585             :                                  "unixpath_mode",
     586             :                                  "unixpath_mode required");
     587           0 :       GNUNET_free (modestring);
     588           0 :       return GNUNET_SYSERR;
     589             :     }
     590           0 :     GNUNET_free (modestring);
     591           0 :     return GNUNET_OK;
     592             :   }
     593             :   /* not reached */
     594           0 :   GNUNET_assert (0);
     595             :   return GNUNET_SYSERR;
     596             : }
     597             : 
     598             : 
     599             : /**
     600             :  * Load configuration parameters for the exchange
     601             :  * server into the corresponding global variables.
     602             :  *
     603             :  * @return #GNUNET_OK on success
     604             :  */
     605             : static int
     606           3 : exchange_serve_process_config ()
     607             : {
     608             :   char *TEH_master_public_key_str;
     609             : 
     610           3 :   if (GNUNET_OK !=
     611           3 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
     612             :                                                "exchange",
     613             :                                                "KEYDIR",
     614             :                                                &TEH_exchange_directory))
     615             :   {
     616           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     617             :                                "exchange",
     618             :                                "KEYDIR");
     619           0 :     return GNUNET_SYSERR;
     620             :   }
     621           3 :   if (GNUNET_OK !=
     622           3 :       GNUNET_CONFIGURATION_get_value_string (cfg,
     623             :                                              "taler",
     624             :                                              "currency",
     625             :                                              &TEH_exchange_currency_string))
     626             :   {
     627           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     628             :                                "taler",
     629             :                                "currency");
     630           0 :     return GNUNET_SYSERR;
     631             :   }
     632           3 :   if (strlen (TEH_exchange_currency_string) >= TALER_CURRENCY_LEN)
     633             :   {
     634           0 :     fprintf (stderr,
     635             :              "Currency `%s' longer than the allowed limit of %u characters.",
     636             :              TEH_exchange_currency_string,
     637             :              (unsigned int) TALER_CURRENCY_LEN);
     638           0 :     return GNUNET_SYSERR;
     639             :   }
     640           3 :   if (GNUNET_OK !=
     641           3 :       GNUNET_CONFIGURATION_get_value_string (cfg,
     642             :                                              "exchange",
     643             :                                              "master_public_key",
     644             :                                              &TEH_master_public_key_str))
     645             :   {
     646           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     647             :                                "exchange",
     648             :                                "master_public_key");
     649           0 :     return GNUNET_SYSERR;
     650             :   }
     651           3 :   if (GNUNET_OK !=
     652           3 :       GNUNET_CRYPTO_eddsa_public_key_from_string (TEH_master_public_key_str,
     653             :                                                   strlen (TEH_master_public_key_str),
     654             :                                                   &TEH_master_public_key.eddsa_pub))
     655             :   {
     656           0 :     fprintf (stderr,
     657             :              "Invalid master public key given in exchange configuration.");
     658           0 :     GNUNET_free (TEH_master_public_key_str);
     659           0 :     return GNUNET_SYSERR;
     660             :   }
     661           3 :   GNUNET_free (TEH_master_public_key_str);
     662             : 
     663           3 :   if ( (GNUNET_OK !=
     664           5 :         TEH_VALIDATION_init (cfg)) ||
     665             :        (GNUNET_OK !=
     666           2 :         TEH_WIRE_init ()) )
     667           1 :     return GNUNET_SYSERR;
     668             : 
     669             : 
     670           2 :   if (NULL ==
     671           2 :       (TEH_plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
     672             :   {
     673           0 :     fprintf (stderr,
     674             :              "Failed to initialize DB subsystem\n");
     675           0 :     TEH_VALIDATION_done ();
     676           0 :     return GNUNET_SYSERR;
     677             :   }
     678           2 :   if (0 != init_db)
     679             :   {
     680           2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     681             :                 "Ensuring that tables and indices are created!\n");
     682           2 :     TEH_plugin->create_tables (TEH_plugin->cls);
     683             :   }
     684             : 
     685           2 :   if (GNUNET_OK !=
     686           2 :       parse_port_config ("exchange",
     687             :                          &serve_port,
     688             :                          &serve_unixpath,
     689             :                          &unixpath_mode))
     690             :   {
     691           0 :     TEH_VALIDATION_done ();
     692           0 :     return GNUNET_SYSERR;
     693             :   }
     694           2 :   if (GNUNET_OK !=
     695           2 :       parse_port_config ("exchange-admin",
     696             :                          &serve_admin_port,
     697             :                          &serve_admin_unixpath,
     698             :                          &unixpath_admin_mode))
     699             :   {
     700           0 :     TEH_VALIDATION_done ();
     701           0 :     return GNUNET_SYSERR;
     702             :   }
     703           2 :   return GNUNET_OK;
     704             : }
     705             : 
     706             : 
     707             : /* Developer logic for supporting the `-f' option. */
     708             : #if HAVE_DEVELOPER
     709             : 
     710             : 
     711             : /**
     712             :  * Option `-f' (specifies an input file to give to the HTTP server).
     713             :  */
     714             : static char *input_filename;
     715             : 
     716             : 
     717             : /**
     718             :  * Run 'nc' or 'ncat' as a fake HTTP client using #input_filename
     719             :  * as the input for the request.  If launching the client worked,
     720             :  * run the #TEH_KS_loop() event loop as usual.
     721             :  *
     722             :  * @return #GNUNET_OK
     723             :  */
     724             : static int
     725           0 : run_fake_client ()
     726             : {
     727             :   pid_t cld;
     728             :   char ports[6];
     729             :   int fd;
     730             :   int ret;
     731             :   int status;
     732             : 
     733           0 :   fd = open (input_filename, O_RDONLY);
     734           0 :   if (-1 == fd)
     735             :   {
     736           0 :     fprintf (stderr,
     737             :              "Failed to open `%s': %s\n",
     738             :              input_filename,
     739           0 :              strerror (errno));
     740           0 :     return GNUNET_SYSERR;
     741             :   }
     742             :   /* Fake HTTP client request with #input_filename as input.
     743             :      We do this using the nc tool. */
     744           0 :   GNUNET_snprintf (ports,
     745             :                    sizeof (ports),
     746             :                    "%u",
     747             :                    serve_port);
     748           0 :   if (0 == (cld = fork()))
     749             :   {
     750           0 :     GNUNET_break (0 == close (0));
     751           0 :     GNUNET_break (0 == dup2 (fd, 0));
     752           0 :     GNUNET_break (0 == close (fd));
     753           0 :     if ( (0 != execlp ("nc",
     754             :                        "nc",
     755             :                        "localhost",
     756             :                        ports,
     757             :                        "-w", "30",
     758           0 :                        NULL)) &&
     759           0 :          (0 != execlp ("ncat",
     760             :                        "ncat",
     761             :                        "localhost",
     762             :                        ports,
     763             :                        "-i", "30",
     764             :                        NULL)) )
     765             :     {
     766           0 :       fprintf (stderr,
     767             :                "Failed to run both `nc' and `ncat': %s\n",
     768           0 :                strerror (errno));
     769             :     }
     770           0 :     _exit (1);
     771             :   }
     772             :   /* parent process */
     773           0 :   GNUNET_break (0 == close (fd));
     774           0 :   ret = TEH_KS_loop ();
     775           0 :   if (cld != waitpid (cld, &status, 0))
     776           0 :     fprintf (stderr,
     777             :              "Waiting for `nc' child failed: %s\n",
     778           0 :              strerror (errno));
     779           0 :   return ret;
     780             : }
     781             : 
     782             : 
     783             : /**
     784             :  * Signature of the callback used by MHD to notify the application
     785             :  * about completed connections.  If we are running in test-mode with
     786             :  * an #input_filename, this function is used to terminate the HTTPD
     787             :  * after the first request has been processed.
     788             :  *
     789             :  * @param cls client-defined closure, NULL
     790             :  * @param connection connection handle (ignored)
     791             :  * @param socket_context socket-specific pointer (ignored)
     792             :  * @param toe reason for connection notification
     793             :  */
     794             : static void
     795          10 : connection_done (void *cls,
     796             :                  struct MHD_Connection *connection,
     797             :                  void **socket_context,
     798             :                  enum MHD_ConnectionNotificationCode toe)
     799             : {
     800             :   /* We only act if the connection is closed. */
     801          10 :   if (MHD_CONNECTION_NOTIFY_CLOSED != toe)
     802           5 :     return;
     803             :   /* This callback is also present if the option wasn't, so
     804             :      make sure the option was actually set. */
     805           5 :   if (NULL == input_filename)
     806           5 :     return;
     807             :   /* We signal ourselves to terminate. */
     808           0 :   if (0 != kill (getpid(),
     809             :                  SIGTERM))
     810           0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     811             :                          "kill");
     812             : }
     813             : 
     814             : /* end of HAVE_DEVELOPER */
     815             : #endif
     816             : 
     817             : /**
     818             :  * Function called for logging by MHD.
     819             :  *
     820             :  * @param cls closure, NULL
     821             :  * @param fm format string (`printf()`-style)
     822             :  * @param ap arguments to @a fm
     823             :  */
     824             : static void
     825           0 : handle_mhd_logs (void *cls,
     826             :                  const char *fm,
     827             :                  va_list ap)
     828             : {
     829             :   char buf[2048];
     830             : 
     831           0 :   vsnprintf (buf,
     832             :              sizeof (buf),
     833             :              fm,
     834             :              ap);
     835           0 :   GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
     836             :                    "libmicrohttpd",
     837             :                    "%s",
     838             :                    buf);
     839           0 : }
     840             : 
     841             : 
     842             : /**
     843             :  * Open UNIX domain socket for listining at @a unix_path with
     844             :  * permissions @a unix_mode.
     845             :  *
     846             :  * @param unix_path where to listen
     847             :  * @param unix_mode access permissions to set
     848             :  * @return -1 on error, otherwise the listen socket
     849             :  */
     850             : static int
     851           0 : open_unix_path (const char *unix_path,
     852             :                 mode_t unix_mode)
     853             : {
     854             :   struct GNUNET_NETWORK_Handle *nh;
     855             :   struct sockaddr_un *un;
     856             :   int fd;
     857             : 
     858           0 :   if (sizeof (un->sun_path) <= strlen (unix_path))
     859             :   {
     860           0 :     fprintf (stderr,
     861             :              "unixpath `%s' too long\n",
     862             :              unix_path);
     863           0 :     return -1;
     864             :   }
     865           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     866             :               "Creating listen socket '%s' with mode %o\n",
     867             :               unix_path,
     868             :               unix_mode);
     869             : 
     870           0 :   if (GNUNET_OK !=
     871           0 :       GNUNET_DISK_directory_create_for_file (unix_path))
     872             :   {
     873           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     874             :                               "mkdir",
     875             :                               unix_path);
     876             :   }
     877             : 
     878           0 :   un = GNUNET_new (struct sockaddr_un);
     879           0 :   un->sun_family = AF_UNIX;
     880           0 :   strncpy (un->sun_path,
     881             :            unix_path,
     882             :            sizeof (un->sun_path) - 1);
     883           0 :   GNUNET_NETWORK_unix_precheck (un);
     884             : 
     885           0 :   if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX,
     886             :                                                   SOCK_STREAM,
     887             :                                                   0)))
     888             :   {
     889           0 :     fprintf (stderr,
     890             :              "create failed for AF_UNIX\n");
     891           0 :     GNUNET_free (un);
     892           0 :     return -1;
     893             :   }
     894           0 :   if (GNUNET_OK !=
     895           0 :       GNUNET_NETWORK_socket_bind (nh,
     896             :                                   (void *) un,
     897             :                                   sizeof (struct sockaddr_un)))
     898             :   {
     899           0 :     fprintf (stderr,
     900             :              "bind failed for AF_UNIX\n");
     901           0 :     GNUNET_free (un);
     902           0 :     GNUNET_NETWORK_socket_close (nh);
     903           0 :     return -1;
     904             :   }
     905           0 :   GNUNET_free (un);
     906           0 :   if (GNUNET_OK !=
     907           0 :       GNUNET_NETWORK_socket_listen (nh,
     908             :                                     UNIX_BACKLOG))
     909             :   {
     910           0 :     fprintf (stderr,
     911             :              "listen failed for AF_UNIX\n");
     912           0 :     GNUNET_NETWORK_socket_close (nh);
     913           0 :     return -1;
     914             :   }
     915             : 
     916           0 :   if (0 != chmod (unix_path,
     917             :                   unix_mode))
     918             :   {
     919           0 :     fprintf (stderr,
     920             :              "chmod failed: %s\n",
     921           0 :              strerror (errno));
     922           0 :     GNUNET_NETWORK_socket_close (nh);
     923           0 :     return -1;
     924             :   }
     925           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     926             :               "set socket '%s' to mode %o\n",
     927             :               unix_path,
     928             :               unix_mode);
     929           0 :   fd = GNUNET_NETWORK_get_fd (nh);
     930           0 :   GNUNET_NETWORK_socket_free_memory_only_ (nh);
     931           0 :   return fd;
     932             : }
     933             : 
     934             : 
     935             : /**
     936             :  * The main function of the taler-exchange-httpd server ("the exchange").
     937             :  *
     938             :  * @param argc number of arguments from the command line
     939             :  * @param argv command line arguments
     940             :  * @return 0 ok, 1 on error
     941             :  */
     942             : int
     943           3 : main (int argc,
     944             :       char *const *argv)
     945             : {
     946           3 :   char *cfgfile = NULL;
     947           3 :   char *loglev = NULL;
     948           3 :   char *logfile = NULL;
     949           3 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
     950             :     GNUNET_GETOPT_option_flag ('C',
     951             :                                   "connection-close",
     952             :                                   "force HTTP connections to be closed after each request",
     953             :                                   &TEH_exchange_connection_close),
     954             :     GNUNET_GETOPT_option_cfgfile (&cfgfile),
     955             :     GNUNET_GETOPT_option_flag ('D',
     956             :                                   "disable-admin",
     957             :                                   "do not run the /admin-HTTP server",
     958             :                                   &no_admin),
     959             :     GNUNET_GETOPT_option_flag ('i',
     960             :                                   "init-db",
     961             :                                   "create database tables and indicies if necessary",
     962             :                                   &init_db),
     963             :    GNUNET_GETOPT_option_uint ('t',
     964             :                                   "timeout",
     965             :                                   "SECONDS",
     966             :                                   "after how long do connections timeout by default (in seconds)",
     967             :                                   &connection_timeout),
     968             : #if HAVE_DEVELOPER
     969             :    GNUNET_GETOPT_option_filename ('f',
     970             :                                   "file-input",
     971             :                                   "FILENAME",
     972             :                                   "run in test-mode using FILENAME as the HTTP request to process",
     973             :                                   &input_filename),
     974             : #endif
     975             :     GNUNET_GETOPT_option_help ("HTTP server providing a RESTful API to access a Taler exchange"),
     976             :     GNUNET_GETOPT_option_loglevel (&loglev),
     977             :     GNUNET_GETOPT_option_logfile (&logfile),
     978             :     GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
     979             :     GNUNET_GETOPT_OPTION_END
     980             :   };
     981             :   int ret;
     982             :   const char *listen_pid;
     983             :   const char *listen_fds;
     984           3 :   int fh = -1;
     985           3 :   int fh_admin = -1;
     986             : 
     987           3 :   if (0 >=
     988           3 :       GNUNET_GETOPT_run ("taler-exchange-httpd",
     989             :                          options,
     990             :                          argc, argv))
     991           0 :     return 1;
     992           3 :   GNUNET_assert (GNUNET_OK ==
     993             :                  GNUNET_log_setup ("taler-exchange-httpd",
     994             :                                    (NULL == loglev) ? "INFO" : loglev,
     995             :                                    logfile));
     996           3 :   if (NULL == cfgfile)
     997           0 :     cfgfile = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file);
     998           3 :   cfg = GNUNET_CONFIGURATION_create ();
     999           3 :   if (GNUNET_SYSERR ==
    1000           3 :       GNUNET_CONFIGURATION_load (cfg, cfgfile))
    1001             :   {
    1002           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1003             :                 _("Malformed configuration file `%s', exit ...\n"),
    1004             :                 cfgfile);
    1005           0 :     GNUNET_free_non_null (cfgfile);
    1006           0 :     return 1;
    1007             :   }
    1008           3 :   GNUNET_free_non_null (cfgfile);
    1009           3 :   if (GNUNET_OK !=
    1010           3 :       exchange_serve_process_config ())
    1011           1 :     return 1;
    1012             : 
    1013             :   /* check for systemd-style FD passing */
    1014           2 :   listen_pid = getenv ("LISTEN_PID");
    1015           2 :   listen_fds = getenv ("LISTEN_FDS");
    1016           2 :   if ( (NULL != listen_pid) &&
    1017           0 :        (NULL != listen_fds) &&
    1018           0 :        (getpid() == strtol (listen_pid,
    1019             :                             NULL,
    1020           0 :                             10)) &&
    1021           0 :        ( (1 == strtoul (listen_fds,
    1022             :                         NULL,
    1023           0 :                         10)) ||
    1024           0 :          (2 == strtoul (listen_fds,
    1025             :                         NULL,
    1026             :                         10)) ) )
    1027             :   {
    1028             :     int flags;
    1029             : 
    1030           0 :     fh = 3;
    1031           0 :     flags = fcntl (fh,
    1032             :                    F_GETFD);
    1033           0 :     if ( (-1 == flags) &&
    1034           0 :          (EBADF == errno) )
    1035             :     {
    1036           0 :       fprintf (stderr,
    1037             :                "Bad listen socket passed, ignored\n");
    1038           0 :       fh = -1;
    1039             :     }
    1040           0 :     flags |= FD_CLOEXEC;
    1041           0 :     if ( (-1 != fh) &&
    1042           0 :          (0 != fcntl (fh,
    1043             :                       F_SETFD,
    1044             :                       flags)) )
    1045           0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    1046             :                            "fcntl");
    1047             : 
    1048           0 :     if (2 == strtoul (listen_fds,
    1049             :                       NULL,
    1050             :                       10))
    1051             :     {
    1052           0 :       fh_admin = 4;
    1053           0 :       flags = fcntl (fh_admin,
    1054             :                      F_GETFD);
    1055           0 :       if ( (-1 == flags) &&
    1056           0 :            (EBADF == errno) )
    1057             :       {
    1058           0 :         fprintf (stderr,
    1059             :                  "Bad listen socket passed, ignored\n");
    1060           0 :         fh_admin = -1;
    1061             :       }
    1062           0 :       flags |= FD_CLOEXEC;
    1063           0 :       if ( (-1 != fh_admin) &&
    1064           0 :            (0 != fcntl (fh_admin,
    1065             :                         F_SETFD,
    1066             :                         flags)) )
    1067           0 :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    1068             :                              "fcntl");
    1069             :     }
    1070             :   }
    1071             : 
    1072             :   /* consider unix path */
    1073           4 :   if ( (-1 == fh) &&
    1074           2 :        (NULL != serve_unixpath) )
    1075             :   {
    1076           0 :     fh = open_unix_path (serve_unixpath,
    1077             :                          unixpath_mode);
    1078           0 :     if (-1 == fh)
    1079           0 :       return 1;
    1080             :   }
    1081           4 :   if ( (-1 == fh_admin) &&
    1082           4 :        (0 == no_admin) &&
    1083           2 :        (NULL != serve_admin_unixpath) )
    1084             :   {
    1085           0 :     fh_admin = open_unix_path (serve_admin_unixpath,
    1086             :                                unixpath_admin_mode);
    1087           0 :     if (-1 == fh_admin)
    1088             :     {
    1089           0 :       if (-1 != fh)
    1090           0 :         GNUNET_break (0 == close (fh));
    1091           0 :       return 1;
    1092             :     }
    1093             :   }
    1094             : 
    1095             :   mhd
    1096           2 :     = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG,
    1097             :                         (-1 == fh) ? serve_port : 0,
    1098             :                         NULL, NULL,
    1099             :                         &handle_mhd_request, NULL,
    1100             :                         MHD_OPTION_LISTEN_SOCKET, fh,
    1101             :                         MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL,
    1102             :                         MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL,
    1103             :                         MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout,
    1104             : #if HAVE_DEVELOPER
    1105             :                         MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL,
    1106             : #endif
    1107             :                         MHD_OPTION_END);
    1108           2 :   if (NULL == mhd)
    1109             :   {
    1110           0 :     fprintf (stderr,
    1111             :              "Failed to start HTTP server.\n");
    1112           0 :     return 1;
    1113             :   }
    1114             : 
    1115           2 :   if (0 == no_admin)
    1116             :   {
    1117             :     mhd_admin
    1118           2 :       = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG,
    1119             :                           (-1 == fh) ? serve_admin_port : 0,
    1120             :                           NULL, NULL,
    1121             :                           &handle_mhd_admin_request, NULL,
    1122             :                           MHD_OPTION_LISTEN_SOCKET, fh_admin,
    1123             :                           MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL,
    1124             :                           MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL,
    1125             :                           MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout,
    1126             : #if HAVE_DEVELOPER
    1127             :                           MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL,
    1128             : #endif
    1129             :                           MHD_OPTION_END);
    1130           2 :     if (NULL == mhd_admin)
    1131             :     {
    1132           0 :       fprintf (stderr,
    1133             :                "Failed to start administrative HTTP server.\n");
    1134           0 :       MHD_stop_daemon (mhd);
    1135           0 :       return 1;
    1136             :     }
    1137             :   }
    1138             : 
    1139             : #if HAVE_DEVELOPER
    1140           2 :   if (NULL != input_filename)
    1141             :   {
    1142             :     /* run only the testfile input, then terminate */
    1143           0 :     ret = run_fake_client ();
    1144             :   }
    1145             :   else
    1146             :   {
    1147             :     /* normal behavior */
    1148           2 :     ret = TEH_KS_loop ();
    1149             :   }
    1150             : #else
    1151             :   /* normal behavior */
    1152             :   ret = TEH_KS_loop ();
    1153             : #endif
    1154             : 
    1155           2 :   switch (ret)
    1156             :   {
    1157             :   case GNUNET_OK:
    1158             :   case GNUNET_SYSERR:
    1159           2 :     MHD_stop_daemon (mhd);
    1160           2 :     if (NULL != mhd_admin)
    1161           2 :       MHD_stop_daemon (mhd_admin);
    1162           2 :     break;
    1163             :   case GNUNET_NO:
    1164             :     {
    1165           0 :       MHD_socket sock = MHD_quiesce_daemon (mhd);
    1166             :       MHD_socket admin_sock;
    1167           0 :       int admin_sock_opened = GNUNET_NO;
    1168             :       pid_t chld;
    1169             :       int flags;
    1170             : 
    1171             :       /* Set flags to make 'sock' inherited by child */
    1172           0 :       if (NULL != mhd_admin)
    1173             :       {
    1174           0 :         admin_sock = MHD_quiesce_daemon (mhd_admin);
    1175           0 :         admin_sock_opened = GNUNET_YES;
    1176             :       }
    1177           0 :       flags = fcntl (sock, F_GETFD);
    1178           0 :       GNUNET_assert (-1 != flags);
    1179           0 :       flags &= ~FD_CLOEXEC;
    1180           0 :       GNUNET_assert (-1 != fcntl (sock, F_SETFD, flags));
    1181           0 :       chld = fork ();
    1182           0 :       if (-1 == chld)
    1183             :       {
    1184             :         /* fork() failed, continue clean up, unhappily */
    1185           0 :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    1186             :                              "fork");
    1187             :       }
    1188           0 :       if (0 == chld)
    1189             :       {
    1190             :         char pids[12];
    1191             : 
    1192             :         /* exec another taler-exchange-httpd, passing on the listen socket;
    1193             :            as in systemd it is expected to be on FD #3 */
    1194           0 :         if (3 != dup2 (sock, 3))
    1195             :         {
    1196           0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    1197             :                                "dup2");
    1198           0 :           _exit (1);
    1199             :         }
    1200           0 :         if ( (GNUNET_YES == admin_sock_opened) &&
    1201           0 :              (4 != dup2 (admin_sock, 4)) )
    1202             :         {
    1203           0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    1204             :                                "dup2");
    1205           0 :           _exit (1);
    1206             :         }
    1207             :         /* Tell the child that it is the desired recipient for FD #3 */
    1208           0 :         GNUNET_snprintf (pids,
    1209             :                          sizeof (pids),
    1210             :                          "%u",
    1211             :                          getpid ());
    1212           0 :         setenv ("LISTEN_PID", pids, 1);
    1213           0 :         setenv ("LISTEN_FDS", (NULL != mhd_admin) ? "2" : "1", 1);
    1214             :         /* Finally, exec the (presumably) more recent exchange binary */
    1215           0 :         execvp ("taler-exchange-httpd",
    1216             :                 argv);
    1217           0 :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    1218             :                              "execvp");
    1219           0 :         _exit (1);
    1220             :       }
    1221             :       /* we're the original process, handle remaining contextions
    1222             :          before exiting; as the listen socket is no longer used,
    1223             :          close it here */
    1224           0 :       GNUNET_break (0 == close (sock));
    1225           0 :       if (GNUNET_YES == admin_sock_opened)
    1226           0 :         GNUNET_break (0 == close (admin_sock));
    1227           0 :       while ( (0 != MHD_get_daemon_info (mhd,
    1228           0 :                                          MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) ||
    1229           0 :               ( (NULL != mhd_admin) &&
    1230           0 :                 (0 != MHD_get_daemon_info (mhd_admin,
    1231           0 :                                            MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) ) )
    1232           0 :         sleep (1);
    1233             :       /* Now we're really done, practice clean shutdown */
    1234           0 :       MHD_stop_daemon (mhd);
    1235           0 :       if (NULL != mhd_admin)
    1236           0 :         MHD_stop_daemon (mhd_admin);
    1237             :     }
    1238           0 :     break;
    1239             :   default:
    1240           0 :     GNUNET_break (0);
    1241           0 :     MHD_stop_daemon (mhd);
    1242           0 :     if (NULL != mhd_admin)
    1243           0 :       MHD_stop_daemon (mhd_admin);
    1244           0 :     break;
    1245             :   }
    1246           2 :   TALER_EXCHANGEDB_plugin_unload (TEH_plugin);
    1247           2 :   TEH_VALIDATION_done ();
    1248           2 :   return (GNUNET_SYSERR == ret) ? 1 : 0;
    1249             : }
    1250             : 
    1251             : /* end of taler-exchange-httpd.c */

Generated by: LCOV version 1.13