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()
This commit is contained in:
William Lallemand 2017-11-15 19:02:58 +01:00 committed by Willy Tarreau
parent fade49d8fb
commit 75ea0a06b0
5 changed files with 44 additions and 6 deletions

View File

@ -83,11 +83,13 @@ int unbind_all_listeners(struct protocol *proto);
* 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

View File

@ -98,6 +98,7 @@ enum li_state {
#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.

View File

@ -239,6 +239,7 @@ int str2listener(char *str, struct proxy *curproxy, struct bind_conf *bind_conf,
next = dupstr = strdup(str);
while (next && *next) {
int inherited = 0;
struct sockaddr_storage *ss2;
int fd = -1;
@ -277,6 +278,7 @@ int str2listener(char *str, struct proxy *curproxy, struct bind_conf *bind_conf,
}
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 @@ int str2listener(char *str, struct proxy *curproxy, struct bind_conf *bind_conf,
}
/* 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;
}

View File

@ -540,6 +540,33 @@ static void mworker_kill(int sig)
}
}
/*
* 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 @@ int main(int argc, char **argv)
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);

View File

@ -372,11 +372,13 @@ int unbind_all_listeners(struct protocol *proto)
* 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 @@ int create_listeners(struct bind_conf *bc, const struct sockaddr_storage *ss,
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);