From e215bba956e8c7c5fdd98cc15d5b645530a24825 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 24 Aug 2018 14:31:53 +0200 Subject: [PATCH] MINOR: connection: make conn_sock_drain() work for all socket families This patch improves the previous fix by implementing the socket draining code directly in conn_sock_drain() so that it always applies regardless of the protocol's family. Thus it gets rid of tcp_drain(). --- include/proto/proto_tcp.h | 1 - src/connection.c | 54 +++++++++++++++++++++++++++++++-------- src/proto_tcp.c | 45 -------------------------------- src/proto_uxst.c | 2 -- 4 files changed, 43 insertions(+), 59 deletions(-) diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h index 5241e7cafb..d4e09c7a65 100644 --- a/include/proto/proto_tcp.h +++ b/include/proto/proto_tcp.h @@ -33,7 +33,6 @@ int tcp_connect_server(struct connection *conn, int data, int delack); int tcp_connect_probe(struct connection *conn); int tcp_get_src(int fd, struct sockaddr *sa, socklen_t salen, int dir); int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir); -int tcp_drain(int fd); /* Export some samples. */ int smp_fetch_src(const struct arg *args, struct sample *smp, const char *kw, void *private); diff --git a/src/connection.c b/src/connection.c index b3ef56eba6..ab32567bd5 100644 --- a/src/connection.c +++ b/src/connection.c @@ -359,29 +359,61 @@ int conn_subscribe(struct connection *conn, int event_type, void *param) */ int conn_sock_drain(struct connection *conn) { + int turns = 2; + int len; + if (!conn_ctrl_ready(conn)) return 1; if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH)) return 1; - if (fdtab[conn->handle.fd].ev & (FD_POLL_ERR|FD_POLL_HUP)) { - fdtab[conn->handle.fd].linger_risk = 0; - } - else { - if (!fd_recv_ready(conn->handle.fd)) - return 0; + if (fdtab[conn->handle.fd].ev & (FD_POLL_ERR|FD_POLL_HUP)) + goto shut; - /* disable draining if we were called and have no drain function */ - if (!conn->ctrl->drain) { - __conn_xprt_stop_recv(conn); - return 0; - } + if (!fd_recv_ready(conn->handle.fd)) + return 0; + if (conn->ctrl->drain) { if (conn->ctrl->drain(conn->handle.fd) <= 0) return 0; + goto shut; + } + + /* no drain function defined, use the generic one */ + + while (turns) { +#ifdef MSG_TRUNC_CLEARS_INPUT + len = recv(conn->handle.fd, NULL, INT_MAX, MSG_DONTWAIT | MSG_NOSIGNAL | MSG_TRUNC); + if (len == -1 && errno == EFAULT) +#endif + len = recv(conn->handle.fd, trash.area, trash.size, + MSG_DONTWAIT | MSG_NOSIGNAL); + + if (len == 0) + goto shut; + + if (len < 0) { + if (errno == EAGAIN) { + /* connection not closed yet */ + fd_cant_recv(conn->handle.fd); + break; + } + if (errno == EINTR) /* oops, try again */ + continue; + /* other errors indicate a dead connection, fine. */ + goto shut; + } + /* OK we read some data, let's try again once */ + turns--; } + /* some data are still present, give up */ + return 0; + + shut: + /* we're certain the connection was shut down */ + fdtab[conn->handle.fd].linger_risk = 0; conn->flags |= CO_FL_SOCK_RD_SH; return 1; } diff --git a/src/proto_tcp.c b/src/proto_tcp.c index d49ecf4c7b..c000602b64 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -83,7 +83,6 @@ static struct protocol proto_tcpv4 = { .enable_all = enable_all_listeners, .get_src = tcp_get_src, .get_dst = tcp_get_dst, - .drain = tcp_drain, .pause = tcp_pause_listener, .add = tcpv4_add_listener, .listeners = LIST_HEAD_INIT(proto_tcpv4.listeners), @@ -107,7 +106,6 @@ static struct protocol proto_tcpv6 = { .enable_all = enable_all_listeners, .get_src = tcp_get_src, .get_dst = tcp_get_dst, - .drain = tcp_drain, .pause = tcp_pause_listener, .add = tcpv6_add_listener, .listeners = LIST_HEAD_INIT(proto_tcpv6.listeners), @@ -616,49 +614,6 @@ int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir) } } -/* Tries to drain any pending incoming data from the socket to reach the - * receive shutdown. Returns positive if the shutdown was found, negative - * if EAGAIN was hit, otherwise zero. This is useful to decide whether we - * can close a connection cleanly are we must kill it hard. - */ -int tcp_drain(int fd) -{ - int turns = 2; - int len; - - while (turns) { -#ifdef MSG_TRUNC_CLEARS_INPUT - len = recv(fd, NULL, INT_MAX, MSG_DONTWAIT | MSG_NOSIGNAL | MSG_TRUNC); - if (len == -1 && errno == EFAULT) -#endif - len = recv(fd, trash.area, trash.size, - MSG_DONTWAIT | MSG_NOSIGNAL); - - if (len == 0) { - /* cool, shutdown received */ - fdtab[fd].linger_risk = 0; - return 1; - } - - if (len < 0) { - if (errno == EAGAIN) { - /* connection not closed yet */ - fd_cant_recv(fd); - return -1; - } - if (errno == EINTR) /* oops, try again */ - continue; - /* other errors indicate a dead connection, fine. */ - fdtab[fd].linger_risk = 0; - return 1; - } - /* OK we read some data, let's try again once */ - turns--; - } - /* some data are still present, give up */ - return 0; -} - /* This is the callback which is set when a connection establishment is pending * and we have nothing to send. It updates the FD polling status. It returns 0 * if it fails in a fatal way or needs to poll to go further, otherwise it diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 9c04d60836..ab788bde72 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -42,7 +42,6 @@ #include #include #include -#include #include static int uxst_bind_listener(struct listener *listener, char *errmsg, int errlen); @@ -72,7 +71,6 @@ static struct protocol proto_unix = { .disable_all = disable_all_listeners, .get_src = uxst_get_src, .get_dst = uxst_get_dst, - .drain = tcp_drain, .pause = uxst_pause_listener, .add = uxst_add_listener, .listeners = LIST_HEAD_INIT(proto_unix.listeners), -- 2.39.5