|
From 58dd0f33fa908e83199d28775bad8ee2f24151a9 Mon Sep 17 00:00:00 2001
|
|
From: Willy Tarreau <w@1wt.eu>
|
|
Date: Mon, 7 Jul 2014 18:36:45 +0200
|
|
Subject: [PATCH 15/21] BUG/MEDIUM: unix: failed abstract socket binding is
|
|
retryable
|
|
|
|
Jan Seda noticed that abstract sockets are incompatible with soft reload,
|
|
because the new process cannot bind and immediately fails. This patch marks
|
|
the binding as retryable and not fatal so that the new process can try to
|
|
bind again after sending a signal to the old process.
|
|
|
|
Note that this fix is not enough to completely solve the problem, but it
|
|
is necessary. This patch should be backported to 1.5.
|
|
(cherry picked from commit 3c5efa2b3268f31cffc2c18887010d4bc906a066)
|
|
---
|
|
src/proto_uxst.c | 30 ++++++++++++++++++++++++++----
|
|
1 file changed, 26 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
|
|
index c9a52ff..409c659 100644
|
|
--- a/src/proto_uxst.c
|
|
+++ b/src/proto_uxst.c
|
|
@@ -166,9 +166,11 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
|
|
const char *path;
|
|
int ext, ready;
|
|
socklen_t ready_len;
|
|
-
|
|
+ int err;
|
|
int ret;
|
|
|
|
+ err = ERR_NONE;
|
|
+
|
|
/* ensure we never return garbage */
|
|
if (errlen)
|
|
*errmsg = 0;
|
|
@@ -191,29 +193,34 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
|
|
if (path[0]) {
|
|
ret = snprintf(tempname, MAXPATHLEN, "%s.%d.tmp", path, pid);
|
|
if (ret < 0 || ret >= MAXPATHLEN) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "name too long for UNIX socket";
|
|
goto err_return;
|
|
}
|
|
|
|
ret = snprintf(backname, MAXPATHLEN, "%s.%d.bak", path, pid);
|
|
if (ret < 0 || ret >= MAXPATHLEN) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "name too long for UNIX socket";
|
|
goto err_return;
|
|
}
|
|
|
|
/* 2. clean existing orphaned entries */
|
|
if (unlink(tempname) < 0 && errno != ENOENT) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "error when trying to unlink previous UNIX socket";
|
|
goto err_return;
|
|
}
|
|
|
|
if (unlink(backname) < 0 && errno != ENOENT) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "error when trying to unlink previous UNIX socket";
|
|
goto err_return;
|
|
}
|
|
|
|
/* 3. backup existing socket */
|
|
if (link(path, backname) < 0 && errno != ENOENT) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "error when trying to preserve previous UNIX socket";
|
|
goto err_return;
|
|
}
|
|
@@ -231,24 +238,35 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
|
|
|
|
fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
if (fd < 0) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "cannot create UNIX socket";
|
|
goto err_unlink_back;
|
|
}
|
|
|
|
fd_ready:
|
|
if (fd >= global.maxsock) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "socket(): not enough free sockets, raise -n argument";
|
|
goto err_unlink_temp;
|
|
}
|
|
|
|
if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "cannot make UNIX socket non-blocking";
|
|
goto err_unlink_temp;
|
|
}
|
|
|
|
if (!ext && bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
/* note that bind() creates the socket <tempname> on the file system */
|
|
- msg = "cannot bind UNIX socket";
|
|
+ if (errno == EADDRINUSE) {
|
|
+ /* the old process might still own it, let's retry */
|
|
+ err |= ERR_RETRYABLE | ERR_ALERT;
|
|
+ msg = "cannot listen to socket";
|
|
+ }
|
|
+ else {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
+ msg = "cannot bind UNIX socket";
|
|
+ }
|
|
goto err_unlink_temp;
|
|
}
|
|
|
|
@@ -261,6 +279,7 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
|
|
(((listener->bind_conf->ux.uid != -1 || listener->bind_conf->ux.gid != -1) &&
|
|
(chown(tempname, listener->bind_conf->ux.uid, listener->bind_conf->ux.gid) == -1)) ||
|
|
(listener->bind_conf->ux.mode != 0 && chmod(tempname, listener->bind_conf->ux.mode) == -1))) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "cannot change UNIX socket ownership";
|
|
goto err_unlink_temp;
|
|
}
|
|
@@ -272,6 +291,7 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
|
|
|
|
if (!(ext && ready) && /* only listen if not already done by external process */
|
|
listen(fd, listener->backlog ? listener->backlog : listener->maxconn) < 0) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "cannot listen to UNIX socket";
|
|
goto err_unlink_temp;
|
|
}
|
|
@@ -281,6 +301,7 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
|
|
* backname. Abstract sockets are not renamed.
|
|
*/
|
|
if (!ext && path[0] && rename(tempname, path) < 0) {
|
|
+ err |= ERR_FATAL | ERR_ALERT;
|
|
msg = "cannot switch final and temporary UNIX sockets";
|
|
goto err_rename;
|
|
}
|
|
@@ -303,7 +324,8 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
|
|
fd_insert(fd);
|
|
fdtab[fd].iocb = listener->proto->accept;
|
|
fdtab[fd].owner = listener; /* reference the listener instead of a task */
|
|
- return ERR_NONE;
|
|
+ return err;
|
|
+
|
|
err_rename:
|
|
ret = rename(backname, path);
|
|
if (ret < 0 && errno == ENOENT)
|
|
@@ -322,7 +344,7 @@ static int uxst_bind_listener(struct listener *listener, char *errmsg, int errle
|
|
else
|
|
snprintf(errmsg, errlen, "%s [fd %d]", msg, fd);
|
|
}
|
|
- return ERR_FATAL | ERR_ALERT;
|
|
+ return err;
|
|
}
|
|
|
|
/* This function closes the UNIX sockets for the specified listener.
|
|
--
|
|
1.8.5.5
|
|
|