exchange

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

mhd_run.c (6801B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2019-2025 Taler Systems SA
      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  * @file mhd_run.c
     18  * @brief API for running an MHD daemon with the
     19  *        GNUnet scheduler
     20  * @author Christian Grothoff
     21  */
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <gnunet/gnunet_json_lib.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h>
     26 #include <errno.h>
     27 #include <sys/resource.h>
     28 #include "taler/taler_util.h"
     29 #include "taler/taler_mhd_lib.h"
     30 
     31 
     32 /**
     33  * Entry in list of HTTP servers we are running.
     34  */
     35 struct DaemonEntry
     36 {
     37   /**
     38    * Kept in a DLL.
     39    */
     40   struct DaemonEntry *next;
     41 
     42   /**
     43    * Kept in a DLL.
     44    */
     45   struct DaemonEntry *prev;
     46 
     47   /**
     48    * The actual daemon.
     49    */
     50   struct MHD_Daemon *mhd;
     51 
     52   /**
     53    * Task running the HTTP server.
     54    */
     55   struct GNUNET_SCHEDULER_Task *mhd_task;
     56 
     57   /**
     58    * Set to true if we should immediately MHD_run() again.
     59    */
     60   bool triggered;
     61 
     62 };
     63 
     64 
     65 /**
     66  * Head of list of HTTP servers.
     67  */
     68 static struct DaemonEntry *mhd_head;
     69 
     70 /**
     71  * Tail of list of HTTP servers.
     72  */
     73 static struct DaemonEntry *mhd_tail;
     74 
     75 
     76 /**
     77  * Function that queries MHD's select sets and
     78  * starts the task waiting for them.
     79  *
     80  * @param[in,out] de daemon to start tasks for
     81  */
     82 static void
     83 prepare_daemon (struct DaemonEntry *de);
     84 
     85 
     86 /**
     87  * Call MHD to process pending requests and then go back
     88  * and schedule the next run.
     89  *
     90  * @param cls our `struct DaemonEntry *`
     91  */
     92 static void
     93 run_daemon (void *cls)
     94 {
     95   struct DaemonEntry *de = cls;
     96 
     97   de->mhd_task = NULL;
     98   do {
     99     de->triggered = false;
    100     GNUNET_assert (MHD_YES ==
    101                    MHD_run (de->mhd));
    102   } while (de->triggered);
    103   prepare_daemon (de);
    104 }
    105 
    106 
    107 static void
    108 prepare_daemon (struct DaemonEntry *de)
    109 {
    110   fd_set rs;
    111   fd_set ws;
    112   fd_set es;
    113   struct GNUNET_NETWORK_FDSet *wrs;
    114   struct GNUNET_NETWORK_FDSet *wws;
    115   int max;
    116   MHD_UNSIGNED_LONG_LONG timeout;
    117   int haveto;
    118   struct GNUNET_TIME_Relative tv;
    119 
    120   FD_ZERO (&rs);
    121   FD_ZERO (&ws);
    122   FD_ZERO (&es);
    123   wrs = GNUNET_NETWORK_fdset_create ();
    124   wws = GNUNET_NETWORK_fdset_create ();
    125   max = -1;
    126   GNUNET_assert (MHD_YES ==
    127                  MHD_get_fdset (de->mhd,
    128                                 &rs,
    129                                 &ws,
    130                                 &es,
    131                                 &max));
    132   haveto = MHD_get_timeout (de->mhd,
    133                             &timeout);
    134   if (haveto == MHD_YES)
    135     tv = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
    136                                         timeout);
    137   else
    138     tv = GNUNET_TIME_UNIT_FOREVER_REL;
    139   GNUNET_NETWORK_fdset_copy_native (wrs,
    140                                     &rs,
    141                                     max + 1);
    142   GNUNET_NETWORK_fdset_copy_native (wws,
    143                                     &ws,
    144                                     max + 1);
    145   de->mhd_task
    146     = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
    147                                    tv,
    148                                    wrs,
    149                                    wws,
    150                                    &run_daemon,
    151                                    de);
    152   GNUNET_NETWORK_fdset_destroy (wrs);
    153   GNUNET_NETWORK_fdset_destroy (wws);
    154 }
    155 
    156 
    157 /**
    158  * Lower the soft limit on open file descriptors to FD_SETSIZE.
    159  *
    160  * Our event loop is select()-based: neither MHD (as we run it) nor
    161  * the GNUnet scheduler can wait on a descriptor numbered FD_SETSIZE
    162  * or higher.  MHD closes connections it accepts on such descriptors,
    163  * the scheduler aborts on an assertion, and libcurl silently stops
    164  * waiting on them.  With the soft limit at FD_SETSIZE, creating such
    165  * a descriptor fails with EMFILE instead: MHD then stops accepting
    166  * until a connection closes, and elsewhere just that operation fails.
    167  *
    168  * FIXME: omit this on platforms with epoll() support once we have
    169  * migrated to MHD2 -- provided that by then the GNUnet scheduler no
    170  * longer select()s on our other descriptors either.
    171  */
    172 static void
    173 limit_descriptors (void)
    174 {
    175   struct rlimit rl;
    176 
    177   if (0 != getrlimit (RLIMIT_NOFILE,
    178                       &rl))
    179   {
    180     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    181                          "getrlimit");
    182     return;
    183   }
    184   if (rl.rlim_cur <= FD_SETSIZE)
    185     return;
    186   rl.rlim_cur = FD_SETSIZE;
    187   if (0 != setrlimit (RLIMIT_NOFILE,
    188                       &rl))
    189     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    190                          "setrlimit");
    191 }
    192 
    193 
    194 void
    195 TALER_MHD_daemon_start (struct MHD_Daemon *daemon)
    196 {
    197   struct DaemonEntry *de;
    198 
    199   limit_descriptors ();
    200   de = GNUNET_new (struct DaemonEntry);
    201   de->mhd = daemon;
    202   GNUNET_CONTAINER_DLL_insert (mhd_head,
    203                                mhd_tail,
    204                                de);
    205   prepare_daemon (de);
    206 }
    207 
    208 
    209 void
    210 TALER_MHD_daemons_halt (void)
    211 {
    212   for (struct DaemonEntry *de = mhd_head;
    213        NULL != de;
    214        de = de->next)
    215   {
    216     if (NULL != de->mhd_task)
    217     {
    218       GNUNET_SCHEDULER_cancel (de->mhd_task);
    219       de->mhd_task = NULL;
    220     }
    221     de->triggered = false;
    222   }
    223 }
    224 
    225 
    226 void
    227 TALER_MHD_daemons_quiesce (void)
    228 {
    229   for (struct DaemonEntry *de = mhd_head;
    230        NULL != de;
    231        de = de->next)
    232   {
    233     int fd;
    234 
    235     if (NULL != de->mhd_task)
    236     {
    237       GNUNET_SCHEDULER_cancel (de->mhd_task);
    238       de->mhd_task = NULL;
    239     }
    240     de->triggered = false;
    241     fd = MHD_quiesce_daemon  (de->mhd);
    242     GNUNET_break (0 == close (fd));
    243   }
    244 }
    245 
    246 
    247 void
    248 TALER_MHD_daemons_destroy (void)
    249 {
    250   struct DaemonEntry *de;
    251 
    252   while (NULL != (de = mhd_head))
    253   {
    254     struct MHD_Daemon *mhd = de->mhd;
    255 
    256     if (NULL != de->mhd_task)
    257     {
    258       GNUNET_SCHEDULER_cancel (de->mhd_task);
    259       de->mhd_task = NULL;
    260     }
    261     MHD_stop_daemon (mhd);
    262     GNUNET_CONTAINER_DLL_remove (mhd_head,
    263                                  mhd_tail,
    264                                  de);
    265     GNUNET_free (de);
    266   }
    267 }
    268 
    269 
    270 void
    271 TALER_MHD_daemon_trigger (void)
    272 {
    273   for (struct DaemonEntry *de = mhd_head;
    274        NULL != de;
    275        de = de->next)
    276   {
    277     if (NULL != de->mhd_task)
    278     {
    279       GNUNET_SCHEDULER_cancel (de->mhd_task);
    280       de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
    281                                                de);
    282     }
    283     else
    284     {
    285       de->triggered = true;
    286     }
    287   }
    288 }
    289 
    290 
    291 /* end of mhd_run.c */