diff options
Diffstat (limited to 'src/lib/daemon_poll.c')
-rw-r--r-- | src/lib/daemon_poll.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/src/lib/daemon_poll.c b/src/lib/daemon_poll.c new file mode 100644 index 00000000..118c0579 --- /dev/null +++ b/src/lib/daemon_poll.c | |||
@@ -0,0 +1,453 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | /** | ||
20 | * @file lib/daemon_poll.c | ||
21 | * @brief functions to run poll-based event loop | ||
22 | * @author Christian Grothoff | ||
23 | */ | ||
24 | #include "internal.h" | ||
25 | #include "daemon_poll.h" | ||
26 | |||
27 | #ifdef HAVE_POLL | ||
28 | |||
29 | |||
30 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
31 | |||
32 | /** | ||
33 | * Set required 'event' members in 'pollfd' elements, | ||
34 | * assuming that @a p[0].fd is MHD side of socketpair | ||
35 | * and @a p[1].fd is TLS connected socket. | ||
36 | * | ||
37 | * @param urh upgrade handle to watch for | ||
38 | * @param p pollfd array to update | ||
39 | */ | ||
40 | static void | ||
41 | urh_update_pollfd (struct MHD_UpgradeResponseHandle *urh, | ||
42 | struct pollfd p[2]) | ||
43 | { | ||
44 | p[0].events = 0; | ||
45 | p[1].events = 0; | ||
46 | |||
47 | if (urh->in_buffer_used < urh->in_buffer_size) | ||
48 | p[0].events |= POLLIN; | ||
49 | if (0 != urh->out_buffer_used) | ||
50 | p[0].events |= POLLOUT; | ||
51 | |||
52 | /* Do not monitor again for errors if error was detected before as | ||
53 | * error state is remembered. */ | ||
54 | if ((0 == (urh->app.celi & MHD_EPOLL_STATE_ERROR)) && | ||
55 | ((0 != urh->in_buffer_size) || | ||
56 | (0 != urh->out_buffer_size) || | ||
57 | (0 != urh->out_buffer_used))) | ||
58 | p[0].events |= MHD_POLL_EVENTS_ERR_DISC; | ||
59 | |||
60 | if (urh->out_buffer_used < urh->out_buffer_size) | ||
61 | p[1].events |= POLLIN; | ||
62 | if (0 != urh->in_buffer_used) | ||
63 | p[1].events |= POLLOUT; | ||
64 | |||
65 | /* Do not monitor again for errors if error was detected before as | ||
66 | * error state is remembered. */ | ||
67 | if ((0 == (urh->mhd.celi & MHD_EPOLL_STATE_ERROR)) && | ||
68 | ((0 != urh->out_buffer_size) || | ||
69 | (0 != urh->in_buffer_size) || | ||
70 | (0 != urh->in_buffer_used))) | ||
71 | p[1].events |= MHD_POLL_EVENTS_ERR_DISC; | ||
72 | } | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Set @a p to watch for @a urh. | ||
77 | * | ||
78 | * @param urh upgrade handle to watch for | ||
79 | * @param p pollfd array to set | ||
80 | */ | ||
81 | static void | ||
82 | urh_to_pollfd (struct MHD_UpgradeResponseHandle *urh, | ||
83 | struct pollfd p[2]) | ||
84 | { | ||
85 | p[0].fd = urh->connection->socket_fd; | ||
86 | p[1].fd = urh->mhd.socket; | ||
87 | urh_update_pollfd (urh, | ||
88 | p); | ||
89 | } | ||
90 | |||
91 | |||
92 | /** | ||
93 | * Update ready state in @a urh based on pollfd. | ||
94 | * @param urh upgrade handle to update | ||
95 | * @param p 'poll()' processed pollfd. | ||
96 | */ | ||
97 | static void | ||
98 | urh_from_pollfd (struct MHD_UpgradeResponseHandle *urh, | ||
99 | struct pollfd p[2]) | ||
100 | { | ||
101 | /* Reset read/write ready, preserve error state. */ | ||
102 | urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); | ||
103 | urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); | ||
104 | |||
105 | if (0 != (p[0].revents & POLLIN)) | ||
106 | urh->app.celi |= MHD_EPOLL_STATE_READ_READY; | ||
107 | if (0 != (p[0].revents & POLLOUT)) | ||
108 | urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY; | ||
109 | if (0 != (p[0].revents & POLLHUP)) | ||
110 | urh->app.celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; | ||
111 | if (0 != (p[0].revents & MHD_POLL_REVENTS_ERRROR)) | ||
112 | urh->app.celi |= MHD_EPOLL_STATE_ERROR; | ||
113 | if (0 != (p[1].revents & POLLIN)) | ||
114 | urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; | ||
115 | if (0 != (p[1].revents & POLLOUT)) | ||
116 | urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY; | ||
117 | if (0 != (p[1].revents & POLLHUP)) | ||
118 | urh->mhd.celi |= MHD_EPOLL_STATE_ERROR; | ||
119 | if (0 != (p[1].revents & MHD_POLL_REVENTS_ERRROR)) | ||
120 | urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; | ||
121 | } | ||
122 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Process all of our connections and possibly the server | ||
127 | * socket using poll(). | ||
128 | * | ||
129 | * @param daemon daemon to run poll loop for | ||
130 | * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking | ||
131 | * @return #MHD_SC_OK on success | ||
132 | */ | ||
133 | enum MHD_StatusCode | ||
134 | MHD_daemon_poll_all_ (struct MHD_Daemon *daemon, | ||
135 | bool may_block) | ||
136 | { | ||
137 | unsigned int num_connections; | ||
138 | struct MHD_Connection *pos; | ||
139 | struct MHD_Connection *prev; | ||
140 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
141 | struct MHD_UpgradeResponseHandle *urh; | ||
142 | struct MHD_UpgradeResponseHandle *urhn; | ||
143 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
144 | |||
145 | if ( (! daemon->disallow_suspend_resume) && | ||
146 | (MHD_YES == resume_suspended_connections (daemon)) ) | ||
147 | may_block = false; | ||
148 | |||
149 | /* count number of connections and thus determine poll set size */ | ||
150 | num_connections = 0; | ||
151 | for (pos = daemon->connections_head; NULL != pos; pos = pos->next) | ||
152 | num_connections++; | ||
153 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
154 | for (urh = daemon->urh_head; NULL != urh; urh = urh->next) | ||
155 | num_connections += 2; | ||
156 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
157 | { | ||
158 | MHD_UNSIGNED_LONG_LONG ltimeout; | ||
159 | unsigned int i; | ||
160 | int timeout; | ||
161 | unsigned int poll_server; | ||
162 | int poll_listen; | ||
163 | int poll_itc_idx; | ||
164 | struct pollfd *p; | ||
165 | MHD_socket ls; | ||
166 | |||
167 | p = MHD_calloc_ ((2 + num_connections), | ||
168 | sizeof (struct pollfd)); | ||
169 | if (NULL == p) | ||
170 | { | ||
171 | #ifdef HAVE_MESSAGES | ||
172 | MHD_DLOG (daemon, | ||
173 | MHD_SC_POLL_MALLOC_FAILURE, | ||
174 | _("Error allocating memory: %s\n"), | ||
175 | MHD_strerror_(errno)); | ||
176 | #endif | ||
177 | return MHD_SC_POLL_MALLOC_FAILURE; | ||
178 | } | ||
179 | poll_server = 0; | ||
180 | poll_listen = -1; | ||
181 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
182 | (! daemon->was_quiesced) && | ||
183 | (daemon->connections < daemon->global_connection_limit) && | ||
184 | (! daemon->at_limit) ) | ||
185 | { | ||
186 | /* only listen if we are not at the connection limit */ | ||
187 | p[poll_server].fd = ls; | ||
188 | p[poll_server].events = POLLIN; | ||
189 | p[poll_server].revents = 0; | ||
190 | poll_listen = (int) poll_server; | ||
191 | poll_server++; | ||
192 | } | ||
193 | poll_itc_idx = -1; | ||
194 | if (MHD_ITC_IS_VALID_(daemon->itc)) | ||
195 | { | ||
196 | p[poll_server].fd = MHD_itc_r_fd_ (daemon->itc); | ||
197 | p[poll_server].events = POLLIN; | ||
198 | p[poll_server].revents = 0; | ||
199 | poll_itc_idx = (int) poll_server; | ||
200 | poll_server++; | ||
201 | } | ||
202 | if (! may_block) | ||
203 | timeout = 0; | ||
204 | else if ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model) || | ||
205 | (MHD_SC_OK != /* FIXME: distinguish between NO_TIMEOUT and errors! */ | ||
206 | MHD_daemon_get_timeout (daemon, | ||
207 | <imeout)) ) | ||
208 | timeout = -1; | ||
209 | else | ||
210 | timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout; | ||
211 | |||
212 | i = 0; | ||
213 | for (pos = daemon->connections_tail; NULL != pos; pos = pos->prev) | ||
214 | { | ||
215 | p[poll_server+i].fd = pos->socket_fd; | ||
216 | switch (pos->request.event_loop_info) | ||
217 | { | ||
218 | case MHD_EVENT_LOOP_INFO_READ: | ||
219 | p[poll_server+i].events |= POLLIN | MHD_POLL_EVENTS_ERR_DISC; | ||
220 | break; | ||
221 | case MHD_EVENT_LOOP_INFO_WRITE: | ||
222 | p[poll_server+i].events |= POLLOUT | MHD_POLL_EVENTS_ERR_DISC; | ||
223 | break; | ||
224 | case MHD_EVENT_LOOP_INFO_BLOCK: | ||
225 | p[poll_server+i].events |= MHD_POLL_EVENTS_ERR_DISC; | ||
226 | break; | ||
227 | case MHD_EVENT_LOOP_INFO_CLEANUP: | ||
228 | timeout = 0; /* clean up "pos" immediately */ | ||
229 | break; | ||
230 | } | ||
231 | i++; | ||
232 | } | ||
233 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
234 | for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev) | ||
235 | { | ||
236 | urh_to_pollfd (urh, | ||
237 | &(p[poll_server+i])); | ||
238 | i += 2; | ||
239 | } | ||
240 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
241 | if (0 == poll_server + num_connections) | ||
242 | { | ||
243 | free (p); | ||
244 | return MHD_SC_OK; | ||
245 | } | ||
246 | if (MHD_sys_poll_(p, | ||
247 | poll_server + num_connections, | ||
248 | timeout) < 0) | ||
249 | { | ||
250 | const int err = MHD_socket_get_error_ (); | ||
251 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
252 | { | ||
253 | free(p); | ||
254 | return MHD_SC_OK; | ||
255 | } | ||
256 | #ifdef HAVE_MESSAGES | ||
257 | MHD_DLOG (daemon, | ||
258 | MHD_SC_UNEXPECTED_POLL_ERROR, | ||
259 | _("poll failed: %s\n"), | ||
260 | MHD_socket_strerr_ (err)); | ||
261 | #endif | ||
262 | free(p); | ||
263 | return MHD_SC_UNEXPECTED_POLL_ERROR; | ||
264 | } | ||
265 | |||
266 | /* Reset. New value will be set when connections are processed. */ | ||
267 | daemon->data_already_pending = false; | ||
268 | |||
269 | /* handle ITC FD */ | ||
270 | /* do it before any other processing so | ||
271 | new signals will be processed in next loop */ | ||
272 | if ( (-1 != poll_itc_idx) && | ||
273 | (0 != (p[poll_itc_idx].revents & POLLIN)) ) | ||
274 | MHD_itc_clear_ (daemon->itc); | ||
275 | |||
276 | /* handle shutdown */ | ||
277 | if (daemon->shutdown) | ||
278 | { | ||
279 | free(p); | ||
280 | return MHD_NO; | ||
281 | } | ||
282 | i = 0; | ||
283 | prev = daemon->connections_tail; | ||
284 | while (NULL != (pos = prev)) | ||
285 | { | ||
286 | prev = pos->prev; | ||
287 | /* first, sanity checks */ | ||
288 | if (i >= num_connections) | ||
289 | break; /* connection list changed somehow, retry later ... */ | ||
290 | if (p[poll_server+i].fd != pos->socket_fd) | ||
291 | continue; /* fd mismatch, something else happened, retry later ... */ | ||
292 | call_handlers (pos, | ||
293 | 0 != (p[poll_server+i].revents & POLLIN), | ||
294 | 0 != (p[poll_server+i].revents & POLLOUT), | ||
295 | 0 != (p[poll_server+i].revents & MHD_POLL_REVENTS_ERR_DISC)); | ||
296 | i++; | ||
297 | } | ||
298 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
299 | for (urh = daemon->urh_tail; NULL != urh; urh = urhn) | ||
300 | { | ||
301 | if (i >= num_connections) | ||
302 | break; /* connection list changed somehow, retry later ... */ | ||
303 | |||
304 | /* Get next connection here as connection can be removed | ||
305 | * from 'daemon->urh_head' list. */ | ||
306 | urhn = urh->prev; | ||
307 | /* Check for fd mismatch. FIXME: required for safety? */ | ||
308 | if ((p[poll_server+i].fd != urh->connection->socket_fd) || | ||
309 | (p[poll_server+i+1].fd != urh->mhd.socket)) | ||
310 | break; | ||
311 | urh_from_pollfd (urh, | ||
312 | &(p[poll_server+i])); | ||
313 | i += 2; | ||
314 | process_urh (urh); | ||
315 | /* Finished forwarding? */ | ||
316 | if ( (0 == urh->in_buffer_size) && | ||
317 | (0 == urh->out_buffer_size) && | ||
318 | (0 == urh->in_buffer_used) && | ||
319 | (0 == urh->out_buffer_used) ) | ||
320 | { | ||
321 | /* MHD_connection_finish_forward_() will remove connection from | ||
322 | * 'daemon->urh_head' list. */ | ||
323 | MHD_connection_finish_forward_ (urh->connection); | ||
324 | urh->clean_ready = true; | ||
325 | /* If 'urh->was_closed' already was set to true, connection will be | ||
326 | * moved immediately to cleanup list. Otherwise connection | ||
327 | * will stay in suspended list until 'urh' will be marked | ||
328 | * with 'was_closed' by application. */ | ||
329 | MHD_resume_connection(urh->connection); | ||
330 | } | ||
331 | } | ||
332 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
333 | /* handle 'listen' FD */ | ||
334 | if ( (-1 != poll_listen) && | ||
335 | (0 != (p[poll_listen].revents & POLLIN)) ) | ||
336 | (void) MHD_accept_connection (daemon); | ||
337 | |||
338 | free(p); | ||
339 | } | ||
340 | return MHD_YES; | ||
341 | } | ||
342 | |||
343 | |||
344 | /** | ||
345 | * Process only the listen socket using poll(). | ||
346 | * | ||
347 | * @param daemon daemon to run poll loop for | ||
348 | * @param may_block true if blocking, false if non-blocking | ||
349 | * @return #MHD_SC_OK on success | ||
350 | */ | ||
351 | enum MHD_StatusCode | ||
352 | MHD_daemon_poll_listen_socket_ (struct MHD_Daemon *daemon, | ||
353 | bool may_block) | ||
354 | { | ||
355 | struct pollfd p[2]; | ||
356 | int timeout; | ||
357 | unsigned int poll_count; | ||
358 | int poll_listen; | ||
359 | int poll_itc_idx; | ||
360 | MHD_socket ls; | ||
361 | |||
362 | memset (&p, | ||
363 | 0, | ||
364 | sizeof (p)); | ||
365 | poll_count = 0; | ||
366 | poll_listen = -1; | ||
367 | poll_itc_idx = -1; | ||
368 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
369 | (! daemon->was_quiesced) ) | ||
370 | |||
371 | { | ||
372 | p[poll_count].fd = ls; | ||
373 | p[poll_count].events = POLLIN; | ||
374 | p[poll_count].revents = 0; | ||
375 | poll_listen = poll_count; | ||
376 | poll_count++; | ||
377 | } | ||
378 | if (MHD_ITC_IS_VALID_(daemon->itc)) | ||
379 | { | ||
380 | p[poll_count].fd = MHD_itc_r_fd_ (daemon->itc); | ||
381 | p[poll_count].events = POLLIN; | ||
382 | p[poll_count].revents = 0; | ||
383 | poll_itc_idx = poll_count; | ||
384 | poll_count++; | ||
385 | } | ||
386 | |||
387 | if (! daemon->disallow_suspend_resume) | ||
388 | (void) resume_suspended_connections (daemon); | ||
389 | |||
390 | if (! may_block) | ||
391 | timeout = 0; | ||
392 | else | ||
393 | timeout = -1; | ||
394 | if (0 == poll_count) | ||
395 | return MHD_SC_OK; | ||
396 | if (MHD_sys_poll_(p, | ||
397 | poll_count, | ||
398 | timeout) < 0) | ||
399 | { | ||
400 | const int err = MHD_socket_get_error_ (); | ||
401 | |||
402 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
403 | return MHD_SC_OK; | ||
404 | #ifdef HAVE_MESSAGES | ||
405 | MHD_DLOG (daemon, | ||
406 | MHD_SC_UNEXPECTED_POLL_ERROR, | ||
407 | _("poll failed: %s\n"), | ||
408 | MHD_socket_strerr_ (err)); | ||
409 | #endif | ||
410 | return MHD_SC_UNEXPECTED_POLL_ERROR; | ||
411 | } | ||
412 | if ( (-1 != poll_itc_idx) && | ||
413 | (0 != (p[poll_itc_idx].revents & POLLIN)) ) | ||
414 | MHD_itc_clear_ (daemon->itc); | ||
415 | |||
416 | /* handle shutdown */ | ||
417 | if (daemon->shutdown) | ||
418 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
419 | if ( (-1 != poll_listen) && | ||
420 | (0 != (p[poll_listen].revents & POLLIN)) ) | ||
421 | (void) MHD_accept_connection (daemon); | ||
422 | return MHD_SC_OK; | ||
423 | } | ||
424 | #endif | ||
425 | |||
426 | |||
427 | /** | ||
428 | * Do poll()-based processing. | ||
429 | * | ||
430 | * @param daemon daemon to run poll()-loop for | ||
431 | * @param may_block true if blocking, false if non-blocking | ||
432 | * @return #MHD_SC_OK on success | ||
433 | */ | ||
434 | enum MHD_StatusCode | ||
435 | MHD_daemon_poll_ (struct MHD_Daemon *daemon, | ||
436 | bool may_block) | ||
437 | { | ||
438 | #ifdef HAVE_POLL | ||
439 | if (daemon->shutdown) | ||
440 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
441 | if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) | ||
442 | return MHD_daemon_poll_all_ (daemon, | ||
443 | may_block); | ||
444 | return MHD_daemon_poll_listen_socket_ (daemon, | ||
445 | may_block); | ||
446 | #else | ||
447 | /* This code should be dead, as we should have checked | ||
448 | this earlier... */ | ||
449 | return MHD_SC_POLL_NOT_SUPPORTED; | ||
450 | #endif | ||
451 | } | ||
452 | |||
453 | /* end of daemon_poll.c */ | ||