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