mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 23:27:04 +02:00
[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.
This commit is contained in:
parent
8755285486
commit
2ff7622c0c
@ -22,6 +22,7 @@
|
||||
#ifndef _PROTO_FD_H
|
||||
#define _PROTO_FD_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
@ -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 <out>.
|
||||
* Should be performed only before initialization.
|
||||
*/
|
||||
int list_pollers(FILE *out);
|
||||
|
||||
/*
|
||||
* Runs the polling loop
|
||||
*/
|
||||
|
@ -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 */
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
70
src/fd.c
70
src/fd.c
@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
@ -122,6 +123,75 @@ int init_pollers()
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lists the known pollers on <out>.
|
||||
* 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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/*
|
||||
|
14
src/proxy.c
14
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 <verbose> 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 <verbose> 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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user