diff options
Diffstat (limited to 'src/lib/daemon_select.c')
-rw-r--r-- | src/lib/daemon_select.c | 711 |
1 files changed, 711 insertions, 0 deletions
diff --git a/src/lib/daemon_select.c b/src/lib/daemon_select.c new file mode 100644 index 00000000..f61ba36a --- /dev/null +++ b/src/lib/daemon_select.c | |||
@@ -0,0 +1,711 @@ | |||
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 | /** | ||
21 | * @file lib/daemon_select.c | ||
22 | * @brief function to run select()-based event loop of a daemon | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "internal.h" | ||
26 | #include "daemon_select.h" | ||
27 | |||
28 | /** | ||
29 | * We defined a macro with the same name as a function we | ||
30 | * are implementing here. Need to undef the macro to avoid | ||
31 | * causing a conflict. | ||
32 | */ | ||
33 | #undef MHD_daemon_get_fdset | ||
34 | |||
35 | /** | ||
36 | * Obtain the `select()` sets for this daemon. Daemon's FDs will be | ||
37 | * added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO | ||
38 | * for each fd_set before calling this function. FD_SETSIZE is assumed | ||
39 | * to be platform's default. | ||
40 | * | ||
41 | * This function should only be called in when MHD is configured to | ||
42 | * use external select with 'select()' or with 'epoll'. In the latter | ||
43 | * case, it will only add the single 'epoll()' file descriptor used by | ||
44 | * MHD to the sets. It's necessary to use #MHD_daemon_get_timeout() in | ||
45 | * combination with this function. | ||
46 | * | ||
47 | * This function must be called only for daemon started without | ||
48 | * #MHD_USE_INTERNAL_POLLING_THREAD flag. | ||
49 | * | ||
50 | * @param daemon daemon to get sets from | ||
51 | * @param read_fd_set read set | ||
52 | * @param write_fd_set write set | ||
53 | * @param except_fd_set except set | ||
54 | * @param max_fd increased to largest FD added (if larger | ||
55 | * than existing value); can be NULL | ||
56 | * @return #MHD_SC_OK on success, otherwise error code | ||
57 | * @ingroup event | ||
58 | */ | ||
59 | enum MHD_StatusCode | ||
60 | MHD_daemon_get_fdset (struct MHD_Daemon *daemon, | ||
61 | fd_set *read_fd_set, | ||
62 | fd_set *write_fd_set, | ||
63 | fd_set *except_fd_set, | ||
64 | MHD_socket *max_fd) | ||
65 | { | ||
66 | return MHD_daemon_get_fdset2 (daemon, | ||
67 | read_fd_set, | ||
68 | write_fd_set, | ||
69 | except_fd_set, | ||
70 | max_fd, | ||
71 | _MHD_SYS_DEFAULT_FD_SETSIZE); | ||
72 | } | ||
73 | |||
74 | |||
75 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
76 | /** | ||
77 | * Obtain the select() file descriptor sets for the | ||
78 | * given @a urh. | ||
79 | * | ||
80 | * @param urh upgrade handle to wait for | ||
81 | * @param[out] rs read set to initialize | ||
82 | * @param[out] ws write set to initialize | ||
83 | * @param[out] es except set to initialize | ||
84 | * @param[out] max_fd maximum FD to update | ||
85 | * @param fd_setsize value of FD_SETSIZE | ||
86 | * @return true on success, false on error | ||
87 | */ | ||
88 | static bool | ||
89 | urh_to_fdset (struct MHD_UpgradeResponseHandle *urh, | ||
90 | fd_set *rs, | ||
91 | fd_set *ws, | ||
92 | fd_set *es, | ||
93 | MHD_socket *max_fd, | ||
94 | unsigned int fd_setsize) | ||
95 | { | ||
96 | const MHD_socket conn_sckt = urh->connection->socket_fd; | ||
97 | const MHD_socket mhd_sckt = urh->mhd.socket; | ||
98 | bool res = true; | ||
99 | |||
100 | /* Do not add to 'es' only if socket is closed | ||
101 | * or not used anymore. */ | ||
102 | if (MHD_INVALID_SOCKET != conn_sckt) | ||
103 | { | ||
104 | if ( (urh->in_buffer_used < urh->in_buffer_size) && | ||
105 | (! MHD_add_to_fd_set_ (conn_sckt, | ||
106 | rs, | ||
107 | max_fd, | ||
108 | fd_setsize)) ) | ||
109 | res = false; | ||
110 | if ( (0 != urh->out_buffer_used) && | ||
111 | (! MHD_add_to_fd_set_ (conn_sckt, | ||
112 | ws, | ||
113 | max_fd, | ||
114 | fd_setsize)) ) | ||
115 | res = false; | ||
116 | /* Do not monitor again for errors if error was detected before as | ||
117 | * error state is remembered. */ | ||
118 | if ((0 == (urh->app.celi & MHD_EPOLL_STATE_ERROR)) && | ||
119 | ((0 != urh->in_buffer_size) || | ||
120 | (0 != urh->out_buffer_size) || | ||
121 | (0 != urh->out_buffer_used))) | ||
122 | MHD_add_to_fd_set_ (conn_sckt, | ||
123 | es, | ||
124 | max_fd, | ||
125 | fd_setsize); | ||
126 | } | ||
127 | if (MHD_INVALID_SOCKET != mhd_sckt) | ||
128 | { | ||
129 | if ( (urh->out_buffer_used < urh->out_buffer_size) && | ||
130 | (! MHD_add_to_fd_set_ (mhd_sckt, | ||
131 | rs, | ||
132 | max_fd, | ||
133 | fd_setsize)) ) | ||
134 | res = false; | ||
135 | if ( (0 != urh->in_buffer_used) && | ||
136 | (! MHD_add_to_fd_set_ (mhd_sckt, | ||
137 | ws, | ||
138 | max_fd, | ||
139 | fd_setsize)) ) | ||
140 | res = false; | ||
141 | /* Do not monitor again for errors if error was detected before as | ||
142 | * error state is remembered. */ | ||
143 | if ((0 == (urh->mhd.celi & MHD_EPOLL_STATE_ERROR)) && | ||
144 | ((0 != urh->out_buffer_size) || | ||
145 | (0 != urh->in_buffer_size) || | ||
146 | (0 != urh->in_buffer_used))) | ||
147 | MHD_add_to_fd_set_ (mhd_sckt, | ||
148 | es, | ||
149 | max_fd, | ||
150 | fd_setsize); | ||
151 | } | ||
152 | |||
153 | return res; | ||
154 | } | ||
155 | #endif | ||
156 | |||
157 | |||
158 | /** | ||
159 | * Internal version of #MHD_daemon_get_fdset2(). | ||
160 | * | ||
161 | * @param daemon daemon to get sets from | ||
162 | * @param read_fd_set read set | ||
163 | * @param write_fd_set write set | ||
164 | * @param except_fd_set except set | ||
165 | * @param max_fd increased to largest FD added (if larger | ||
166 | * than existing value); can be NULL | ||
167 | * @param fd_setsize value of FD_SETSIZE | ||
168 | * @return #MHD_SC_OK on success | ||
169 | * @ingroup event | ||
170 | */ | ||
171 | static enum MHD_StatusCode | ||
172 | internal_get_fdset2 (struct MHD_Daemon *daemon, | ||
173 | fd_set *read_fd_set, | ||
174 | fd_set *write_fd_set, | ||
175 | fd_set *except_fd_set, | ||
176 | MHD_socket *max_fd, | ||
177 | unsigned int fd_setsize) | ||
178 | |||
179 | { | ||
180 | struct MHD_Connection *pos; | ||
181 | struct MHD_Connection *posn; | ||
182 | enum MHD_StatusCode result = MHD_SC_OK; | ||
183 | MHD_socket ls; | ||
184 | |||
185 | if (daemon->shutdown) | ||
186 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
187 | |||
188 | ls = daemon->listen_socket; | ||
189 | if ( (MHD_INVALID_SOCKET != ls) && | ||
190 | (! daemon->was_quiesced) && | ||
191 | (! MHD_add_to_fd_set_ (ls, | ||
192 | read_fd_set, | ||
193 | max_fd, | ||
194 | fd_setsize)) ) | ||
195 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
196 | |||
197 | /* Add all sockets to 'except_fd_set' as well to watch for | ||
198 | * out-of-band data. However, ignore errors if INFO_READ | ||
199 | * or INFO_WRITE sockets will not fit 'except_fd_set'. */ | ||
200 | /* Start from oldest connections. Make sense for W32 FDSETs. */ | ||
201 | for (pos = daemon->connections_tail; NULL != pos; pos = posn) | ||
202 | { | ||
203 | posn = pos->prev; | ||
204 | |||
205 | switch (pos->request.event_loop_info) | ||
206 | { | ||
207 | case MHD_EVENT_LOOP_INFO_READ: | ||
208 | if (! MHD_add_to_fd_set_ (pos->socket_fd, | ||
209 | read_fd_set, | ||
210 | max_fd, | ||
211 | fd_setsize)) | ||
212 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
213 | #ifdef MHD_POSIX_SOCKETS | ||
214 | MHD_add_to_fd_set_ (pos->socket_fd, | ||
215 | except_fd_set, | ||
216 | max_fd, | ||
217 | fd_setsize); | ||
218 | #endif /* MHD_POSIX_SOCKETS */ | ||
219 | break; | ||
220 | case MHD_EVENT_LOOP_INFO_WRITE: | ||
221 | if (! MHD_add_to_fd_set_ (pos->socket_fd, | ||
222 | write_fd_set, | ||
223 | max_fd, | ||
224 | fd_setsize)) | ||
225 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
226 | #ifdef MHD_POSIX_SOCKETS | ||
227 | MHD_add_to_fd_set_ (pos->socket_fd, | ||
228 | except_fd_set, | ||
229 | max_fd, | ||
230 | fd_setsize); | ||
231 | #endif /* MHD_POSIX_SOCKETS */ | ||
232 | break; | ||
233 | case MHD_EVENT_LOOP_INFO_BLOCK: | ||
234 | if ( (NULL == except_fd_set) || | ||
235 | ! MHD_add_to_fd_set_ (pos->socket_fd, | ||
236 | except_fd_set, | ||
237 | max_fd, | ||
238 | fd_setsize)) | ||
239 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
240 | break; | ||
241 | case MHD_EVENT_LOOP_INFO_CLEANUP: | ||
242 | /* this should never happen */ | ||
243 | break; | ||
244 | } | ||
245 | } | ||
246 | #ifdef MHD_WINSOCK_SOCKETS | ||
247 | /* W32 use limited array for fd_set so add INFO_READ/INFO_WRITE sockets | ||
248 | * only after INFO_BLOCK sockets to ensure that INFO_BLOCK sockets will | ||
249 | * not be pushed out. */ | ||
250 | for (pos = daemon->connections_tail; NULL != pos; pos = posn) | ||
251 | { | ||
252 | posn = pos->prev; | ||
253 | MHD_add_to_fd_set_ (pos->socket_fd, | ||
254 | except_fd_set, | ||
255 | max_fd, | ||
256 | fd_setsize); | ||
257 | } | ||
258 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
259 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
260 | { | ||
261 | struct MHD_UpgradeResponseHandle *urh; | ||
262 | |||
263 | for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev) | ||
264 | { | ||
265 | if (! urh_to_fdset (urh, | ||
266 | read_fd_set, | ||
267 | write_fd_set, | ||
268 | except_fd_set, | ||
269 | max_fd, | ||
270 | fd_setsize)) | ||
271 | result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
272 | } | ||
273 | } | ||
274 | #endif | ||
275 | return result; | ||
276 | } | ||
277 | |||
278 | |||
279 | /** | ||
280 | * Obtain the `select()` sets for this daemon. Daemon's FDs will be | ||
281 | * added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO | ||
282 | * for each fd_set before calling this function. | ||
283 | * | ||
284 | * Passing custom FD_SETSIZE as @a fd_setsize allow usage of | ||
285 | * larger/smaller than platform's default fd_sets. | ||
286 | * | ||
287 | * This function should only be called in when MHD is configured to | ||
288 | * use external select with 'select()' or with 'epoll'. In the latter | ||
289 | * case, it will only add the single 'epoll' file descriptor used by | ||
290 | * MHD to the sets. It's necessary to use #MHD_get_timeout() in | ||
291 | * combination with this function. | ||
292 | * | ||
293 | * This function must be called only for daemon started | ||
294 | * without #MHD_USE_INTERNAL_POLLING_THREAD flag. | ||
295 | * | ||
296 | * @param daemon daemon to get sets from | ||
297 | * @param read_fd_set read set | ||
298 | * @param write_fd_set write set | ||
299 | * @param except_fd_set except set | ||
300 | * @param max_fd increased to largest FD added (if larger | ||
301 | * than existing value); can be NULL | ||
302 | * @param fd_setsize value of FD_SETSIZE | ||
303 | * @return #MHD_SC_OK on success, otherwise error code | ||
304 | * @ingroup event | ||
305 | */ | ||
306 | enum MHD_StatusCode | ||
307 | MHD_daemon_get_fdset2 (struct MHD_Daemon *daemon, | ||
308 | fd_set *read_fd_set, | ||
309 | fd_set *write_fd_set, | ||
310 | fd_set *except_fd_set, | ||
311 | MHD_socket *max_fd, | ||
312 | unsigned int fd_setsize) | ||
313 | { | ||
314 | if ( (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model) || | ||
315 | (MHD_ELS_POLL == daemon->event_loop_syscall) ) | ||
316 | return MHD_SC_CONFIGURATION_MISSMATCH_FOR_GET_FDSET; | ||
317 | |||
318 | #ifdef EPOLL_SUPPORT | ||
319 | if (MHD_ELS_EPOLL == daemon->event_loop_syscall) | ||
320 | { | ||
321 | if (daemon->shutdown) | ||
322 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
323 | |||
324 | /* we're in epoll mode, use the epoll FD as a stand-in for | ||
325 | the entire event set */ | ||
326 | |||
327 | return MHD_add_to_fd_set_ (daemon->epoll_fd, | ||
328 | read_fd_set, | ||
329 | max_fd, | ||
330 | fd_setsize) | ||
331 | ? MHD_SC_OK | ||
332 | : MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
333 | } | ||
334 | #endif | ||
335 | |||
336 | return internal_get_fdset2 (daemon, | ||
337 | read_fd_set, | ||
338 | write_fd_set, | ||
339 | except_fd_set, | ||
340 | max_fd, | ||
341 | fd_setsize); | ||
342 | } | ||
343 | |||
344 | |||
345 | /** | ||
346 | * Update the @a urh based on the ready FDs in | ||
347 | * the @a rs, @a ws, and @a es. | ||
348 | * | ||
349 | * @param urh upgrade handle to update | ||
350 | * @param rs read result from select() | ||
351 | * @param ws write result from select() | ||
352 | * @param es except result from select() | ||
353 | */ | ||
354 | static void | ||
355 | urh_from_fdset (struct MHD_UpgradeResponseHandle *urh, | ||
356 | const fd_set *rs, | ||
357 | const fd_set *ws, | ||
358 | const fd_set *es) | ||
359 | { | ||
360 | const MHD_socket conn_sckt = urh->connection->socket_fd; | ||
361 | const MHD_socket mhd_sckt = urh->mhd.socket; | ||
362 | |||
363 | /* Reset read/write ready, preserve error state. */ | ||
364 | urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); | ||
365 | urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); | ||
366 | |||
367 | if (MHD_INVALID_SOCKET != conn_sckt) | ||
368 | { | ||
369 | if (FD_ISSET (conn_sckt, rs)) | ||
370 | urh->app.celi |= MHD_EPOLL_STATE_READ_READY; | ||
371 | if (FD_ISSET (conn_sckt, ws)) | ||
372 | urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY; | ||
373 | if (FD_ISSET (conn_sckt, es)) | ||
374 | urh->app.celi |= MHD_EPOLL_STATE_ERROR; | ||
375 | } | ||
376 | if ((MHD_INVALID_SOCKET != mhd_sckt)) | ||
377 | { | ||
378 | if (FD_ISSET (mhd_sckt, rs)) | ||
379 | urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; | ||
380 | if (FD_ISSET (mhd_sckt, ws)) | ||
381 | urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY; | ||
382 | if (FD_ISSET (mhd_sckt, es)) | ||
383 | urh->mhd.celi |= MHD_EPOLL_STATE_ERROR; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | |||
388 | /** | ||
389 | * Internal version of #MHD_run_from_select(). | ||
390 | * | ||
391 | * @param daemon daemon to run select loop for | ||
392 | * @param read_fd_set read set | ||
393 | * @param write_fd_set write set | ||
394 | * @param except_fd_set except set | ||
395 | * @return #MHD_SC_OK on success | ||
396 | * @ingroup event | ||
397 | */ | ||
398 | static enum MHD_StatusCode | ||
399 | internal_run_from_select (struct MHD_Daemon *daemon, | ||
400 | const fd_set *read_fd_set, | ||
401 | const fd_set *write_fd_set, | ||
402 | const fd_set *except_fd_set) | ||
403 | { | ||
404 | MHD_socket ds; | ||
405 | struct MHD_Connection *pos; | ||
406 | struct MHD_Connection *prev; | ||
407 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
408 | struct MHD_UpgradeResponseHandle *urh; | ||
409 | struct MHD_UpgradeResponseHandle *urhn; | ||
410 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
411 | /* Reset. New value will be set when connections are processed. */ | ||
412 | /* Note: no-op for thread-per-connection as it is always false in that mode. */ | ||
413 | daemon->data_already_pending = false; | ||
414 | |||
415 | /* Clear ITC to avoid spinning select */ | ||
416 | /* Do it before any other processing so new signals | ||
417 | will trigger select again and will be processed */ | ||
418 | if ( (MHD_ITC_IS_VALID_(daemon->itc)) && | ||
419 | (FD_ISSET (MHD_itc_r_fd_ (daemon->itc), | ||
420 | read_fd_set)) ) | ||
421 | MHD_itc_clear_ (daemon->itc); | ||
422 | |||
423 | /* select connection thread handling type */ | ||
424 | if ( (MHD_INVALID_SOCKET != (ds = daemon->listen_socket)) && | ||
425 | (! daemon->was_quiesced) && | ||
426 | (FD_ISSET (ds, | ||
427 | read_fd_set)) ) | ||
428 | (void) MHD_accept_connection (daemon); | ||
429 | |||
430 | if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) | ||
431 | { | ||
432 | /* do not have a thread per connection, process all connections now */ | ||
433 | prev = daemon->connections_tail; | ||
434 | while (NULL != (pos = prev)) | ||
435 | { | ||
436 | prev = pos->prev; | ||
437 | ds = pos->socket_fd; | ||
438 | if (MHD_INVALID_SOCKET == ds) | ||
439 | continue; | ||
440 | call_handlers (pos, | ||
441 | FD_ISSET (ds, | ||
442 | read_fd_set), | ||
443 | FD_ISSET (ds, | ||
444 | write_fd_set), | ||
445 | FD_ISSET (ds, | ||
446 | except_fd_set)); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) | ||
451 | /* handle upgraded HTTPS connections */ | ||
452 | for (urh = daemon->urh_tail; NULL != urh; urh = urhn) | ||
453 | { | ||
454 | urhn = urh->prev; | ||
455 | /* update urh state based on select() output */ | ||
456 | urh_from_fdset (urh, | ||
457 | read_fd_set, | ||
458 | write_fd_set, | ||
459 | except_fd_set); | ||
460 | /* call generic forwarding function for passing data */ | ||
461 | process_urh (urh); | ||
462 | /* Finished forwarding? */ | ||
463 | if ( (0 == urh->in_buffer_size) && | ||
464 | (0 == urh->out_buffer_size) && | ||
465 | (0 == urh->in_buffer_used) && | ||
466 | (0 == urh->out_buffer_used) ) | ||
467 | { | ||
468 | MHD_connection_finish_forward_ (urh->connection); | ||
469 | urh->clean_ready = true; | ||
470 | /* Resuming will move connection to cleanup list. */ | ||
471 | MHD_resume_connection(urh->connection); | ||
472 | } | ||
473 | } | ||
474 | #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ | ||
475 | MHD_cleanup_connections (daemon); | ||
476 | return MHD_SC_OK; | ||
477 | } | ||
478 | |||
479 | |||
480 | /** | ||
481 | * Run webserver operations. This method should be called by clients | ||
482 | * in combination with #MHD_get_fdset and #MHD_get_timeout() if the | ||
483 | * client-controlled select method is used. | ||
484 | * | ||
485 | * You can use this function instead of #MHD_run if you called | ||
486 | * `select()` on the result from #MHD_get_fdset. File descriptors in | ||
487 | * the sets that are not controlled by MHD will be ignored. Calling | ||
488 | * this function instead of #MHD_run is more efficient as MHD will not | ||
489 | * have to call `select()` again to determine which operations are | ||
490 | * ready. | ||
491 | * | ||
492 | * This function cannot be used with daemon started with | ||
493 | * #MHD_USE_INTERNAL_POLLING_THREAD flag. | ||
494 | * | ||
495 | * @param daemon daemon to run select loop for | ||
496 | * @param read_fd_set read set | ||
497 | * @param write_fd_set write set | ||
498 | * @param except_fd_set except set | ||
499 | * @return #MHD_SC_OK on success | ||
500 | * @ingroup event | ||
501 | */ | ||
502 | enum MHD_StatusCode | ||
503 | MHD_daemon_run_from_select (struct MHD_Daemon *daemon, | ||
504 | const fd_set *read_fd_set, | ||
505 | |||
506 | const fd_set *write_fd_set, | ||
507 | const fd_set *except_fd_set) | ||
508 | { | ||
509 | if ( (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_model) || | ||
510 | (MHD_ELS_POLL == daemon->event_loop_syscall) ) | ||
511 | return MHD_SC_CONFIGURATION_MISSMATCH_FOR_RUN_SELECT; | ||
512 | if (MHD_ELS_EPOLL == daemon->event_loop_syscall) | ||
513 | { | ||
514 | #ifdef EPOLL_SUPPORT | ||
515 | enum MHD_StatusCode sc; | ||
516 | |||
517 | sc = MHD_daemon_epoll_ (daemon, | ||
518 | MHD_NO); | ||
519 | MHD_cleanup_connections (daemon); | ||
520 | return sc; | ||
521 | #else /* ! EPOLL_SUPPORT */ | ||
522 | return MHD_NO; | ||
523 | #endif /* ! EPOLL_SUPPORT */ | ||
524 | } | ||
525 | |||
526 | /* Resuming external connections when using an extern mainloop */ | ||
527 | if (! daemon->disallow_suspend_resume) | ||
528 | resume_suspended_connections (daemon); | ||
529 | |||
530 | return internal_run_from_select (daemon, | ||
531 | read_fd_set, | ||
532 | write_fd_set, | ||
533 | except_fd_set); | ||
534 | } | ||
535 | |||
536 | |||
537 | /** | ||
538 | * Main internal select() call. Will compute select sets, call | ||
539 | * select() and then #internal_run_from_select() with the result. | ||
540 | * | ||
541 | * @param daemon daemon to run select() loop for | ||
542 | * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking | ||
543 | * @return #MHD_SC_OK on success | ||
544 | */ | ||
545 | enum MHD_StatusCode | ||
546 | MHD_daemon_select_ (struct MHD_Daemon *daemon, | ||
547 | int may_block) | ||
548 | { | ||
549 | int num_ready; | ||
550 | fd_set rs; | ||
551 | fd_set ws; | ||
552 | fd_set es; | ||
553 | MHD_socket maxsock; | ||
554 | struct timeval timeout; | ||
555 | struct timeval *tv; | ||
556 | MHD_UNSIGNED_LONG_LONG ltimeout; | ||
557 | int err_state; | ||
558 | MHD_socket ls; | ||
559 | enum MHD_StatusCode sc; | ||
560 | enum MHD_StatusCode sc2; | ||
561 | |||
562 | timeout.tv_sec = 0; | ||
563 | timeout.tv_usec = 0; | ||
564 | if (daemon->shutdown) | ||
565 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
566 | FD_ZERO (&rs); | ||
567 | FD_ZERO (&ws); | ||
568 | FD_ZERO (&es); | ||
569 | maxsock = MHD_INVALID_SOCKET; | ||
570 | sc = MHD_SC_OK; | ||
571 | if ( (! daemon->disallow_suspend_resume) && | ||
572 | (MHD_YES == resume_suspended_connections (daemon)) && | ||
573 | (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) ) | ||
574 | may_block = MHD_NO; | ||
575 | |||
576 | if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) | ||
577 | { | ||
578 | |||
579 | /* single-threaded, go over everything */ | ||
580 | if (MHD_SC_OK != | ||
581 | (sc = internal_get_fdset2 (daemon, | ||
582 | &rs, | ||
583 | &ws, | ||
584 | &es, | ||
585 | &maxsock, | ||
586 | FD_SETSIZE))) | ||
587 | { | ||
588 | #ifdef HAVE_MESSAGES | ||
589 | MHD_DLOG (daemon, | ||
590 | sc, | ||
591 | _("Could not obtain daemon fdsets")); | ||
592 | #endif | ||
593 | } | ||
594 | } | ||
595 | else | ||
596 | { | ||
597 | /* accept only, have one thread per connection */ | ||
598 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
599 | (! daemon->was_quiesced) && | ||
600 | (! MHD_add_to_fd_set_ (ls, | ||
601 | &rs, | ||
602 | &maxsock, | ||
603 | FD_SETSIZE)) ) | ||
604 | { | ||
605 | #ifdef HAVE_MESSAGES | ||
606 | MHD_DLOG (daemon, | ||
607 | MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, | ||
608 | _("Could not add listen socket to fdset")); | ||
609 | #endif | ||
610 | return MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
611 | } | ||
612 | } | ||
613 | if ( (MHD_ITC_IS_VALID_(daemon->itc)) && | ||
614 | (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc), | ||
615 | &rs, | ||
616 | &maxsock, | ||
617 | FD_SETSIZE)) ) | ||
618 | { | ||
619 | #if defined(MHD_WINSOCK_SOCKETS) | ||
620 | /* fdset limit reached, new connections | ||
621 | cannot be handled. Remove listen socket FD | ||
622 | from fdset and retry to add ITC FD. */ | ||
623 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
624 | (! daemon->was_quiesced) ) | ||
625 | { | ||
626 | FD_CLR (ls, | ||
627 | &rs); | ||
628 | if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_(daemon->itc), | ||
629 | &rs, | ||
630 | &maxsock, | ||
631 | FD_SETSIZE)) | ||
632 | { | ||
633 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
634 | sc = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; | ||
635 | #ifdef HAVE_MESSAGES | ||
636 | MHD_DLOG (daemon, | ||
637 | sc, | ||
638 | _("Could not add control inter-thread communication channel FD to fdset")); | ||
639 | #endif | ||
640 | #if defined(MHD_WINSOCK_SOCKETS) | ||
641 | } | ||
642 | } | ||
643 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
644 | } | ||
645 | /* Stop listening if we are at the configured connection limit */ | ||
646 | /* If we're at the connection limit, no point in really | ||
647 | accepting new connections; however, make sure we do not miss | ||
648 | the shutdown OR the termination of an existing connection; so | ||
649 | only do this optimization if we have a signaling ITC in | ||
650 | place. */ | ||
651 | if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && | ||
652 | (MHD_ITC_IS_VALID_(daemon->itc)) && | ||
653 | ( (daemon->connections == daemon->global_connection_limit) || | ||
654 | (daemon->at_limit) ) ) | ||
655 | { | ||
656 | FD_CLR (ls, | ||
657 | &rs); | ||
658 | } | ||
659 | tv = NULL; | ||
660 | if (MHD_SC_OK != sc) | ||
661 | may_block = MHD_NO; | ||
662 | if (MHD_NO == may_block) | ||
663 | { | ||
664 | timeout.tv_usec = 0; | ||
665 | timeout.tv_sec = 0; | ||
666 | tv = &timeout; | ||
667 | } | ||
668 | else if ( (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_model) && | ||
669 | (MHD_SC_OK == | ||
670 | MHD_daemon_get_timeout (daemon, | ||
671 | <imeout)) ) | ||
672 | { | ||
673 | /* ltimeout is in ms */ | ||
674 | timeout.tv_usec = (ltimeout % 1000) * 1000; | ||
675 | if (ltimeout / 1000 > TIMEVAL_TV_SEC_MAX) | ||
676 | timeout.tv_sec = TIMEVAL_TV_SEC_MAX; | ||
677 | else | ||
678 | timeout.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE)(ltimeout / 1000); | ||
679 | tv = &timeout; | ||
680 | } | ||
681 | num_ready = MHD_SYS_select_ (maxsock + 1, | ||
682 | &rs, | ||
683 | &ws, | ||
684 | &es, | ||
685 | tv); | ||
686 | if (daemon->shutdown) | ||
687 | return MHD_SC_DAEMON_ALREADY_SHUTDOWN; | ||
688 | if (num_ready < 0) | ||
689 | { | ||
690 | const int err = MHD_socket_get_error_ (); | ||
691 | |||
692 | if (MHD_SCKT_ERR_IS_EINTR_(err)) | ||
693 | return sc; | ||
694 | #ifdef HAVE_MESSAGES | ||
695 | MHD_DLOG (daemon, | ||
696 | MHD_SC_UNEXPECTED_SELECT_ERROR, | ||
697 | _("select failed: %s\n"), | ||
698 | MHD_socket_strerr_ (err)); | ||
699 | #endif | ||
700 | return MHD_SC_UNEXPECTED_SELECT_ERROR; | ||
701 | } | ||
702 | if (MHD_SC_OK != | ||
703 | (sc2 = internal_run_from_select (daemon, | ||
704 | &rs, | ||
705 | &ws, | ||
706 | &es))) | ||
707 | return sc2; | ||
708 | return sc; | ||
709 | } | ||
710 | |||
711 | /* end of daemon_select.c */ | ||