From ff3e6488123b7750eda3e9fd8cd8947f2eb155a2 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 12 Mar 2015 23:56:52 +0100 Subject: [PATCH] MINOR: connection: implement conn_sock_send() This function is an equivalent to send() which operates over a connection instead of a file descriptor. It checks that the control layer is ready and that it's allowed to send. If automatically enables polling if it cannot send. It simplifies the return checks by returning zero in all cases where it cannot send so that the caller only has to care about negative values indicating errors. --- include/proto/connection.h | 3 +++ src/connection.c | 50 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/include/proto/connection.h b/include/proto/connection.h index 5f80cc5ba..27922f3db 100644 --- a/include/proto/connection.h +++ b/include/proto/connection.h @@ -45,6 +45,9 @@ int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connectio int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst); int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote); +/* raw send() directly on the socket */ +int conn_sock_send(struct connection *conn, const void *buf, int len, int flags); + /* returns true is the transport layer is ready */ static inline int conn_xprt_ready(const struct connection *conn) { diff --git a/src/connection.c b/src/connection.c index c21c98bae..8b3c835e7 100644 --- a/src/connection.c +++ b/src/connection.c @@ -218,6 +218,56 @@ void conn_update_sock_polling(struct connection *c) c->flags = f; } +/* Send a message over an established connection. It makes use of send() and + * returns the same return code and errno. If the socket layer is not ready yet + * then -1 is returned and ENOTSOCK is set into errno. If the fd is not marked + * as ready, or if EAGAIN or ENOTCONN is returned, then we return 0. It returns + * EMSGSIZE if called with a zero length message. The purpose is to simplify + * some rare attempts to directly write on the socket from above the connection + * (typically send_proxy). In case of EAGAIN, the fd is marked as "cant_send". + * It automatically retries on EINTR. Other errors cause the connection to be + * marked as in error state. It takes similar arguments as send() except the + * first one which is the connection instead of the file descriptor. Note, + * MSG_DONTWAIT and MSG_NOSIGNAL are forced on the flags. + */ +int conn_sock_send(struct connection *conn, const void *buf, int len, int flags) +{ + int ret; + + ret = -1; + errno = ENOTSOCK; + + if (conn->flags & CO_FL_SOCK_WR_SH) + goto fail; + + if (!conn_ctrl_ready(conn)) + goto fail; + + errno = EMSGSIZE; + if (!len) + goto fail; + + if (!fd_send_ready(conn->t.sock.fd)) + goto wait; + + do { + ret = send(conn->t.sock.fd, buf, len, flags | MSG_DONTWAIT | MSG_NOSIGNAL); + } while (ret < 0 && errno == EINTR); + + + if (ret > 0) + return ret; + + if (ret == 0 || errno == EAGAIN || errno == ENOTCONN) { + wait: + fd_cant_send(conn->t.sock.fd); + return 0; + } + fail: + conn->flags |= CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH | CO_FL_ERROR; + return ret; +} + /* * Get data length from tlv */