From: Willy Tarreau Date: Mon, 9 Apr 2007 17:29:56 +0000 (+0200) Subject: [MAJOR] delay registering of listener sockets at startup X-Git-Tag: v1.3.9~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2ff7622c0cf0d8f1ea45ccbe6f23d2e9449f7b91;p=thirdparty%2Fhaproxy.git [MAJOR] delay registering of listener sockets at startup Some pollers such as kqueue lose their FD across fork(), meaning that the registered file descriptors are lost too. Now when the proxies are started by start_proxies(), the file descriptors are not registered yet, leaving enough time for the fork() to take place and to get a new pollfd. It will be the first call to maintain_proxies that will register them. --- diff --git a/include/proto/fd.h b/include/proto/fd.h index 9b4c632790..72b2c6be84 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 68335c033c..37b5281ae7 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 6931a0c5d5..e37fe0d44b 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 3958343113..3654994ce1 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 06adeb42de..1dbf228896 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 c5dbedf25a..6c1a13294e 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 15a01cc696..5ddfebdd00 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 37d4b59a9c..5ddf9352e6 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 ff3cdc7847..a5431c2b3f 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); } }