diff --git a/include/proto/fd.h b/include/proto/fd.h index 9b4c63279..72b2c6be8 100644 --- a/include/proto/fd.h +++ b/include/proto/fd.h @@ -22,6 +22,7 @@ #ifndef _PROTO_FD_H #define _PROTO_FD_H +#include #include #include #include @@ -46,6 +47,21 @@ void disable_poller(const char *poller_name); */ int init_pollers(); +/* + * Some pollers may lose their connection after a fork(). It may be necessary + * to create initialize part of them again. Returns 0 in case of failure, + * otherwise 1. The fork() function may be NULL if unused. In case of error, + * the the current poller is destroyed and the caller is responsible for trying + * another one by calling init_pollers() again. + */ +int fork_poller(); + +/* + * Lists the known pollers on . + * Should be performed only before initialization. + */ +int list_pollers(FILE *out); + /* * Runs the polling loop */ diff --git a/include/types/fd.h b/include/types/fd.h index 68335c033..37b5281ae 100644 --- a/include/types/fd.h +++ b/include/types/fd.h @@ -83,6 +83,8 @@ struct poller { REGPRM2 void (*poll)(struct poller *p, int wait_time); /* the poller itself */ REGPRM1 int (*init)(struct poller *p); /* poller initialization */ REGPRM1 void (*term)(struct poller *p); /* termination of this poller */ + REGPRM1 int (*test)(struct poller *p); /* pre-init check of the poller */ + REGPRM1 int (*fork)(struct poller *p); /* post-fork re-opening */ const char *name; /* poller name */ int pref; /* try pollers with higher preference first */ }; diff --git a/src/ev_epoll.c b/src/ev_epoll.c index 6931a0c5d..e37fe0d44 100644 --- a/src/ev_epoll.c +++ b/src/ev_epoll.c @@ -296,6 +296,21 @@ REGPRM1 static void epoll_term(struct poller *p) p->pref = 0; } +/* + * Check that the poller works. + * Returns 1 if OK, otherwise 0. + */ +REGPRM1 static int epoll_test(struct poller *p) +{ + int fd; + + fd = epoll_create(global.maxsock + 1); + if (fd < 0) + return 0; + close(fd); + return 1; +} + /* * The only exported function. Returns 1. */ @@ -305,6 +320,7 @@ int epoll_register(struct poller *p) p->pref = 300; p->private = NULL; + p->test = epoll_test; p->init = epoll_init; p->term = epoll_term; p->poll = epoll_poll; diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c index 395834311..3654994ce 100644 --- a/src/ev_kqueue.c +++ b/src/ev_kqueue.c @@ -197,19 +197,49 @@ REGPRM1 static void kqueue_term(struct poller *p) p->pref = 0; } +/* + * Check that the poller works. + * Returns 1 if OK, otherwise 0. + */ +REGPRM1 static int kqueue_test(struct poller *p) +{ + int fd; + + fd = kqueue(); + if (fd < 0) + return 0; + close(fd); + return 1; +} + +/* + * Recreate the kqueue file descriptor after a fork(). Returns 1 if OK, + * otherwise 0. Note that some pollers need to be reopened after a fork() + * (such as kqueue), and some others may fail to do so in a chroot. + */ +REGPRM1 static int kqueue_fork(struct poller *p) +{ + close(kqueue_fd); + kqueue_fd = kqueue(); + if (kqueue_fd < 0) + return 0; + return 1; +} + /* * The only exported function. Returns 1. */ -//int kqueue_native_register(struct poller *p) int kqueue_register(struct poller *p) { p->name = "kqueue"; p->pref = 300; p->private = NULL; + p->test = kqueue_test; p->init = kqueue_init; p->term = kqueue_term; p->poll = kqueue_poll; + p->fork = kqueue_fork; p->is_set = __fd_is_set; p->cond_s = p->set = __fd_set; diff --git a/src/ev_poll.c b/src/ev_poll.c index 06adeb42d..1dbf22889 100644 --- a/src/ev_poll.c +++ b/src/ev_poll.c @@ -204,6 +204,15 @@ REGPRM1 static void poll_term(struct poller *p) p->pref = 0; } +/* + * Check that the poller works. + * Returns 1 if OK, otherwise 0. + */ +REGPRM1 static int poll_test(struct poller *p) +{ + return 1; +} + /* * The only exported function. Returns 1. */ @@ -213,6 +222,7 @@ int poll_register(struct poller *p) p->pref = 200; p->private = NULL; + p->test = poll_test; p->init = poll_init; p->term = poll_term; p->poll = poll_poll; diff --git a/src/ev_select.c b/src/ev_select.c index c5dbedf25..6c1a13294 100644 --- a/src/ev_select.c +++ b/src/ev_select.c @@ -204,6 +204,15 @@ REGPRM1 static void select_term(struct poller *p) p->pref = 0; } +/* + * Check that the poller works. + * Returns 1 if OK, otherwise 0. + */ +REGPRM1 static int select_test(struct poller *p) +{ + return 1; +} + /* * The only exported function. Returns 1. */ @@ -213,6 +222,7 @@ int select_register(struct poller *p) p->pref = 150; p->private = NULL; + p->test = select_test; p->init = select_init; p->term = select_term; p->poll = select_poll; diff --git a/src/fd.c b/src/fd.c index 15a01cc69..5ddfebdd0 100644 --- a/src/fd.c +++ b/src/fd.c @@ -10,6 +10,7 @@ * */ +#include #include #include #include @@ -122,6 +123,75 @@ int init_pollers() return 0; } +/* + * Lists the known pollers on . + * Should be performed only before initialization. + */ +int list_pollers(FILE *out) +{ + int p; + int last, next; + int usable; + struct poller *bp; + + fprintf(out, "Available polling systems :\n"); + + usable = 0; + bp = NULL; + last = next = -1; + while (1) { + for (p = 0; p < nbpollers; p++) { + if (!bp || (pollers[p].pref > bp->pref)) + bp = &pollers[p]; + if ((next < 0 || pollers[p].pref > next) + && (last < 0 || pollers[p].pref < last)) + next = pollers[p].pref; + } + + if (next == -1) + break; + + for (p = 0; p < nbpollers; p++) { + if (pollers[p].pref == next) { + fprintf(out, " %10s : ", pollers[p].name); + if (pollers[p].pref == 0) + fprintf(out, "disabled, "); + else + fprintf(out, "pref=%3d, ", pollers[p].pref); + if (pollers[p].test(&pollers[p])) { + fprintf(out, " test result OK"); + if (next > 0) + usable++; + } else + fprintf(out, " test result FAILED"); + fprintf(out, "\n"); + } + } + last = next; + next = -1; + }; + fprintf(out, "Total: %d (%d usable), will use %s.\n", nbpollers, usable, bp ? bp->name : "none"); + return 0; +} + +/* + * Some pollers may lose their connection after a fork(). It may be necessary + * to create initialize part of them again. Returns 0 in case of failure, + * otherwise 1. The fork() function may be NULL if unused. In case of error, + * the the current poller is destroyed and the caller is responsible for trying + * another one by calling init_pollers() again. + */ +int fork_poller() +{ + if (cur_poller.fork) { + if (cur_poller.fork(&cur_poller)) + return 1; + cur_poller.term(&cur_poller); + return 0; + } + return 1; +} + /* * Local variables: * c-indent-level: 8 diff --git a/src/haproxy.c b/src/haproxy.c index 37d4b59a9..5ddf9352e 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -540,12 +540,15 @@ void init(int argc, char **argv) /* Note: we could disable any poller by name here */ + if (global.mode & (MODE_VERBOSE|MODE_DEBUG)) + list_pollers(stderr); + if (!init_pollers()) { - Alert("No polling mechanism available\n"); + Alert("No polling mechanism available.\n"); exit(1); } - if (global.mode & MODE_DEBUG) { - printf("Note: using %s() as the polling mechanism.\n", cur_poller.name); + if (global.mode & (MODE_VERBOSE|MODE_DEBUG)) { + printf("Using %s() as the polling mechanism.\n", cur_poller.name); } } @@ -909,6 +912,7 @@ int main(int argc, char **argv) } pid = getpid(); /* update child's pid */ setsid(); + fork_poller(); } /* diff --git a/src/proxy.c b/src/proxy.c index ff3cdc784..a5431c2b3 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -54,9 +54,14 @@ const char *proxy_type_str(struct proxy *proxy) /* - * this function starts all the proxies. Its return value is composed from - * ERR_NONE, ERR_RETRYABLE and ERR_FATAL. Retryable errors will only be printed - * if is not zero. + * This function creates all proxy sockets. It should be done very early, + * typically before privileges are dropped. The sockets will be registered + * but not added to any fd_set, in order not to loose them across the fork(). + * The proxies also start in IDLE state, meaning that it will be + * maintain_proxies that will finally complete their loading. + * + * Its return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL. + * Retryable errors will only be printed if is not zero. */ int start_proxies(int verbose) { @@ -147,13 +152,12 @@ int start_proxies(int verbose) fdtab[fd].cb[DIR_RD].b = fdtab[fd].cb[DIR_WR].b = NULL; fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy instead of a task */ fdtab[fd].state = FD_STLISTEN; - EV_FD_SET(fd, DIR_RD); fd_insert(fd); listeners++; } if (!pxerr) { - curproxy->state = PR_STRUN; + curproxy->state = PR_STIDLE; send_log(curproxy, LOG_NOTICE, "Proxy %s started.\n", curproxy->id); } }