LCOV - code coverage report
Current view: top level - mhd - mhd_run.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 80.2 % 81 65
Test Date: 2026-10-12 22:34:41 Functions: 87.5 % 8 7

            Line data    Source code
       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         3286 : run_daemon (void *cls)
      94              : {
      95         3286 :   struct DaemonEntry *de = cls;
      96              : 
      97         3286 :   de->mhd_task = NULL;
      98              :   do {
      99         3286 :     de->triggered = false;
     100         3286 :     GNUNET_assert (MHD_YES ==
     101              :                    MHD_run (de->mhd));
     102         3286 :   } while (de->triggered);
     103         3286 :   prepare_daemon (de);
     104         3286 : }
     105              : 
     106              : 
     107              : static void
     108         3336 : 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        56712 :   FD_ZERO (&rs);
     121        56712 :   FD_ZERO (&ws);
     122        56712 :   FD_ZERO (&es);
     123         3336 :   wrs = GNUNET_NETWORK_fdset_create ();
     124         3336 :   wws = GNUNET_NETWORK_fdset_create ();
     125         3336 :   max = -1;
     126         3336 :   GNUNET_assert (MHD_YES ==
     127              :                  MHD_get_fdset (de->mhd,
     128              :                                 &rs,
     129              :                                 &ws,
     130              :                                 &es,
     131              :                                 &max));
     132         3336 :   haveto = MHD_get_timeout (de->mhd,
     133              :                             &timeout);
     134         3336 :   if (haveto == MHD_YES)
     135         2287 :     tv = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
     136              :                                         timeout);
     137              :   else
     138         1049 :     tv = GNUNET_TIME_UNIT_FOREVER_REL;
     139         3336 :   GNUNET_NETWORK_fdset_copy_native (wrs,
     140              :                                     &rs,
     141              :                                     max + 1);
     142         3336 :   GNUNET_NETWORK_fdset_copy_native (wws,
     143              :                                     &ws,
     144              :                                     max + 1);
     145              :   de->mhd_task
     146         3336 :     = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
     147              :                                    tv,
     148              :                                    wrs,
     149              :                                    wws,
     150              :                                    &run_daemon,
     151              :                                    de);
     152         3336 :   GNUNET_NETWORK_fdset_destroy (wrs);
     153         3336 :   GNUNET_NETWORK_fdset_destroy (wws);
     154         3336 : }
     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           50 : limit_descriptors (void)
     174              : {
     175              :   struct rlimit rl;
     176              : 
     177           50 :   if (0 != getrlimit (RLIMIT_NOFILE,
     178              :                       &rl))
     179              :   {
     180            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     181              :                          "getrlimit");
     182           25 :     return;
     183              :   }
     184           50 :   if (rl.rlim_cur <= FD_SETSIZE)
     185           25 :     return;
     186           25 :   rl.rlim_cur = FD_SETSIZE;
     187           25 :   if (0 != setrlimit (RLIMIT_NOFILE,
     188              :                       &rl))
     189            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     190              :                          "setrlimit");
     191              : }
     192              : 
     193              : 
     194              : void
     195           50 : TALER_MHD_daemon_start (struct MHD_Daemon *daemon)
     196              : {
     197              :   struct DaemonEntry *de;
     198              : 
     199           50 :   limit_descriptors ();
     200           50 :   de = GNUNET_new (struct DaemonEntry);
     201           50 :   de->mhd = daemon;
     202           50 :   GNUNET_CONTAINER_DLL_insert (mhd_head,
     203              :                                mhd_tail,
     204              :                                de);
     205           50 :   prepare_daemon (de);
     206           50 : }
     207              : 
     208              : 
     209              : void
     210           25 : TALER_MHD_daemons_halt (void)
     211              : {
     212           25 :   for (struct DaemonEntry *de = mhd_head;
     213           75 :        NULL != de;
     214           50 :        de = de->next)
     215              :   {
     216           50 :     if (NULL != de->mhd_task)
     217              :     {
     218           50 :       GNUNET_SCHEDULER_cancel (de->mhd_task);
     219           50 :       de->mhd_task = NULL;
     220              :     }
     221           50 :     de->triggered = false;
     222              :   }
     223           25 : }
     224              : 
     225              : 
     226              : void
     227            0 : TALER_MHD_daemons_quiesce (void)
     228              : {
     229            0 :   for (struct DaemonEntry *de = mhd_head;
     230            0 :        NULL != de;
     231            0 :        de = de->next)
     232              :   {
     233              :     int fd;
     234              : 
     235            0 :     if (NULL != de->mhd_task)
     236              :     {
     237            0 :       GNUNET_SCHEDULER_cancel (de->mhd_task);
     238            0 :       de->mhd_task = NULL;
     239              :     }
     240            0 :     de->triggered = false;
     241            0 :     fd = MHD_quiesce_daemon  (de->mhd);
     242            0 :     GNUNET_break (0 == close (fd));
     243              :   }
     244            0 : }
     245              : 
     246              : 
     247              : void
     248           25 : TALER_MHD_daemons_destroy (void)
     249              : {
     250              :   struct DaemonEntry *de;
     251              : 
     252           75 :   while (NULL != (de = mhd_head))
     253              :   {
     254           50 :     struct MHD_Daemon *mhd = de->mhd;
     255              : 
     256           50 :     if (NULL != de->mhd_task)
     257              :     {
     258            0 :       GNUNET_SCHEDULER_cancel (de->mhd_task);
     259            0 :       de->mhd_task = NULL;
     260              :     }
     261           50 :     MHD_stop_daemon (mhd);
     262           50 :     GNUNET_CONTAINER_DLL_remove (mhd_head,
     263              :                                  mhd_tail,
     264              :                                  de);
     265           50 :     GNUNET_free (de);
     266              :   }
     267           25 : }
     268              : 
     269              : 
     270              : void
     271          265 : TALER_MHD_daemon_trigger (void)
     272              : {
     273          265 :   for (struct DaemonEntry *de = mhd_head;
     274          795 :        NULL != de;
     275          530 :        de = de->next)
     276              :   {
     277          530 :     if (NULL != de->mhd_task)
     278              :     {
     279          530 :       GNUNET_SCHEDULER_cancel (de->mhd_task);
     280          530 :       de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon,
     281              :                                                de);
     282              :     }
     283              :     else
     284              :     {
     285            0 :       de->triggered = true;
     286              :     }
     287              :   }
     288          265 : }
     289              : 
     290              : 
     291              : /* end of mhd_run.c */
        

Generated by: LCOV version 2.0-1