[core] consolidate duplicated read-to-close code
[lighttpd.git] / src / fdevent.c
blob4da56729e17c129ae110aa7cf28dbd9119726a88
1 #include "first.h"
3 #include "base.h"
4 #include "log.h"
6 #include <sys/types.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <stdio.h>
13 #include <fcntl.h>
14 #include <assert.h>
17 fdevents *fdevent_init(server *srv, size_t maxfds, fdevent_handler_t type) {
18 fdevents *ev;
20 ev = calloc(1, sizeof(*ev));
21 force_assert(NULL != ev);
22 ev->srv = srv;
23 ev->fdarray = calloc(maxfds, sizeof(*ev->fdarray));
24 force_assert(NULL != ev->fdarray);
25 ev->maxfds = maxfds;
26 ev->highfd = -1;
28 switch(type) {
29 case FDEVENT_HANDLER_POLL:
30 if (0 != fdevent_poll_init(ev)) {
31 log_error_write(srv, __FILE__, __LINE__, "S",
32 "event-handler poll failed");
33 goto error;
35 return ev;
36 case FDEVENT_HANDLER_SELECT:
37 if (0 != fdevent_select_init(ev)) {
38 log_error_write(srv, __FILE__, __LINE__, "S",
39 "event-handler select failed");
40 goto error;
42 return ev;
43 case FDEVENT_HANDLER_LINUX_SYSEPOLL:
44 if (0 != fdevent_linux_sysepoll_init(ev)) {
45 log_error_write(srv, __FILE__, __LINE__, "S",
46 "event-handler linux-sysepoll failed, try to set server.event-handler = \"poll\" or \"select\"");
47 goto error;
49 return ev;
50 case FDEVENT_HANDLER_SOLARIS_DEVPOLL:
51 if (0 != fdevent_solaris_devpoll_init(ev)) {
52 log_error_write(srv, __FILE__, __LINE__, "S",
53 "event-handler solaris-devpoll failed, try to set server.event-handler = \"poll\" or \"select\"");
54 goto error;
56 return ev;
57 case FDEVENT_HANDLER_SOLARIS_PORT:
58 if (0 != fdevent_solaris_port_init(ev)) {
59 log_error_write(srv, __FILE__, __LINE__, "S",
60 "event-handler solaris-eventports failed, try to set server.event-handler = \"poll\" or \"select\"");
61 goto error;
63 return ev;
64 case FDEVENT_HANDLER_FREEBSD_KQUEUE:
65 if (0 != fdevent_freebsd_kqueue_init(ev)) {
66 log_error_write(srv, __FILE__, __LINE__, "S",
67 "event-handler freebsd-kqueue failed, try to set server.event-handler = \"poll\" or \"select\"");
68 goto error;
70 return ev;
71 case FDEVENT_HANDLER_LIBEV:
72 if (0 != fdevent_libev_init(ev)) {
73 log_error_write(srv, __FILE__, __LINE__, "S",
74 "event-handler libev failed, try to set server.event-handler = \"poll\" or \"select\"");
75 goto error;
77 return ev;
78 case FDEVENT_HANDLER_UNSET:
79 break;
82 error:
83 free(ev->fdarray);
84 free(ev);
86 log_error_write(srv, __FILE__, __LINE__, "S",
87 "event-handler is unknown, try to set server.event-handler = \"poll\" or \"select\"");
88 return NULL;
91 void fdevent_free(fdevents *ev) {
92 size_t i;
93 if (!ev) return;
95 if (ev->free) ev->free(ev);
97 for (i = 0; i < ev->maxfds; i++) {
98 if (ev->fdarray[i] > (fdnode *)0x2) free(ev->fdarray[i]);
101 free(ev->fdarray);
102 free(ev);
105 int fdevent_reset(fdevents *ev) {
106 if (ev->reset) return ev->reset(ev);
108 return 0;
111 static fdnode *fdnode_init(void) {
112 fdnode *fdn;
114 fdn = calloc(1, sizeof(*fdn));
115 force_assert(NULL != fdn);
116 fdn->fd = -1;
117 return fdn;
120 static void fdnode_free(fdnode *fdn) {
121 free(fdn);
124 int fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx) {
125 fdnode *fdn;
127 fdn = fdnode_init();
128 fdn->handler = handler;
129 fdn->fd = fd;
130 fdn->ctx = ctx;
131 fdn->handler_ctx = NULL;
132 fdn->events = 0;
134 ev->fdarray[fd] = fdn;
136 return 0;
139 int fdevent_unregister(fdevents *ev, int fd) {
140 fdnode *fdn;
142 if (!ev) return 0;
143 fdn = ev->fdarray[fd];
145 fdnode_free(fdn);
147 ev->fdarray[fd] = NULL;
149 return 0;
152 void fdevent_sched_close(fdevents *ev, int fd, int issock) {
153 if (!ev) return;
154 ev->fdarray[fd] = (issock ? (fdnode *)0x1 : (fdnode *)0x2);
155 if (ev->highfd < fd) ev->highfd = fd;
158 void fdevent_sched_run(server *srv, fdevents *ev) {
159 const int highfd = ev->highfd;
160 for (int fd = 0; fd <= highfd; ++fd) {
161 fdnode * const fdn = ev->fdarray[fd];
162 int rc;
163 if (!((uintptr_t)fdn & 0x3)) continue;
164 #ifdef _WIN32
165 if (fdn == (fdnode *)0x1) {
166 rc = closesocket(fd);
168 else if (fdn == (fdnode *)0x2) {
169 rc = close(fd);
171 #else
172 rc = close(fd);
173 #endif
175 if (0 != rc) {
176 log_error_write(srv, __FILE__, __LINE__, "sds", "close failed ", fd, strerror(errno));
179 ev->fdarray[fd] = NULL;
180 --srv->cur_fds;
182 ev->highfd = -1;
185 void fdevent_event_del(fdevents *ev, int *fde_ndx, int fd) {
186 if (-1 == fd) return;
187 if (ev->fdarray[fd] <= (fdnode *)0x2) return;
189 if (ev->event_del) *fde_ndx = ev->event_del(ev, *fde_ndx, fd);
190 ev->fdarray[fd]->events = 0;
193 void fdevent_event_set(fdevents *ev, int *fde_ndx, int fd, int events) {
194 if (-1 == fd) return;
196 /*(Note: skips registering with kernel if initial events is 0,
197 * so caller should pass non-zero events for initial registration.
198 * If never registered due to never being called with non-zero events,
199 * then FDEVENT_HUP or FDEVENT_ERR will never be returned.) */
200 if (ev->fdarray[fd]->events == events) return;/*(no change; nothing to do)*/
202 if (ev->event_set) *fde_ndx = ev->event_set(ev, *fde_ndx, fd, events);
203 ev->fdarray[fd]->events = events;
206 void fdevent_event_add(fdevents *ev, int *fde_ndx, int fd, int event) {
207 int events;
208 if (-1 == fd) return;
210 events = ev->fdarray[fd]->events;
211 if ((events & event) || 0 == event) return; /*(no change; nothing to do)*/
213 events |= event;
214 if (ev->event_set) *fde_ndx = ev->event_set(ev, *fde_ndx, fd, events);
215 ev->fdarray[fd]->events = events;
218 void fdevent_event_clr(fdevents *ev, int *fde_ndx, int fd, int event) {
219 int events;
220 if (-1 == fd) return;
222 events = ev->fdarray[fd]->events;
223 if (!(events & event)) return; /*(no change; nothing to do)*/
225 events &= ~event;
226 if (ev->event_set) *fde_ndx = ev->event_set(ev, *fde_ndx, fd, events);
227 ev->fdarray[fd]->events = events;
230 int fdevent_poll(fdevents *ev, int timeout_ms) {
231 if (ev->poll == NULL) SEGFAULT();
232 return ev->poll(ev, timeout_ms);
235 int fdevent_event_get_revent(fdevents *ev, size_t ndx) {
236 if (ev->event_get_revent == NULL) SEGFAULT();
238 return ev->event_get_revent(ev, ndx);
241 int fdevent_event_get_fd(fdevents *ev, size_t ndx) {
242 if (ev->event_get_fd == NULL) SEGFAULT();
244 return ev->event_get_fd(ev, ndx);
247 fdevent_handler fdevent_get_handler(fdevents *ev, int fd) {
248 if (ev->fdarray[fd] == NULL) SEGFAULT();
249 if ((uintptr_t)ev->fdarray[fd] & 0x3) return NULL;
250 if (ev->fdarray[fd]->fd != fd) SEGFAULT();
252 return ev->fdarray[fd]->handler;
255 void * fdevent_get_context(fdevents *ev, int fd) {
256 if (ev->fdarray[fd] == NULL) SEGFAULT();
257 if ((uintptr_t)ev->fdarray[fd] & 0x3) return NULL;
258 if (ev->fdarray[fd]->fd != fd) SEGFAULT();
260 return ev->fdarray[fd]->ctx;
263 void fd_close_on_exec(int fd) {
264 #ifdef FD_CLOEXEC
265 if (fd < 0) return;
266 force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
267 #else
268 UNUSED(fd);
269 #endif
272 int fdevent_fcntl_set(fdevents *ev, int fd) {
273 return ((ev) && (ev->fcntl_set)) ? ev->fcntl_set(ev, fd) : 0;
276 int fdevent_fcntl_set_nb(fdevents *ev, int fd) {
277 if ((ev) && (ev->fcntl_set)) return ev->fcntl_set(ev, fd);
278 #ifdef O_NONBLOCK
279 return fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
280 #else
281 return 0;
282 #endif
285 int fdevent_fcntl_set_nb_cloexec(fdevents *ev, int fd) {
286 fd_close_on_exec(fd);
287 return fdevent_fcntl_set_nb(ev, fd);
290 int fdevent_fcntl_set_nb_cloexec_sock(fdevents *ev, int fd) {
291 #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
292 return ((ev) && (ev->fcntl_set)) ? ev->fcntl_set(ev, fd) : 0;
293 #else
294 return fdevent_fcntl_set_nb_cloexec(ev, fd);
295 #endif
298 int fdevent_socket_cloexec(int domain, int type, int protocol) {
299 #ifdef SOCK_CLOEXEC
300 return socket(domain, type | SOCK_CLOEXEC, protocol);
301 #else
302 int fd;
303 if (-1 != (fd = socket(domain, type, protocol))) {
304 #ifdef FD_CLOEXEC
305 fcntl(fd, F_SETFD, FD_CLOEXEC);
306 #endif
308 return fd;
309 #endif
312 int fdevent_socket_nb_cloexec(int domain, int type, int protocol) {
313 #ifdef SOCK_CLOEXEC
314 return socket(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol);
315 #else
316 int fd;
317 if (-1 != (fd = socket(domain, type, protocol))) {
318 #ifdef FD_CLOEXEC
319 fcntl(fd, F_SETFD, FD_CLOEXEC);
320 #endif
321 #ifdef O_NONBLOCK
322 fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
323 #endif
325 return fd;
326 #endif
329 #ifndef O_NOCTTY
330 #define O_NOCTTY 0
331 #endif
333 int fdevent_open_cloexec(const char *pathname, int flags, mode_t mode) {
334 #ifdef O_CLOEXEC
335 return open(pathname, flags | O_CLOEXEC | O_NOCTTY, mode);
336 #else
337 int fd = open(pathname, flags | O_NOCTTY, mode);
338 #ifdef FD_CLOEXEC
339 if (fd != -1)
340 fcntl(fd, F_SETFD, FD_CLOEXEC);
341 #endif
342 return fd;
343 #endif
347 int fdevent_event_next_fdndx(fdevents *ev, int ndx) {
348 if (ev->event_next_fdndx) return ev->event_next_fdndx(ev, ndx);
350 return -1;
354 #include <netinet/tcp.h>
355 #if (defined(__APPLE__) && defined(__MACH__)) \
356 || defined(__FreeBSD__) || defined(__NetBSD__) \
357 || defined(__OpenBSD__) || defined(__DragonFly__)
358 #include <netinet/tcp_fsm.h>
359 #endif
361 /* fd must be TCP socket (AF_INET, AF_INET6), end-of-stream recv() 0 bytes */
362 int fdevent_is_tcp_half_closed(int fd) {
363 #ifdef TCP_CONNECTION_INFO /* Darwin */
364 struct tcp_connection_info tcpi;
365 socklen_t tlen = sizeof(tcpi);
366 return (0 == getsockopt(fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &tcpi, &tlen)
367 && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
368 #elif defined(TCP_INFO) && defined(TCPS_CLOSE_WAIT)
369 /* FreeBSD, NetBSD (not present in OpenBSD or DragonFlyBSD) */
370 struct tcp_info tcpi;
371 socklen_t tlen = sizeof(tcpi);
372 return (0 == getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpi, &tlen)
373 && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
374 #elif defined(TCP_INFO) && defined(__linux__)
375 /* Linux (TCP_CLOSE_WAIT is enum, so can not #ifdef TCP_CLOSE_WAIT) */
376 struct tcp_info tcpi;
377 socklen_t tlen = sizeof(tcpi);/*SOL_TCP == IPPROTO_TCP*/
378 return (0 == getsockopt(fd, SOL_TCP, TCP_INFO, &tcpi, &tlen)
379 && tcpi.tcpi_state == TCP_CLOSE_WAIT);
380 #else
381 UNUSED(fd);
382 /*(0 != getpeername() error might indicate TCP RST, but success
383 * would not differentiate between half-close and full-close)*/
384 return 0; /* false (not half-closed) or TCP state unknown */
385 #endif