[mod_openssl] remove erroneous SSL_set_shutdown()
[lighttpd.git] / src / fdevent.c
blob232cfeadce23db7064cdaa30eeb0b9b60fdf089b
1 #include "first.h"
3 #include "base.h"
4 #include "fdevent.h"
5 #include "buffer.h"
6 #include "log.h"
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include "sys-socket.h"
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <time.h>
19 #ifdef SOCK_CLOEXEC
20 static int use_sock_cloexec;
21 #endif
23 fdevents *fdevent_init(server *srv, size_t maxfds, int type) {
24 fdevents *ev;
26 #ifdef SOCK_CLOEXEC
27 /* Test if SOCK_CLOEXEC is supported by kernel.
28 * Linux kernels < 2.6.27 might return EINVAL if SOCK_CLOEXEC used
29 * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=529929
30 * http://www.linksysinfo.org/index.php?threads/lighttpd-no-longer-starts-toastman-1-28-0510-7.73132/ */
31 int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
32 if (fd >= 0) {
33 use_sock_cloexec = 1;
34 close(fd);
36 #endif
38 ev = calloc(1, sizeof(*ev));
39 force_assert(NULL != ev);
40 ev->srv = srv;
41 ev->fdarray = calloc(maxfds, sizeof(*ev->fdarray));
42 if (NULL == ev->fdarray) {
43 log_error_write(srv, __FILE__, __LINE__, "SDS",
44 "server.max-fds too large? (", maxfds-1, ")");
45 free(ev);
46 return NULL;
48 ev->maxfds = maxfds;
49 ev->highfd = -1;
51 switch(type) {
52 case FDEVENT_HANDLER_POLL:
53 if (0 != fdevent_poll_init(ev)) {
54 log_error_write(srv, __FILE__, __LINE__, "S",
55 "event-handler poll failed");
56 goto error;
58 return ev;
59 case FDEVENT_HANDLER_SELECT:
60 if (0 != fdevent_select_init(ev)) {
61 log_error_write(srv, __FILE__, __LINE__, "S",
62 "event-handler select failed");
63 goto error;
65 return ev;
66 case FDEVENT_HANDLER_LINUX_SYSEPOLL:
67 if (0 != fdevent_linux_sysepoll_init(ev)) {
68 log_error_write(srv, __FILE__, __LINE__, "S",
69 "event-handler linux-sysepoll failed, try to set server.event-handler = \"poll\" or \"select\"");
70 goto error;
72 return ev;
73 case FDEVENT_HANDLER_SOLARIS_DEVPOLL:
74 if (0 != fdevent_solaris_devpoll_init(ev)) {
75 log_error_write(srv, __FILE__, __LINE__, "S",
76 "event-handler solaris-devpoll failed, try to set server.event-handler = \"poll\" or \"select\"");
77 goto error;
79 return ev;
80 case FDEVENT_HANDLER_SOLARIS_PORT:
81 if (0 != fdevent_solaris_port_init(ev)) {
82 log_error_write(srv, __FILE__, __LINE__, "S",
83 "event-handler solaris-eventports failed, try to set server.event-handler = \"poll\" or \"select\"");
84 goto error;
86 return ev;
87 case FDEVENT_HANDLER_FREEBSD_KQUEUE:
88 if (0 != fdevent_freebsd_kqueue_init(ev)) {
89 log_error_write(srv, __FILE__, __LINE__, "S",
90 "event-handler freebsd-kqueue failed, try to set server.event-handler = \"poll\" or \"select\"");
91 goto error;
93 return ev;
94 case FDEVENT_HANDLER_LIBEV:
95 if (0 != fdevent_libev_init(ev)) {
96 log_error_write(srv, __FILE__, __LINE__, "S",
97 "event-handler libev failed, try to set server.event-handler = \"poll\" or \"select\"");
98 goto error;
100 return ev;
101 case FDEVENT_HANDLER_UNSET:
102 default:
103 break;
106 error:
107 free(ev->fdarray);
108 free(ev);
110 log_error_write(srv, __FILE__, __LINE__, "S",
111 "event-handler is unknown, try to set server.event-handler = \"poll\" or \"select\"");
112 return NULL;
115 void fdevent_free(fdevents *ev) {
116 size_t i;
117 if (!ev) return;
119 if (ev->free) ev->free(ev);
121 for (i = 0; i < ev->maxfds; i++) {
122 if (ev->fdarray[i] > (fdnode *)0x2) free(ev->fdarray[i]);
125 free(ev->fdarray);
126 free(ev);
129 int fdevent_reset(fdevents *ev) {
130 if (ev->reset) return ev->reset(ev);
132 return 0;
135 static fdnode *fdnode_init(void) {
136 fdnode *fdn;
138 fdn = calloc(1, sizeof(*fdn));
139 force_assert(NULL != fdn);
140 fdn->fd = -1;
141 return fdn;
144 static void fdnode_free(fdnode *fdn) {
145 free(fdn);
148 int fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx) {
149 fdnode *fdn;
151 fdn = fdnode_init();
152 fdn->handler = handler;
153 fdn->fd = fd;
154 fdn->ctx = ctx;
155 fdn->handler_ctx = NULL;
156 fdn->events = 0;
158 ev->fdarray[fd] = fdn;
160 return 0;
163 int fdevent_unregister(fdevents *ev, int fd) {
164 fdnode *fdn;
166 if (!ev) return 0;
167 fdn = ev->fdarray[fd];
169 fdnode_free(fdn);
171 ev->fdarray[fd] = NULL;
173 return 0;
176 void fdevent_sched_close(fdevents *ev, int fd, int issock) {
177 if (!ev) return;
178 ev->fdarray[fd] = (issock ? (fdnode *)0x1 : (fdnode *)0x2);
179 if (ev->highfd < fd) ev->highfd = fd;
182 void fdevent_sched_run(server *srv, fdevents *ev) {
183 const int highfd = ev->highfd;
184 for (int fd = 0; fd <= highfd; ++fd) {
185 fdnode * const fdn = ev->fdarray[fd];
186 int rc;
187 if (!((uintptr_t)fdn & 0x3)) continue;
188 #ifdef _WIN32
189 if (fdn == (fdnode *)0x1) {
190 rc = closesocket(fd);
192 else if (fdn == (fdnode *)0x2) {
193 rc = close(fd);
195 #else
196 rc = close(fd);
197 #endif
199 if (0 != rc) {
200 log_error_write(srv, __FILE__, __LINE__, "sds", "close failed ", fd, strerror(errno));
203 ev->fdarray[fd] = NULL;
204 --srv->cur_fds;
206 ev->highfd = -1;
209 void fdevent_event_del(fdevents *ev, int *fde_ndx, int fd) {
210 if (-1 == fd) return;
211 if (ev->fdarray[fd] <= (fdnode *)0x2) return;
213 if (ev->event_del) *fde_ndx = ev->event_del(ev, *fde_ndx, fd);
214 ev->fdarray[fd]->events = 0;
217 void fdevent_event_set(fdevents *ev, int *fde_ndx, int fd, int events) {
218 if (-1 == fd) return;
220 /*(Note: skips registering with kernel if initial events is 0,
221 * so caller should pass non-zero events for initial registration.
222 * If never registered due to never being called with non-zero events,
223 * then FDEVENT_HUP or FDEVENT_ERR will never be returned.) */
224 if (ev->fdarray[fd]->events == events) return;/*(no change; nothing to do)*/
226 if (ev->event_set) *fde_ndx = ev->event_set(ev, *fde_ndx, fd, events);
227 ev->fdarray[fd]->events = events;
230 void fdevent_event_add(fdevents *ev, int *fde_ndx, int fd, int event) {
231 int events;
232 if (-1 == fd) return;
234 events = ev->fdarray[fd]->events;
235 if ((events & event) || 0 == event) return; /*(no change; nothing to do)*/
237 events |= event;
238 if (ev->event_set) *fde_ndx = ev->event_set(ev, *fde_ndx, fd, events);
239 ev->fdarray[fd]->events = events;
242 void fdevent_event_clr(fdevents *ev, int *fde_ndx, int fd, int event) {
243 int events;
244 if (-1 == fd) return;
246 events = ev->fdarray[fd]->events;
247 if (!(events & event)) return; /*(no change; nothing to do)*/
249 events &= ~event;
250 if (ev->event_set) *fde_ndx = ev->event_set(ev, *fde_ndx, fd, events);
251 ev->fdarray[fd]->events = events;
254 int fdevent_poll(fdevents *ev, int timeout_ms) {
255 if (ev->poll == NULL) SEGFAULT();
256 return ev->poll(ev, timeout_ms);
259 int fdevent_event_get_revent(fdevents *ev, size_t ndx) {
260 if (ev->event_get_revent == NULL) SEGFAULT();
262 return ev->event_get_revent(ev, ndx);
265 int fdevent_event_get_fd(fdevents *ev, size_t ndx) {
266 if (ev->event_get_fd == NULL) SEGFAULT();
268 return ev->event_get_fd(ev, ndx);
271 fdevent_handler fdevent_get_handler(fdevents *ev, int fd) {
272 if (ev->fdarray[fd] == NULL) SEGFAULT();
273 if ((uintptr_t)ev->fdarray[fd] & 0x3) return NULL;
274 if (ev->fdarray[fd]->fd != fd) SEGFAULT();
276 return ev->fdarray[fd]->handler;
279 void * fdevent_get_context(fdevents *ev, int fd) {
280 if (ev->fdarray[fd] == NULL) SEGFAULT();
281 if ((uintptr_t)ev->fdarray[fd] & 0x3) return NULL;
282 if (ev->fdarray[fd]->fd != fd) SEGFAULT();
284 return ev->fdarray[fd]->ctx;
287 void fdevent_setfd_cloexec(int fd) {
288 #ifdef FD_CLOEXEC
289 if (fd < 0) return;
290 force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
291 #else
292 UNUSED(fd);
293 #endif
296 void fdevent_clrfd_cloexec(int fd) {
297 #ifdef FD_CLOEXEC
298 if (fd >= 0) force_assert(-1 != fcntl(fd, F_SETFD, 0));
299 #else
300 UNUSED(fd);
301 #endif
304 int fdevent_fcntl_set_nb(fdevents *ev, int fd) {
305 UNUSED(ev);
306 #ifdef O_NONBLOCK
307 return fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
308 #else
309 UNUSED(fd);
310 return 0;
311 #endif
314 int fdevent_fcntl_set_nb_cloexec(fdevents *ev, int fd) {
315 fdevent_setfd_cloexec(fd);
316 return fdevent_fcntl_set_nb(ev, fd);
319 int fdevent_fcntl_set_nb_cloexec_sock(fdevents *ev, int fd) {
320 #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
321 if (use_sock_cloexec)
322 return 0;
323 #endif
324 return fdevent_fcntl_set_nb_cloexec(ev, fd);
327 int fdevent_socket_cloexec(int domain, int type, int protocol) {
328 int fd;
329 #ifdef SOCK_CLOEXEC
330 if (use_sock_cloexec)
331 return socket(domain, type | SOCK_CLOEXEC, protocol);
332 #endif
333 if (-1 != (fd = socket(domain, type, protocol))) {
334 #ifdef FD_CLOEXEC
335 force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
336 #endif
338 return fd;
341 int fdevent_socket_nb_cloexec(int domain, int type, int protocol) {
342 int fd;
343 #ifdef SOCK_CLOEXEC
344 if (use_sock_cloexec)
345 return socket(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol);
346 #endif
347 if (-1 != (fd = socket(domain, type, protocol))) {
348 #ifdef FD_CLOEXEC
349 force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
350 #endif
351 #ifdef O_NONBLOCK
352 force_assert(-1 != fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR));
353 #endif
355 return fd;
358 #ifndef O_NOCTTY
359 #define O_NOCTTY 0
360 #endif
362 int fdevent_open_cloexec(const char *pathname, int flags, mode_t mode) {
363 #ifdef O_CLOEXEC
364 return open(pathname, flags | O_CLOEXEC | O_NOCTTY, mode);
365 #else
366 int fd = open(pathname, flags | O_NOCTTY, mode);
367 #ifdef FD_CLOEXEC
368 if (fd != -1)
369 force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
370 #endif
371 return fd;
372 #endif
376 int fdevent_open_devnull(void) {
377 #if defined(_WIN32)
378 return fdevent_open_cloexec("nul", O_RDWR, 0);
379 #else
380 return fdevent_open_cloexec("/dev/null", O_RDWR, 0);
381 #endif
385 int fdevent_open_dirname(char *path) {
386 /*(handle special cases of no dirname or dirname is root directory)*/
387 char * const c = strrchr(path, '/');
388 const char * const dname = (NULL != c ? c == path ? "/" : path : ".");
389 int dfd;
390 int flags = O_RDONLY;
391 #ifdef O_DIRECTORY
392 flags |= O_DIRECTORY;
393 #endif
394 if (NULL != c) *c = '\0';
395 dfd = fdevent_open_cloexec(dname, flags, 0);
396 if (NULL != c) *c = '/';
397 return dfd;
401 int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen) {
402 int fd;
403 socklen_t len = (socklen_t) *addrlen;
405 #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
406 #if defined(__NetBSD__)
407 fd = paccept(listenfd, addr, &len, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK);
408 #else
409 fd = (use_sock_cloexec)
410 ? accept4(listenfd, addr, &len, SOCK_CLOEXEC | SOCK_NONBLOCK)
411 : accept(listenfd, addr, &len);
412 #endif
413 #else
414 fd = accept(listenfd, addr, &len);
415 #endif
417 if (fd >= 0) *addrlen = (size_t)len;
418 return fd;
422 int fdevent_event_next_fdndx(fdevents *ev, int ndx) {
423 if (ev->event_next_fdndx) return ev->event_next_fdndx(ev, ndx);
425 return -1;
429 #ifdef FD_CLOEXEC
430 static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd) {
431 if (oldfd >= 0) {
432 if (oldfd != newfd) {
433 force_assert(oldfd > STDERR_FILENO);
434 if (newfd != dup2(oldfd, newfd)) return -1;
436 else {
437 fdevent_clrfd_cloexec(newfd);
440 return newfd;
442 #else
443 static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd, int reuse) {
444 if (oldfd >= 0) {
445 if (oldfd != newfd) {
446 force_assert(oldfd > STDERR_FILENO);
447 if (newfd != dup2(oldfd, newfd)) return -1;
448 if (!reuse) close(oldfd);
451 return newfd;
453 #endif
456 int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr) {
457 #ifdef FD_CLOEXEC
458 if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO))
459 return -1;
460 if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO))
461 return -1;
462 if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO))
463 return -1;
464 #else
465 if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO,
466 fdin == fdout
467 || fdin == fderr))
468 return -1;
469 if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO,
470 fdout == fderr))
471 return -1;
472 if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO,
474 return -1;
475 #endif
477 return 0;
481 #include <stdio.h> /* perror() */
482 #include <signal.h> /* signal() */
484 pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd) {
485 #ifdef HAVE_FORK
487 pid_t pid = fork();
488 if (0 != pid) return pid; /* parent (pid > 0) or fork() error (-1 == pid) */
490 /* child (0 == pid) */
492 if (-1 != dfd) {
493 if (0 != fchdir(dfd))
494 _exit(errno);
495 close(dfd);
498 if (0 != fdevent_set_stdin_stdout_stderr(fdin, fdout, fderr)) _exit(errno);
499 #ifdef FD_CLOEXEC
500 /*(might not be sufficient for open fds, but modern OS have FD_CLOEXEC)*/
501 for (int i = 3; i < 256; ++i) close(i);
502 #endif
504 /* reset_signals which may have been ignored (SIG_IGN) */
505 #ifdef SIGTTOU
506 signal(SIGTTOU, SIG_DFL);
507 #endif
508 #ifdef SIGTTIN
509 signal(SIGTTIN, SIG_DFL);
510 #endif
511 #ifdef SIGTSTP
512 signal(SIGTSTP, SIG_DFL);
513 #endif
514 signal(SIGPIPE, SIG_DFL);
516 execve(name, argv, envp ? envp : environ);
518 if (0 == memcmp(argv[0], "/bin/sh", sizeof("/bin/sh")-1)
519 && argv[1] && 0 == memcmp(argv[1], "-c", sizeof("-c")-1))
520 perror(argv[2]);
521 else
522 perror(argv[0]);
523 _exit(errno);
525 #else
527 UNUSED(name);
528 UNUSED(argv);
529 UNUSED(envp);
530 UNUSED(fdin);
531 UNUSED(fdout);
532 UNUSED(fderr);
533 UNUSED(dfd);
534 return (pid_t)-1;
536 #endif
540 typedef struct fdevent_cmd_pipe {
541 pid_t pid;
542 int fds[2];
543 const char *cmd;
544 time_t start;
545 } fdevent_cmd_pipe;
547 typedef struct fdevent_cmd_pipes {
548 fdevent_cmd_pipe *ptr;
549 size_t used;
550 size_t size;
551 } fdevent_cmd_pipes;
553 static fdevent_cmd_pipes cmd_pipes;
556 static pid_t fdevent_open_logger_pipe_spawn(const char *logger, int rfd) {
557 char *args[4];
558 int devnull = fdevent_open_devnull();
559 pid_t pid;
561 if (-1 == devnull) {
562 return -1;
565 *(const char **)&args[0] = "/bin/sh";
566 *(const char **)&args[1] = "-c";
567 *(const char **)&args[2] = logger;
568 args[3] = NULL;
570 pid = fdevent_fork_execve(args[0], args, NULL, rfd, devnull, devnull, -1);
572 if (pid > 0) {
573 close(devnull);
575 else {
576 int errnum = errno;
577 close(devnull);
578 errno = errnum;
580 return pid;
584 static void fdevent_restart_logger_pipe(fdevent_cmd_pipe *fcp, time_t ts) {
585 if (fcp->pid > 0) return; /* assert */
586 if (fcp->start + 5 < ts) { /* limit restart to once every 5 sec */
587 /* restart child process using existing pipe fds */
588 fcp->start = ts;
589 fcp->pid = fdevent_open_logger_pipe_spawn(fcp->cmd, fcp->fds[0]);
594 void fdevent_restart_logger_pipes(time_t ts) {
595 for (size_t i = 0; i < cmd_pipes.used; ++i) {
596 fdevent_cmd_pipe * const fcp = cmd_pipes.ptr+i;
597 if (fcp->pid > 0) continue;
598 fdevent_restart_logger_pipe(fcp, ts);
603 int fdevent_waitpid_logger_pipe_pid(pid_t pid, time_t ts) {
604 for (size_t i = 0; i < cmd_pipes.used; ++i) {
605 fdevent_cmd_pipe * const fcp = cmd_pipes.ptr+i;
606 if (pid != fcp->pid) continue;
607 fcp->pid = -1;
608 fdevent_restart_logger_pipe(fcp, ts);
609 return 1;
611 return 0;
615 void fdevent_clr_logger_pipe_pids(void) {
616 for (size_t i = 0; i < cmd_pipes.used; ++i) {
617 fdevent_cmd_pipe *fcp = cmd_pipes.ptr+i;
618 fcp->pid = -1;
623 int fdevent_reaped_logger_pipe(pid_t pid) {
624 for (size_t i = 0; i < cmd_pipes.used; ++i) {
625 fdevent_cmd_pipe *fcp = cmd_pipes.ptr+i;
626 if (fcp->pid == pid) {
627 time_t ts = time(NULL);
628 if (fcp->start + 5 < ts) { /* limit restart to once every 5 sec */
629 fcp->start = ts;
630 fcp->pid = fdevent_open_logger_pipe_spawn(fcp->cmd,fcp->fds[0]);
631 return 1;
633 else {
634 fcp->pid = -1;
635 return -1;
639 return 0;
643 void fdevent_close_logger_pipes(void) {
644 for (size_t i = 0; i < cmd_pipes.used; ++i) {
645 fdevent_cmd_pipe *fcp = cmd_pipes.ptr+i;
646 close(fcp->fds[0]);
647 if (fcp->fds[1] != STDERR_FILENO) close(fcp->fds[1]);
649 free(cmd_pipes.ptr);
650 cmd_pipes.ptr = NULL;
651 cmd_pipes.used = 0;
652 cmd_pipes.size = 0;
656 void fdevent_breakagelog_logger_pipe(int fd) {
657 for (size_t i = 0; i < cmd_pipes.used; ++i) {
658 fdevent_cmd_pipe *fcp = cmd_pipes.ptr+i;
659 if (fcp->fds[1] != fd) continue;
660 fcp->fds[1] = STDERR_FILENO;
661 break;
666 static void fdevent_init_logger_pipe(const char *cmd, int fds[2], pid_t pid) {
667 fdevent_cmd_pipe *fcp;
668 if (cmd_pipes.used == cmd_pipes.size) {
669 cmd_pipes.size += 4;
670 cmd_pipes.ptr =
671 realloc(cmd_pipes.ptr, cmd_pipes.size * sizeof(fdevent_cmd_pipe));
672 force_assert(cmd_pipes.ptr);
674 fcp = cmd_pipes.ptr + cmd_pipes.used++;
675 fcp->cmd = cmd; /* note: cmd must persist in memory (or else copy here) */
676 fcp->fds[0] = fds[0];
677 fcp->fds[1] = fds[1];
678 fcp->pid = pid;
679 fcp->start = time(NULL);
683 static int fdevent_open_logger_pipe(const char *logger) {
684 int fds[2];
685 pid_t pid;
686 if (pipe(fds)) {
687 return -1;
689 fdevent_setfd_cloexec(fds[0]);
690 fdevent_setfd_cloexec(fds[1]);
692 pid = fdevent_open_logger_pipe_spawn(logger, fds[0]);
694 if (pid > 0) {
695 fdevent_init_logger_pipe(logger, fds, pid);
696 return fds[1];
698 else {
699 int errnum = errno;
700 close(fds[0]);
701 close(fds[1]);
702 errno = errnum;
703 return -1;
708 #ifndef O_LARGEFILE
709 #define O_LARGEFILE 0
710 #endif
712 int fdevent_open_logger(const char *logger) {
713 if (logger[0] != '|') {
714 int flags = O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE;
715 return fdevent_open_cloexec(logger, flags, 0644);
717 else {
718 return fdevent_open_logger_pipe(logger+1); /*(skip the '|')*/
722 int fdevent_cycle_logger(const char *logger, int *curfd) {
723 if (logger[0] != '|') {
724 int fd = fdevent_open_logger(logger);
725 if (-1 == fd) return -1; /*(error; leave *curfd as-is)*/
726 if (-1 != *curfd) close(*curfd);
727 *curfd = fd;
729 return *curfd;
733 #include <sys/ioctl.h>
734 #ifdef HAVE_SYS_FILIO_H
735 #include <sys/filio.h> /* FIONREAD (for illumos (OpenIndiana)) */
736 #endif
737 #ifdef _WIN32
738 #include <winsock2.h>
739 #endif
740 int fdevent_ioctl_fionread (int fd, int fdfmt, int *toread) {
741 #ifdef _WIN32
742 if (fdfmt != S_IFSOCK) { errno = ENOTSOCK; return -1; }
743 return ioctlsocket(fd, FIONREAD, toread);
744 #else
745 #ifdef __CYGWIN__
746 /*(cygwin supports FIONREAD on pipes, not sockets)*/
747 if (fdfmt != S_IFIFO) { errno = EOPNOTSUPP; return -1; }
748 #else
749 UNUSED(fdfmt);
750 #endif
751 return ioctl(fd, FIONREAD, toread);
752 #endif
756 int fdevent_connect_status(int fd) {
757 /* try to finish the connect() */
758 /*(should be called after connect() only when fd is writable (POLLOUT))*/
759 int opt;
760 socklen_t len = sizeof(opt);
761 return (0 == getsockopt(fd,SOL_SOCKET,SO_ERROR,&opt,&len)) ? opt : errno;
765 #include <netinet/tcp.h>
766 #if (defined(__APPLE__) && defined(__MACH__)) \
767 || defined(__FreeBSD__) || defined(__NetBSD__) \
768 || defined(__OpenBSD__) || defined(__DragonFly__)
769 #include <netinet/tcp_fsm.h>
770 #endif
772 /* fd must be TCP socket (AF_INET, AF_INET6), end-of-stream recv() 0 bytes */
773 int fdevent_is_tcp_half_closed(int fd) {
774 #ifdef TCP_CONNECTION_INFO /* Darwin */
775 struct tcp_connection_info tcpi;
776 socklen_t tlen = sizeof(tcpi);
777 return (0 == getsockopt(fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &tcpi, &tlen)
778 && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
779 #elif defined(TCP_INFO) && defined(TCPS_CLOSE_WAIT)
780 /* FreeBSD, NetBSD (not present in OpenBSD or DragonFlyBSD) */
781 struct tcp_info tcpi;
782 socklen_t tlen = sizeof(tcpi);
783 return (0 == getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpi, &tlen)
784 && tcpi.tcpi_state == TCPS_CLOSE_WAIT);
785 #elif defined(TCP_INFO) && defined(__linux__)
786 /* Linux (TCP_CLOSE_WAIT is enum, so can not #ifdef TCP_CLOSE_WAIT) */
787 struct tcp_info tcpi;
788 socklen_t tlen = sizeof(tcpi);/*SOL_TCP == IPPROTO_TCP*/
789 return (0 == getsockopt(fd, SOL_TCP, TCP_INFO, &tcpi, &tlen)
790 && tcpi.tcpi_state == TCP_CLOSE_WAIT);
791 #else
792 UNUSED(fd);
793 /*(0 != getpeername() error might indicate TCP RST, but success
794 * would not differentiate between half-close and full-close)*/
795 return 0; /* false (not half-closed) or TCP state unknown */
796 #endif
800 int fdevent_set_tcp_nodelay (const int fd, const int opt)
802 return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
806 int fdevent_set_so_reuseaddr (const int fd, const int opt)
808 return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));