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 */