2016-03-19 15:14:35 +00:00
|
|
|
#include "first.h"
|
|
|
|
|
2017-11-01 04:16:38 +00:00
|
|
|
#include "fdevent_impl.h"
|
2017-03-28 04:04:31 +00:00
|
|
|
#include "fdevent.h"
|
2017-06-22 01:41:59 +00:00
|
|
|
#include "buffer.h"
|
2010-08-06 21:57:15 +00:00
|
|
|
#include "log.h"
|
2005-02-20 14:27:00 +00:00
|
|
|
|
2009-10-11 14:31:42 +00:00
|
|
|
#include <sys/types.h>
|
2017-07-17 04:52:14 +00:00
|
|
|
#include <sys/wait.h>
|
2017-04-15 21:38:15 +00:00
|
|
|
#include "sys-socket.h"
|
2005-02-20 14:27:00 +00:00
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2017-07-17 04:52:14 +00:00
|
|
|
#include <time.h>
|
2005-02-20 14:27:00 +00:00
|
|
|
|
2017-06-10 17:22:48 +00:00
|
|
|
#ifdef SOCK_CLOEXEC
|
2017-04-15 21:38:15 +00:00
|
|
|
static int use_sock_cloexec;
|
2017-06-10 17:22:48 +00:00
|
|
|
#endif
|
2018-04-11 01:37:39 +00:00
|
|
|
#ifdef SOCK_NONBLOCK
|
|
|
|
static int use_sock_nonblock;
|
|
|
|
#endif
|
2017-04-15 21:38:15 +00:00
|
|
|
|
2019-12-25 04:15:02 +00:00
|
|
|
int fdevent_config(const char **event_handler_name, log_error_st *errh) {
|
2017-11-01 04:16:38 +00:00
|
|
|
static const struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] =
|
|
|
|
{
|
|
|
|
/* - epoll is most reliable
|
|
|
|
* - select works everywhere
|
|
|
|
*/
|
|
|
|
#ifdef FDEVENT_USE_LINUX_EPOLL
|
|
|
|
{ FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" },
|
|
|
|
{ FDEVENT_HANDLER_LINUX_SYSEPOLL, "epoll" },
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_SOLARIS_PORT
|
|
|
|
{ FDEVENT_HANDLER_SOLARIS_PORT, "solaris-eventports" },
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_SOLARIS_DEVPOLL
|
|
|
|
{ FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_FREEBSD_KQUEUE
|
|
|
|
{ FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
|
|
|
|
{ FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue" },
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_POLL
|
|
|
|
{ FDEVENT_HANDLER_POLL, "poll" },
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_SELECT
|
|
|
|
{ FDEVENT_HANDLER_SELECT, "select" },
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_LIBEV
|
|
|
|
{ FDEVENT_HANDLER_LIBEV, "libev" },
|
|
|
|
#endif
|
|
|
|
{ FDEVENT_HANDLER_UNSET, NULL }
|
|
|
|
};
|
|
|
|
|
2019-12-25 04:15:02 +00:00
|
|
|
const char * event_handler = *event_handler_name;
|
|
|
|
fdevent_handler_t et = FDEVENT_HANDLER_UNSET;
|
|
|
|
|
|
|
|
if (NULL == event_handler) {
|
2017-11-01 04:16:38 +00:00
|
|
|
/* choose a good default
|
|
|
|
*
|
|
|
|
* the event_handler list is sorted by 'goodness'
|
|
|
|
* taking the first available should be the best solution
|
|
|
|
*/
|
2019-12-25 04:15:02 +00:00
|
|
|
et = event_handlers[0].et;
|
|
|
|
*event_handler_name = event_handlers[0].name;
|
2017-11-01 04:16:38 +00:00
|
|
|
|
2019-12-25 04:15:02 +00:00
|
|
|
if (FDEVENT_HANDLER_UNSET == et) {
|
|
|
|
log_error(errh, __FILE__, __LINE__,
|
2019-11-25 06:54:08 +00:00
|
|
|
"sorry, there is no event handler for this system");
|
2017-11-01 04:16:38 +00:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* User override
|
|
|
|
*/
|
|
|
|
|
2019-12-10 03:13:44 +00:00
|
|
|
for (uint32_t i = 0; event_handlers[i].name; ++i) {
|
2019-12-25 04:15:02 +00:00
|
|
|
if (0 == strcmp(event_handlers[i].name, event_handler)) {
|
|
|
|
et = event_handlers[i].et;
|
2017-11-01 04:16:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-25 04:15:02 +00:00
|
|
|
if (FDEVENT_HANDLER_UNSET == et) {
|
|
|
|
log_error(errh, __FILE__, __LINE__,
|
2019-11-16 01:26:54 +00:00
|
|
|
"the selected event-handler in unknown or not supported: %s",
|
2019-12-25 04:15:02 +00:00
|
|
|
event_handler);
|
2017-11-01 04:16:38 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-25 04:15:02 +00:00
|
|
|
return et;
|
2017-11-01 04:16:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char * fdevent_show_event_handlers(void) {
|
|
|
|
return
|
|
|
|
"\nEvent Handlers:\n\n"
|
|
|
|
#ifdef FDEVENT_USE_SELECT
|
|
|
|
"\t+ select (generic)\n"
|
|
|
|
#else
|
|
|
|
"\t- select (generic)\n"
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_POLL
|
|
|
|
"\t+ poll (Unix)\n"
|
|
|
|
#else
|
|
|
|
"\t- poll (Unix)\n"
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_LINUX_EPOLL
|
|
|
|
"\t+ epoll (Linux)\n"
|
|
|
|
#else
|
|
|
|
"\t- epoll (Linux)\n"
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_SOLARIS_DEVPOLL
|
|
|
|
"\t+ /dev/poll (Solaris)\n"
|
|
|
|
#else
|
|
|
|
"\t- /dev/poll (Solaris)\n"
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_SOLARIS_PORT
|
|
|
|
"\t+ eventports (Solaris)\n"
|
|
|
|
#else
|
|
|
|
"\t- eventports (Solaris)\n"
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_FREEBSD_KQUEUE
|
|
|
|
"\t+ kqueue (FreeBSD)\n"
|
|
|
|
#else
|
|
|
|
"\t- kqueue (FreeBSD)\n"
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_LIBEV
|
|
|
|
"\t+ libev (generic)\n"
|
|
|
|
#else
|
|
|
|
"\t- libev (generic)\n"
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2019-12-25 04:15:02 +00:00
|
|
|
fdevents * fdevent_init(const char *event_handler, int *max_fds, int *cur_fds, log_error_st *errh) {
|
2005-02-20 14:27:00 +00:00
|
|
|
fdevents *ev;
|
2019-12-25 04:15:02 +00:00
|
|
|
uint32_t maxfds = (0 != *max_fds)
|
|
|
|
? (uint32_t)*max_fds
|
|
|
|
: 4096;
|
|
|
|
int type = fdevent_config(&event_handler, errh);
|
|
|
|
if (type <= 0) return NULL;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2017-04-15 21:38:15 +00:00
|
|
|
#ifdef SOCK_CLOEXEC
|
|
|
|
/* Test if SOCK_CLOEXEC is supported by kernel.
|
|
|
|
* Linux kernels < 2.6.27 might return EINVAL if SOCK_CLOEXEC used
|
|
|
|
* https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=529929
|
2018-04-11 01:37:39 +00:00
|
|
|
* http://www.linksysinfo.org/index.php?threads/lighttpd-no-longer-starts-toastman-1-28-0510-7.73132/
|
|
|
|
* Test if SOCK_NONBLOCK is ignored by kernel on sockets.
|
|
|
|
* (reported on Android running a custom ROM)
|
|
|
|
* https://redmine.lighttpd.net/issues/2883
|
|
|
|
*/
|
2019-03-05 02:59:47 +00:00
|
|
|
#ifdef SOCK_NONBLOCK
|
2018-04-11 01:37:39 +00:00
|
|
|
int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
2019-03-05 02:59:47 +00:00
|
|
|
#else
|
|
|
|
int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
|
|
|
#endif
|
2017-04-15 21:38:15 +00:00
|
|
|
if (fd >= 0) {
|
2018-04-11 01:37:39 +00:00
|
|
|
int flags = fcntl(fd, F_GETFL, 0);
|
2019-03-05 02:59:47 +00:00
|
|
|
#ifdef SOCK_NONBLOCK
|
2018-04-11 01:37:39 +00:00
|
|
|
use_sock_nonblock = (-1 != flags && (flags & O_NONBLOCK));
|
2019-03-05 02:59:47 +00:00
|
|
|
#endif
|
2017-04-15 21:38:15 +00:00
|
|
|
use_sock_cloexec = 1;
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-11-01 04:16:38 +00:00
|
|
|
#ifdef FDEVENT_USE_SELECT
|
2019-12-25 04:15:02 +00:00
|
|
|
/* select limits itself
|
|
|
|
* as it is a hard limit and will lead to a segfault we add some safety
|
|
|
|
* */
|
2017-11-01 04:16:38 +00:00
|
|
|
if (type == FDEVENT_HANDLER_SELECT) {
|
2019-12-25 04:15:02 +00:00
|
|
|
if (maxfds > (uint32_t)FD_SETSIZE - 200)
|
|
|
|
maxfds = (uint32_t)FD_SETSIZE - 200;
|
2017-11-01 04:16:38 +00:00
|
|
|
}
|
|
|
|
#endif
|
2019-12-25 04:15:02 +00:00
|
|
|
*max_fds = (int)maxfds;
|
|
|
|
++maxfds; /*(+1 for event-handler fd)*/
|
2017-11-01 04:16:38 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
ev = calloc(1, sizeof(*ev));
|
2016-01-30 13:59:07 +00:00
|
|
|
force_assert(NULL != ev);
|
2019-12-25 04:15:02 +00:00
|
|
|
ev->errh = errh;
|
|
|
|
ev->cur_fds = cur_fds;
|
|
|
|
ev->event_handler = event_handler;
|
2005-02-20 14:27:00 +00:00
|
|
|
ev->fdarray = calloc(maxfds, sizeof(*ev->fdarray));
|
2017-02-13 19:24:53 +00:00
|
|
|
if (NULL == ev->fdarray) {
|
2019-12-25 04:15:02 +00:00
|
|
|
log_error(ev->errh, __FILE__, __LINE__,
|
2019-12-10 03:13:44 +00:00
|
|
|
"server.max-fds too large? (%u)", maxfds-1);
|
2017-02-13 19:24:53 +00:00
|
|
|
free(ev);
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
ev->maxfds = maxfds;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
switch(type) {
|
2019-02-23 06:35:58 +00:00
|
|
|
#ifdef FDEVENT_USE_POLL
|
2005-02-20 14:27:00 +00:00
|
|
|
case FDEVENT_HANDLER_POLL:
|
2019-02-23 06:35:58 +00:00
|
|
|
if (0 == fdevent_poll_init(ev)) return ev;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_SELECT
|
2005-02-20 14:27:00 +00:00
|
|
|
case FDEVENT_HANDLER_SELECT:
|
2019-02-23 06:35:58 +00:00
|
|
|
if (0 == fdevent_select_init(ev)) return ev;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_LINUX_EPOLL
|
2005-02-20 14:27:00 +00:00
|
|
|
case FDEVENT_HANDLER_LINUX_SYSEPOLL:
|
2019-02-23 06:35:58 +00:00
|
|
|
if (0 == fdevent_linux_sysepoll_init(ev)) return ev;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_SOLARIS_DEVPOLL
|
2005-02-20 14:27:00 +00:00
|
|
|
case FDEVENT_HANDLER_SOLARIS_DEVPOLL:
|
2019-02-23 06:35:58 +00:00
|
|
|
if (0 == fdevent_solaris_devpoll_init(ev)) return ev;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_SOLARIS_PORT
|
2011-06-13 17:34:57 +00:00
|
|
|
case FDEVENT_HANDLER_SOLARIS_PORT:
|
2019-02-23 06:35:58 +00:00
|
|
|
if (0 == fdevent_solaris_port_init(ev)) return ev;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_FREEBSD_KQUEUE
|
2005-02-20 14:27:00 +00:00
|
|
|
case FDEVENT_HANDLER_FREEBSD_KQUEUE:
|
2019-02-23 06:35:58 +00:00
|
|
|
if (0 == fdevent_freebsd_kqueue_init(ev)) return ev;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef FDEVENT_USE_LIBEV
|
2010-08-07 10:46:34 +00:00
|
|
|
case FDEVENT_HANDLER_LIBEV:
|
2019-02-23 06:35:58 +00:00
|
|
|
if (0 == fdevent_libev_init(ev)) return ev;
|
|
|
|
break;
|
|
|
|
#endif
|
2020-07-10 23:35:26 +00:00
|
|
|
/*case FDEVENT_HANDLER_UNSET:*/
|
2017-03-28 04:04:31 +00:00
|
|
|
default:
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-11-13 11:43:26 +00:00
|
|
|
free(ev->fdarray);
|
|
|
|
free(ev);
|
|
|
|
|
2019-12-25 04:15:02 +00:00
|
|
|
log_error(errh, __FILE__, __LINE__,
|
2019-11-16 01:26:54 +00:00
|
|
|
"event-handler failed: %s; "
|
|
|
|
"try to set server.event-handler = \"poll\" or \"select\"",
|
2019-12-25 04:15:02 +00:00
|
|
|
event_handler);
|
2010-08-07 10:46:34 +00:00
|
|
|
return NULL;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void fdevent_free(fdevents *ev) {
|
|
|
|
if (!ev) return;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (ev->free) ev->free(ev);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-12-10 03:13:44 +00:00
|
|
|
for (uint32_t i = 0; i < ev->maxfds; ++i) {
|
2017-10-21 16:20:27 +00:00
|
|
|
/* (fdevent_sched_run() should already have been run,
|
|
|
|
* but take reasonable precautions anyway) */
|
|
|
|
if (ev->fdarray[i])
|
|
|
|
free((fdnode *)((uintptr_t)ev->fdarray[i] & ~0x3));
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(ev->fdarray);
|
|
|
|
free(ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
int fdevent_reset(fdevents *ev) {
|
2019-02-23 06:35:58 +00:00
|
|
|
int rc = (NULL != ev->reset) ? ev->reset(ev) : 0;
|
|
|
|
if (-1 == rc) {
|
2019-12-25 04:15:02 +00:00
|
|
|
log_error(ev->errh, __FILE__, __LINE__,
|
2019-11-16 01:26:54 +00:00
|
|
|
"event-handler failed: %s; "
|
|
|
|
"try to set server.event-handler = \"poll\" or \"select\"",
|
2019-12-25 04:15:02 +00:00
|
|
|
ev->event_handler ? ev->event_handler : "");
|
2019-02-23 06:35:58 +00:00
|
|
|
}
|
|
|
|
return rc;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2012-08-31 14:11:41 +00:00
|
|
|
static fdnode *fdnode_init(void) {
|
2019-02-23 06:35:58 +00:00
|
|
|
return calloc(1, sizeof(fdnode));
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2009-03-07 21:05:37 +00:00
|
|
|
static void fdnode_free(fdnode *fdn) {
|
2005-02-20 14:27:00 +00:00
|
|
|
free(fdn);
|
|
|
|
}
|
|
|
|
|
2019-03-01 04:58:49 +00:00
|
|
|
fdnode * fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx) {
|
2019-02-23 06:35:58 +00:00
|
|
|
fdnode *fdn = ev->fdarray[fd] = fdnode_init();
|
|
|
|
force_assert(NULL != fdn);
|
2005-02-20 14:27:00 +00:00
|
|
|
fdn->handler = handler;
|
|
|
|
fdn->fd = fd;
|
|
|
|
fdn->ctx = ctx;
|
2010-08-17 09:54:42 +00:00
|
|
|
fdn->events = 0;
|
2019-02-23 06:35:58 +00:00
|
|
|
fdn->fde_ndx = -1;
|
2019-02-17 23:35:05 +00:00
|
|
|
#ifdef FDEVENT_USE_LIBEV
|
|
|
|
fdn->handler_ctx = NULL;
|
|
|
|
#endif
|
2019-03-01 04:58:49 +00:00
|
|
|
return fdn;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 06:35:58 +00:00
|
|
|
void fdevent_unregister(fdevents *ev, int fd) {
|
|
|
|
fdnode *fdn = ev->fdarray[fd];
|
|
|
|
if ((uintptr_t)fdn & 0x3) return; /*(should not happen)*/
|
2005-02-20 14:27:00 +00:00
|
|
|
ev->fdarray[fd] = NULL;
|
2019-02-23 06:35:58 +00:00
|
|
|
fdnode_free(fdn);
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2016-08-24 19:30:11 +00:00
|
|
|
void fdevent_sched_close(fdevents *ev, int fd, int issock) {
|
2019-02-23 06:35:58 +00:00
|
|
|
fdnode *fdn = ev->fdarray[fd];
|
2017-10-21 16:20:27 +00:00
|
|
|
if ((uintptr_t)fdn & 0x3) return;
|
|
|
|
ev->fdarray[fd] = (fdnode *)((uintptr_t)fdn | (issock ? 0x1 : 0x2));
|
2019-02-27 05:32:29 +00:00
|
|
|
fdn->handler = (fdevent_handler)NULL;
|
2017-10-21 16:20:27 +00:00
|
|
|
fdn->ctx = ev->pendclose;
|
|
|
|
ev->pendclose = fdn;
|
2016-08-24 19:30:11 +00:00
|
|
|
}
|
|
|
|
|
2019-02-25 04:43:54 +00:00
|
|
|
static void fdevent_sched_run(fdevents *ev) {
|
2017-10-21 16:20:27 +00:00
|
|
|
for (fdnode *fdn = ev->pendclose; fdn; ) {
|
|
|
|
int fd, rc;
|
|
|
|
fdnode *fdn_tmp;
|
2016-08-24 19:30:11 +00:00
|
|
|
#ifdef _WIN32
|
2017-10-21 16:20:27 +00:00
|
|
|
rc = (uintptr_t)fdn & 0x3;
|
|
|
|
#endif
|
|
|
|
fdn = (fdnode *)((uintptr_t)fdn & ~0x3);
|
|
|
|
fd = fdn->fd;
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (rc == 0x1) {
|
2016-08-24 19:30:11 +00:00
|
|
|
rc = closesocket(fd);
|
|
|
|
}
|
2017-10-21 16:20:27 +00:00
|
|
|
else if (rc == 0x2) {
|
2016-08-24 19:30:11 +00:00
|
|
|
rc = close(fd);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
rc = close(fd);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (0 != rc) {
|
2019-12-25 04:15:02 +00:00
|
|
|
log_perror(ev->errh, __FILE__, __LINE__, "close failed %d", fd);
|
2016-08-24 19:30:11 +00:00
|
|
|
}
|
2017-10-21 16:20:27 +00:00
|
|
|
else {
|
2019-12-25 04:15:02 +00:00
|
|
|
--(*ev->cur_fds);
|
2017-10-21 16:20:27 +00:00
|
|
|
}
|
2016-08-24 19:30:11 +00:00
|
|
|
|
2017-10-21 16:20:27 +00:00
|
|
|
fdn_tmp = fdn;
|
|
|
|
fdn = (fdnode *)fdn->ctx; /* next */
|
|
|
|
/*(fdevent_unregister)*/
|
|
|
|
fdnode_free(fdn_tmp);
|
2016-08-24 19:30:11 +00:00
|
|
|
ev->fdarray[fd] = NULL;
|
|
|
|
}
|
2017-10-21 16:20:27 +00:00
|
|
|
ev->pendclose = NULL;
|
2016-08-24 19:30:11 +00:00
|
|
|
}
|
|
|
|
|
2019-09-07 16:22:04 +00:00
|
|
|
__attribute_cold__
|
|
|
|
__attribute_noinline__
|
|
|
|
static int fdevent_fdnode_event_unsetter_retry(fdevents *ev, fdnode *fdn) {
|
|
|
|
do {
|
|
|
|
switch (errno) {
|
|
|
|
#ifdef EWOULDBLOCK
|
|
|
|
#if EAGAIN != EWOULDBLOCK
|
|
|
|
case EWOULDBLOCK:
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
case EAGAIN:
|
|
|
|
case EINTR:
|
|
|
|
/* temporary error; retry */
|
|
|
|
break;
|
|
|
|
/*case ENOMEM:*/
|
|
|
|
default:
|
|
|
|
/* unrecoverable error; might leak fd */
|
2019-12-25 04:15:02 +00:00
|
|
|
log_perror(ev->errh, __FILE__, __LINE__,
|
2019-11-25 06:54:08 +00:00
|
|
|
"fdevent event_del failed on fd %d", fdn->fd);
|
2019-09-07 16:22:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} while (0 != ev->event_del(ev, fdn));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-03-01 04:58:49 +00:00
|
|
|
static void fdevent_fdnode_event_unsetter(fdevents *ev, fdnode *fdn) {
|
2019-02-23 06:35:58 +00:00
|
|
|
if (-1 == fdn->fde_ndx) return;
|
2019-09-07 16:22:04 +00:00
|
|
|
if (0 != ev->event_del(ev, fdn))
|
|
|
|
fdevent_fdnode_event_unsetter_retry(ev, fdn);
|
|
|
|
fdn->fde_ndx = -1;
|
|
|
|
fdn->events = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
__attribute_noinline__
|
|
|
|
static int fdevent_fdnode_event_setter_retry(fdevents *ev, fdnode *fdn, int events) {
|
|
|
|
do {
|
|
|
|
switch (errno) {
|
|
|
|
#ifdef EWOULDBLOCK
|
|
|
|
#if EAGAIN != EWOULDBLOCK
|
|
|
|
case EWOULDBLOCK:
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
case EAGAIN:
|
|
|
|
case EINTR:
|
|
|
|
/* temporary error; retry */
|
|
|
|
break;
|
|
|
|
/*case ENOMEM:*/
|
|
|
|
default:
|
|
|
|
/* unrecoverable error */
|
2019-12-25 04:15:02 +00:00
|
|
|
log_perror(ev->errh, __FILE__, __LINE__,
|
2019-11-25 06:54:08 +00:00
|
|
|
"fdevent event_set failed on fd %d", fdn->fd);
|
2019-09-07 16:22:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} while (0 != ev->event_set(ev, fdn, events));
|
|
|
|
return 1;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2019-03-01 04:58:49 +00:00
|
|
|
static void fdevent_fdnode_event_setter(fdevents *ev, fdnode *fdn, int events) {
|
2019-02-23 06:35:58 +00:00
|
|
|
/*(Note: skips registering with kernel if initial events is 0,
|
|
|
|
* so caller should pass non-zero events for initial registration.
|
|
|
|
* If never registered due to never being called with non-zero events,
|
|
|
|
* then FDEVENT_HUP or FDEVENT_ERR will never be returned.) */
|
|
|
|
if (fdn->events == events) return;/*(no change; nothing to do)*/
|
|
|
|
|
2019-09-07 16:22:04 +00:00
|
|
|
if (0 == ev->event_set(ev, fdn, events)
|
|
|
|
|| fdevent_fdnode_event_setter_retry(ev, fdn, events))
|
2019-02-23 06:35:58 +00:00
|
|
|
fdn->events = events;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-03-01 04:58:49 +00:00
|
|
|
void fdevent_fdnode_event_del(fdevents *ev, fdnode *fdn) {
|
|
|
|
if (NULL != fdn) fdevent_fdnode_event_unsetter(ev, fdn);
|
2019-02-23 06:35:58 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-03-01 04:58:49 +00:00
|
|
|
void fdevent_fdnode_event_set(fdevents *ev, fdnode *fdn, int events) {
|
|
|
|
if (NULL != fdn) fdevent_fdnode_event_setter(ev, fdn, events);
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2019-03-01 04:58:49 +00:00
|
|
|
void fdevent_fdnode_event_add(fdevents *ev, fdnode *fdn, int event) {
|
|
|
|
if (NULL != fdn) fdevent_fdnode_event_setter(ev, fdn, (fdn->events|event));
|
[core] option to stream response body to client (fixes #949, #760, #1283, #1387)
Set server.stream-response-body = 1 or server.stream-response-body = 2
to have lighttpd stream response body to client as it arrives from the
backend (CGI, FastCGI, SCGI, proxy).
default: buffer entire response body before sending response to client.
(This preserves existing behavior for now, but may in the future be
changed to stream response to client, which is the behavior more
commonly expected.)
x-ref:
"fastcgi, cgi, flush, php5 problem."
https://redmine.lighttpd.net/issues/949
"Random crashing on FreeBSD 6.1"
https://redmine.lighttpd.net/issues/760
"Memory usage increases when proxy+ssl+large file"
https://redmine.lighttpd.net/issues/1283
"lighttpd+fastcgi memory problem"
https://redmine.lighttpd.net/issues/1387
2016-06-11 15:04:01 +00:00
|
|
|
}
|
|
|
|
|
2019-03-01 04:58:49 +00:00
|
|
|
void fdevent_fdnode_event_clr(fdevents *ev, fdnode *fdn, int event) {
|
|
|
|
if (NULL != fdn) fdevent_fdnode_event_setter(ev, fdn, (fdn->events&~event));
|
[core] option to stream response body to client (fixes #949, #760, #1283, #1387)
Set server.stream-response-body = 1 or server.stream-response-body = 2
to have lighttpd stream response body to client as it arrives from the
backend (CGI, FastCGI, SCGI, proxy).
default: buffer entire response body before sending response to client.
(This preserves existing behavior for now, but may in the future be
changed to stream response to client, which is the behavior more
commonly expected.)
x-ref:
"fastcgi, cgi, flush, php5 problem."
https://redmine.lighttpd.net/issues/949
"Random crashing on FreeBSD 6.1"
https://redmine.lighttpd.net/issues/760
"Memory usage increases when proxy+ssl+large file"
https://redmine.lighttpd.net/issues/1283
"lighttpd+fastcgi memory problem"
https://redmine.lighttpd.net/issues/1387
2016-06-11 15:04:01 +00:00
|
|
|
}
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
int fdevent_poll(fdevents *ev, int timeout_ms) {
|
2019-02-25 04:43:54 +00:00
|
|
|
int n = ev->poll(ev, timeout_ms);
|
|
|
|
if (n >= 0)
|
|
|
|
fdevent_sched_run(ev);
|
|
|
|
else if (errno != EINTR)
|
2019-12-25 04:15:02 +00:00
|
|
|
log_perror(ev->errh, __FILE__, __LINE__, "fdevent_poll failed");
|
2019-02-25 04:43:54 +00:00
|
|
|
return n;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2017-04-15 21:38:15 +00:00
|
|
|
void fdevent_setfd_cloexec(int fd) {
|
2005-02-20 14:27:00 +00:00
|
|
|
#ifdef FD_CLOEXEC
|
2014-02-16 13:08:29 +00:00
|
|
|
if (fd < 0) return;
|
|
|
|
force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
|
|
|
|
#else
|
|
|
|
UNUSED(fd);
|
2005-02-20 14:27:00 +00:00
|
|
|
#endif
|
2014-02-16 13:08:29 +00:00
|
|
|
}
|
|
|
|
|
2017-04-15 21:38:15 +00:00
|
|
|
void fdevent_clrfd_cloexec(int fd) {
|
|
|
|
#ifdef FD_CLOEXEC
|
|
|
|
if (fd >= 0) force_assert(-1 != fcntl(fd, F_SETFD, 0));
|
|
|
|
#else
|
|
|
|
UNUSED(fd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-12-21 17:29:09 +00:00
|
|
|
int fdevent_fcntl_set_nb(int fd) {
|
2006-10-04 13:26:23 +00:00
|
|
|
#ifdef O_NONBLOCK
|
2005-02-20 14:27:00 +00:00
|
|
|
return fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
|
|
|
|
#else
|
2017-07-29 04:30:53 +00:00
|
|
|
UNUSED(fd);
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-12-21 17:29:09 +00:00
|
|
|
int fdevent_fcntl_set_nb_cloexec(int fd) {
|
2017-06-20 03:25:39 +00:00
|
|
|
fdevent_setfd_cloexec(fd);
|
2019-12-21 17:29:09 +00:00
|
|
|
return fdevent_fcntl_set_nb(fd);
|
2013-12-24 04:28:44 +00:00
|
|
|
}
|
|
|
|
|
2019-12-21 17:29:09 +00:00
|
|
|
int fdevent_fcntl_set_nb_cloexec_sock(int fd) {
|
2013-12-24 04:28:44 +00:00
|
|
|
#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
|
2018-04-11 01:37:39 +00:00
|
|
|
if (use_sock_cloexec && use_sock_nonblock)
|
2017-07-29 04:30:53 +00:00
|
|
|
return 0;
|
2013-12-24 04:28:44 +00:00
|
|
|
#endif
|
2019-12-21 17:29:09 +00:00
|
|
|
return fdevent_fcntl_set_nb_cloexec(fd);
|
2013-12-24 04:28:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int fdevent_socket_cloexec(int domain, int type, int protocol) {
|
|
|
|
int fd;
|
2017-04-15 21:38:15 +00:00
|
|
|
#ifdef SOCK_CLOEXEC
|
|
|
|
if (use_sock_cloexec)
|
|
|
|
return socket(domain, type | SOCK_CLOEXEC, protocol);
|
|
|
|
#endif
|
2013-12-24 04:28:44 +00:00
|
|
|
if (-1 != (fd = socket(domain, type, protocol))) {
|
|
|
|
#ifdef FD_CLOEXEC
|
2017-05-16 03:59:22 +00:00
|
|
|
force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
|
2013-12-24 04:28:44 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fdevent_socket_nb_cloexec(int domain, int type, int protocol) {
|
|
|
|
int fd;
|
2017-04-15 21:38:15 +00:00
|
|
|
#ifdef SOCK_CLOEXEC
|
2019-03-05 02:59:47 +00:00
|
|
|
#ifdef SOCK_NONBLOCK
|
2018-04-11 01:37:39 +00:00
|
|
|
if (use_sock_cloexec && use_sock_nonblock)
|
2017-04-15 21:38:15 +00:00
|
|
|
return socket(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol);
|
2019-03-05 02:59:47 +00:00
|
|
|
#else
|
|
|
|
if (use_sock_cloexec) {
|
|
|
|
fd = socket(domain, type | SOCK_CLOEXEC, protocol);
|
|
|
|
#ifdef O_NONBLOCK
|
|
|
|
if (-1 != fd) force_assert(-1 != fcntl(fd,F_SETFL,O_NONBLOCK|O_RDWR));
|
|
|
|
#endif
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
#endif
|
2017-04-15 21:38:15 +00:00
|
|
|
#endif
|
2013-12-24 04:28:44 +00:00
|
|
|
if (-1 != (fd = socket(domain, type, protocol))) {
|
|
|
|
#ifdef FD_CLOEXEC
|
2017-05-16 03:59:22 +00:00
|
|
|
force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
|
2013-12-24 04:28:44 +00:00
|
|
|
#endif
|
|
|
|
#ifdef O_NONBLOCK
|
2017-05-16 03:59:22 +00:00
|
|
|
force_assert(-1 != fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR));
|
2013-12-24 04:28:44 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2020-10-09 15:19:13 +00:00
|
|
|
int fdevent_dup_cloexec (int fd) {
|
|
|
|
#ifdef F_DUPFD_CLOEXEC
|
|
|
|
return fcntl(fd, F_DUPFD_CLOEXEC, 3);
|
|
|
|
#else
|
|
|
|
const int newfd = fcntl(fd, F_DUPFD, 3);
|
|
|
|
if (newfd >= 0) fdevent_setfd_cloexec(newfd);
|
|
|
|
return newfd;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-03-11 03:06:49 +00:00
|
|
|
#ifndef O_BINARY
|
|
|
|
#define O_BINARY 0
|
|
|
|
#endif
|
|
|
|
#ifndef O_LARGEFILE
|
|
|
|
#define O_LARGEFILE 0
|
|
|
|
#endif
|
2013-12-24 04:28:44 +00:00
|
|
|
#ifndef O_NOCTTY
|
|
|
|
#define O_NOCTTY 0
|
|
|
|
#endif
|
2019-03-11 03:06:49 +00:00
|
|
|
#ifndef O_NOFOLLOW
|
|
|
|
#define O_NOFOLLOW 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*(O_NOFOLLOW is not handled here)*/
|
|
|
|
/*(Note: O_NOFOLLOW affects only the final path segment, the target file,
|
|
|
|
* not any intermediate symlinks along the path)*/
|
2013-12-24 04:28:44 +00:00
|
|
|
|
2019-03-11 03:06:49 +00:00
|
|
|
/* O_CLOEXEC handled further below, if defined) */
|
|
|
|
#ifdef O_NONBLOCK
|
|
|
|
#define FDEVENT_O_FLAGS \
|
|
|
|
(O_BINARY | O_LARGEFILE | O_NOCTTY | O_NONBLOCK)
|
|
|
|
#else
|
|
|
|
#define FDEVENT_O_FLAGS \
|
|
|
|
(O_BINARY | O_LARGEFILE | O_NOCTTY )
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int fdevent_open_cloexec(const char *pathname, int symlinks, int flags, mode_t mode) {
|
|
|
|
if (!symlinks) flags |= O_NOFOLLOW;
|
2013-12-24 04:28:44 +00:00
|
|
|
#ifdef O_CLOEXEC
|
2019-03-11 03:06:49 +00:00
|
|
|
return open(pathname, flags | O_CLOEXEC | FDEVENT_O_FLAGS, mode);
|
2013-12-24 04:28:44 +00:00
|
|
|
#else
|
2019-03-11 03:06:49 +00:00
|
|
|
int fd = open(pathname, flags | FDEVENT_O_FLAGS, mode);
|
2013-12-24 04:28:44 +00:00
|
|
|
#ifdef FD_CLOEXEC
|
|
|
|
if (fd != -1)
|
2017-05-16 03:59:22 +00:00
|
|
|
force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC));
|
2013-12-24 04:28:44 +00:00
|
|
|
#endif
|
|
|
|
return fd;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
|
2017-06-20 03:00:45 +00:00
|
|
|
int fdevent_open_devnull(void) {
|
|
|
|
#if defined(_WIN32)
|
2019-03-11 03:06:49 +00:00
|
|
|
return fdevent_open_cloexec("nul", 0, O_RDWR, 0);
|
2017-06-20 03:00:45 +00:00
|
|
|
#else
|
2019-03-11 03:06:49 +00:00
|
|
|
return fdevent_open_cloexec("/dev/null", 0, O_RDWR, 0);
|
2017-06-20 03:00:45 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-03-11 03:06:49 +00:00
|
|
|
int fdevent_open_dirname(char *path, int symlinks) {
|
2017-06-20 03:00:45 +00:00
|
|
|
/*(handle special cases of no dirname or dirname is root directory)*/
|
|
|
|
char * const c = strrchr(path, '/');
|
|
|
|
const char * const dname = (NULL != c ? c == path ? "/" : path : ".");
|
|
|
|
int dfd;
|
|
|
|
int flags = O_RDONLY;
|
|
|
|
#ifdef O_DIRECTORY
|
|
|
|
flags |= O_DIRECTORY;
|
|
|
|
#endif
|
|
|
|
if (NULL != c) *c = '\0';
|
2019-03-11 03:06:49 +00:00
|
|
|
dfd = fdevent_open_cloexec(dname, symlinks, flags, 0);
|
2017-06-20 03:00:45 +00:00
|
|
|
if (NULL != c) *c = '/';
|
|
|
|
return dfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-03-16 05:38:16 +00:00
|
|
|
int fdevent_mkstemp_append(char *path) {
|
|
|
|
#ifdef __COVERITY__
|
|
|
|
/* POSIX-2008 requires mkstemp create file with 0600 perms */
|
|
|
|
umask(0600);
|
|
|
|
#endif
|
|
|
|
/* coverity[secure_temp : FALSE] */
|
|
|
|
const int fd = mkstemp(path);
|
|
|
|
if (fd < 0) return fd;
|
|
|
|
|
|
|
|
if (0 != fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_APPEND)) {
|
|
|
|
/* (should not happen; fd is regular file) */
|
|
|
|
int errnum = errno;
|
|
|
|
close(fd);
|
|
|
|
errno = errnum;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fdevent_setfd_cloexec(fd);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-15 21:38:15 +00:00
|
|
|
int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen) {
|
|
|
|
int fd;
|
|
|
|
socklen_t len = (socklen_t) *addrlen;
|
|
|
|
|
|
|
|
#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
|
|
|
|
#if defined(__NetBSD__)
|
2018-03-25 04:57:38 +00:00
|
|
|
const int sock_cloexec = 1;
|
2017-04-15 21:38:15 +00:00
|
|
|
fd = paccept(listenfd, addr, &len, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK);
|
|
|
|
#else
|
2018-03-25 04:57:38 +00:00
|
|
|
int sock_cloexec = use_sock_cloexec;
|
|
|
|
if (sock_cloexec) {
|
|
|
|
fd = accept4(listenfd, addr, &len, SOCK_CLOEXEC | SOCK_NONBLOCK);
|
2018-04-11 01:37:39 +00:00
|
|
|
if (fd >= 0) {
|
|
|
|
if (!use_sock_nonblock) {
|
2019-12-21 17:29:09 +00:00
|
|
|
if (0 != fdevent_fcntl_set_nb(fd)) {
|
2018-04-11 01:37:39 +00:00
|
|
|
close(fd);
|
|
|
|
fd = -1;
|
|
|
|
}
|
|
|
|
}
|
2020-01-01 20:39:32 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
switch (errno) {
|
|
|
|
case ENOSYS:
|
|
|
|
case ENOTSUP:
|
|
|
|
case EPERM:
|
|
|
|
fd = accept(listenfd, addr, &len);
|
|
|
|
sock_cloexec = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-03-25 04:57:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fd = accept(listenfd, addr, &len);
|
|
|
|
}
|
2017-04-15 21:38:15 +00:00
|
|
|
#endif
|
|
|
|
#else
|
2018-03-25 04:57:38 +00:00
|
|
|
const int sock_cloexec = 0;
|
2017-04-15 21:38:15 +00:00
|
|
|
fd = accept(listenfd, addr, &len);
|
|
|
|
#endif
|
|
|
|
|
2018-03-25 04:57:38 +00:00
|
|
|
if (fd >= 0) {
|
|
|
|
*addrlen = (size_t)len;
|
2019-12-21 17:29:09 +00:00
|
|
|
if (!sock_cloexec && 0 != fdevent_fcntl_set_nb_cloexec(fd)) {
|
2018-03-25 04:57:38 +00:00
|
|
|
close(fd);
|
|
|
|
fd = -1;
|
|
|
|
}
|
|
|
|
}
|
2017-04-15 21:38:15 +00:00
|
|
|
return fd;
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
|
2016-08-06 08:11:16 +00:00
|
|
|
|
2018-03-25 07:45:05 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <crt_externs.h>
|
|
|
|
#define environ (* _NSGetEnviron())
|
|
|
|
#else
|
|
|
|
extern char **environ;
|
|
|
|
#endif
|
|
|
|
char ** fdevent_environ (void) { return environ; }
|
|
|
|
|
|
|
|
|
2017-06-20 03:00:45 +00:00
|
|
|
#ifdef FD_CLOEXEC
|
|
|
|
static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd) {
|
|
|
|
if (oldfd >= 0) {
|
|
|
|
if (oldfd != newfd) {
|
|
|
|
force_assert(oldfd > STDERR_FILENO);
|
|
|
|
if (newfd != dup2(oldfd, newfd)) return -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fdevent_clrfd_cloexec(newfd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newfd;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd, int reuse) {
|
|
|
|
if (oldfd >= 0) {
|
|
|
|
if (oldfd != newfd) {
|
|
|
|
force_assert(oldfd > STDERR_FILENO);
|
|
|
|
if (newfd != dup2(oldfd, newfd)) return -1;
|
|
|
|
if (!reuse) close(oldfd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newfd;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr) {
|
|
|
|
#ifdef FD_CLOEXEC
|
|
|
|
if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO))
|
|
|
|
return -1;
|
|
|
|
if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO))
|
|
|
|
return -1;
|
|
|
|
if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO))
|
|
|
|
return -1;
|
|
|
|
#else
|
|
|
|
if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO,
|
|
|
|
fdin == fdout
|
|
|
|
|| fdin == fderr))
|
|
|
|
return -1;
|
|
|
|
if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO,
|
|
|
|
fdout == fderr))
|
|
|
|
return -1;
|
|
|
|
if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO,
|
|
|
|
0))
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|