BUG/MEDIUM: mworker: does not close inherited FD
At the end of the master initialisation, a call to protocol_unbind_all()
was made, in order to close all the FDs.
Unfortunately, this function closes the inherited FDs (fd@), upon reload
the master wasn't able to reload a configuration with those FDs.
The create_listeners() function now store a flag to specify if the fd
was inherited or not.
Replace the protocol_unbind_all() by mworker_cleanlisteners() +
deinit_pollers()
diff --git a/include/proto/listener.h b/include/proto/listener.h
index 2758a3c..a33aeed 100644
--- a/include/proto/listener.h
+++ b/include/proto/listener.h
@@ -83,11 +83,13 @@
* range <portl> to <porth>, and possibly attached to fd <fd> (or -1 for auto
* allocation). The address family is taken from ss->ss_family. The number of
* jobs and listeners is automatically increased by the number of listeners
- * created. It returns non-zero on success, zero on error with the error message
+ * created. If the <inherited> argument is set to 1, it specifies that the FD
+ * was obtained from a parent process.
+ * It returns non-zero on success, zero on error with the error message
* set in <err>.
*/
int create_listeners(struct bind_conf *bc, const struct sockaddr_storage *ss,
- int portl, int porth, int fd, char **err);
+ int portl, int porth, int fd, int inherited, char **err);
/* Delete a listener from its protocol's list of listeners. The listener's
* state is automatically updated from LI_ASSIGNED to LI_INIT. The protocol's
diff --git a/include/types/listener.h b/include/types/listener.h
index 545f88c..b92c35e 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -98,6 +98,7 @@
#define LI_O_V6ONLY 0x0400 /* bind to IPv6 only on Linux >= 2.4.21 */
#define LI_O_V4V6 0x0800 /* bind to IPv4/IPv6 on Linux >= 2.4.21 */
#define LI_O_ACC_CIP 0x1000 /* find the proxied address in the NetScaler Client IP header */
+#define LI_O_INHERITED 0x2000 /* inherited FD from the parent process (fd@) */
/* Note: if a listener uses LI_O_UNLIMITED, it is highly recommended that it adds its own
* maxconn setting to the global.maxsock value so that its resources are reserved.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index b42dd54..57f25fa 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -239,6 +239,7 @@
next = dupstr = strdup(str);
while (next && *next) {
+ int inherited = 0;
struct sockaddr_storage *ss2;
int fd = -1;
@@ -277,6 +278,7 @@
}
else if (ss2->ss_family == AF_UNSPEC) {
socklen_t addr_len;
+ inherited = 1;
/* We want to attach to an already bound fd whose number
* is in the addr part of ss2 when cast to sockaddr_in.
@@ -295,7 +297,7 @@
}
/* OK the address looks correct */
- if (!create_listeners(bind_conf, ss2, port, end, fd, err)) {
+ if (!create_listeners(bind_conf, ss2, port, end, fd, inherited, err)) {
memprintf(err, "%s for address '%s'.\n", *err, str);
goto fail;
}
diff --git a/src/haproxy.c b/src/haproxy.c
index 00cc25b..5b45dfb 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -541,6 +541,33 @@
}
/*
+ * Upon a reload, the master worker needs to close all listeners FDs but the mworker_pipe
+ * fd, and the FD provided by fd@
+ */
+static void mworker_cleanlisteners()
+{
+ struct listener *l, *l_next;
+ struct proxy *curproxy;
+
+ for (curproxy = proxy; curproxy; curproxy = curproxy->next) {
+
+ list_for_each_entry_safe(l, l_next, &curproxy->conf.listeners, by_fe) {
+ /* does not close if the FD is inherited with fd@
+ * from the parent process */
+ if (!(l->options & LI_O_INHERITED)) {
+ close(l->fd);
+ LIST_DEL(&l->by_fe);
+ LIST_DEL(&l->by_bind);
+ free(l->name);
+ free(l->counters);
+ free(l);
+ }
+ }
+ }
+}
+
+
+/*
* remove a pid forom the olpid array and decrease nb_oldpids
* return 1 pid was found otherwise return 0
*/
@@ -2694,7 +2721,8 @@
if (proc == global.nbproc) {
if (global.mode & MODE_MWORKER) {
- protocol_unbind_all();
+ mworker_cleanlisteners();
+ deinit_pollers();
mworker_wait();
/* should never get there */
exit(EXIT_FAILURE);
diff --git a/src/listener.c b/src/listener.c
index c7db79a..7cb4d6a 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -372,11 +372,13 @@
* range <portl> to <porth>, and possibly attached to fd <fd> (or -1 for auto
* allocation). The address family is taken from ss->ss_family. The number of
* jobs and listeners is automatically increased by the number of listeners
- * created. It returns non-zero on success, zero on error with the error message
+ * created. If the <inherited> argument is set to 1, it specifies that the FD
+ * was obtained from a parent process.
+ * It returns non-zero on success, zero on error with the error message
* set in <err>.
*/
int create_listeners(struct bind_conf *bc, const struct sockaddr_storage *ss,
- int portl, int porth, int fd, char **err)
+ int portl, int porth, int fd, int inherited, char **err)
{
struct protocol *proto = protocol_by_family(ss->ss_family);
struct listener *l;
@@ -404,6 +406,9 @@
proto->add(l, port);
+ if (inherited)
+ l->options |= LI_O_INHERITED;
+
HA_SPIN_INIT(&l->lock);
HA_ATOMIC_ADD(&jobs, 1);
HA_ATOMIC_ADD(&listeners, 1);