]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MAJOR] delay registering of listener sockets at startup
authorWilly Tarreau <w@1wt.eu>
Mon, 9 Apr 2007 17:29:56 +0000 (19:29 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 9 Apr 2007 17:29:56 +0000 (19:29 +0200)
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.

include/proto/fd.h
include/types/fd.h
src/ev_epoll.c
src/ev_kqueue.c
src/ev_poll.c
src/ev_select.c
src/fd.c
src/haproxy.c
src/proxy.c

index 9b4c63279098375bd0d7882febc4621819df14c7..72b2c6be84246bdd53344b97d75e8d199d17d80e 100644 (file)
@@ -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
  */
index 68335c033c878be60af5d9846b940b359699c656..37b5281ae78ecfc400b4686345b10cf1c66551b4 100644 (file)
@@ -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 */
 };
index 6931a0c5d56fc56dde3c508d12850b28905f89fb..e37fe0d44b43e1b96a3d78bfae9f1cd76bae465d 100644 (file)
@@ -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;
index 39583431136a807a8e7833137688ff638a161213..3654994ce151b106f59634f820fa75d6f507b82c 100644 (file)
@@ -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;
index 06adeb42de4240ec48026f43b54293b483f3204f..1dbf22889607f4263c67042387fb7aa127225d88 100644 (file)
@@ -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;
index c5dbedf25a3cedc3ff1224dc940e1bcd90a1f9df..6c1a13294e3b2ba20ddba909d2a8bc7655ea9b6b 100644 (file)
@@ -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;
index 15a01cc6964ff77a2acd1ebefaa2b4c0535a6a2e..5ddfebdd0095901c5edc431371782c100e1dd12c 100644 (file)
--- a/src/fd.c
+++ b/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
index 37d4b59a9cb352c65ac2cbe5043e97de9cb0f9e7..5ddf9352e618aee44eb80f893ebd7697cd014279 100644 (file)
@@ -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();
        }
 
        /*
index ff3cdc7847f94042bed03f63570732a4da3de0ae..a5431c2b3f19ef85d7c44bf00d49b80f0f530990 100644 (file)
@@ -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);
                }
        }