REORG: sock: move get_old_sockets() from haproxy.c
The new function was called sock_get_old_sockets() and was left as-is
except a minimum amount of style lifting to make it more readable. It
will never be awesome anyway since it's used very early in the boot
sequence and needs to perform socket I/O without any external help.
diff --git a/src/haproxy.c b/src/haproxy.c
index 7eb5c0e..8f41cf5 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -39,7 +39,6 @@
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <net/if.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
@@ -1124,244 +1123,6 @@
free(err);
}
-/* Retrieves old sockets from worker process running the CLI at address
- * <unixsocket>. Fills xfer_sock_list with what is found. Returns 0 on
- * success, -1 on failure.
- */
-static int get_old_sockets(const char *unixsocket)
-{
- char *cmsgbuf = NULL, *tmpbuf = NULL;
- int *tmpfd = NULL;
- struct sockaddr_un addr;
- struct cmsghdr *cmsg;
- struct msghdr msghdr;
- struct iovec iov;
- struct xfer_sock_list *xfer_sock = NULL;
- struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
- int sock = -1;
- int ret = -1;
- int ret2 = -1;
- int fd_nb;
- int got_fd = 0;
- int i = 0;
- size_t maxoff = 0, curoff = 0;
-
- memset(&msghdr, 0, sizeof(msghdr));
- cmsgbuf = malloc(CMSG_SPACE(sizeof(int)) * MAX_SEND_FD);
- if (!cmsgbuf) {
- ha_warning("Failed to allocate memory to send sockets\n");
- goto out;
- }
- sock = socket(PF_UNIX, SOCK_STREAM, 0);
- if (sock < 0) {
- ha_warning("Failed to connect to the old process socket '%s'\n",
- unixsocket);
- goto out;
- }
- strncpy(addr.sun_path, unixsocket, sizeof(addr.sun_path) - 1);
- addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
- addr.sun_family = PF_UNIX;
- ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
- if (ret < 0) {
- ha_warning("Failed to connect to the old process socket '%s'\n",
- unixsocket);
- goto out;
- }
- setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
- iov.iov_base = &fd_nb;
- iov.iov_len = sizeof(fd_nb);
- msghdr.msg_iov = &iov;
- msghdr.msg_iovlen = 1;
- send(sock, "_getsocks\n", strlen("_getsocks\n"), 0);
- /* First, get the number of file descriptors to be received */
- if (recvmsg(sock, &msghdr, MSG_WAITALL) != sizeof(fd_nb)) {
- ha_warning("Failed to get the number of sockets to be transferred !\n");
- goto out;
- }
- if (fd_nb == 0) {
- ret2 = 0;
- goto out;
- }
- tmpbuf = malloc(fd_nb * (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int)));
- if (tmpbuf == NULL) {
- ha_warning("Failed to allocate memory while receiving sockets\n");
- goto out;
- }
- tmpfd = malloc(fd_nb * sizeof(int));
- if (tmpfd == NULL) {
- ha_warning("Failed to allocate memory while receiving sockets\n");
- goto out;
- }
- msghdr.msg_control = cmsgbuf;
- msghdr.msg_controllen = CMSG_SPACE(sizeof(int)) * MAX_SEND_FD;
- iov.iov_len = MAX_SEND_FD * (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int));
- do {
- int ret3;
-
- iov.iov_base = tmpbuf + curoff;
- ret = recvmsg(sock, &msghdr, 0);
- if (ret == -1 && errno == EINTR)
- continue;
- if (ret <= 0)
- break;
- /* Send an ack to let the sender know we got the sockets
- * and it can send some more
- */
- do {
- ret3 = send(sock, &got_fd, sizeof(got_fd), 0);
- } while (ret3 == -1 && errno == EINTR);
- for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg != NULL;
- cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_RIGHTS) {
- size_t totlen = cmsg->cmsg_len -
- CMSG_LEN(0);
- if (totlen / sizeof(int) + got_fd > fd_nb) {
- ha_warning("Got to many sockets !\n");
- goto out;
- }
- /*
- * Be paranoid and use memcpy() to avoid any
- * potential alignment issue.
- */
- memcpy(&tmpfd[got_fd], CMSG_DATA(cmsg), totlen);
- got_fd += totlen / sizeof(int);
- }
- }
- curoff += ret;
- } while (got_fd < fd_nb);
-
- if (got_fd != fd_nb) {
- ha_warning("We didn't get the expected number of sockets (expecting %d got %d)\n",
- fd_nb, got_fd);
- goto out;
- }
- maxoff = curoff;
- curoff = 0;
- for (i = 0; i < got_fd; i++) {
- int fd = tmpfd[i];
- socklen_t socklen;
- int len;
-
- xfer_sock = calloc(1, sizeof(*xfer_sock));
- if (!xfer_sock) {
- ha_warning("Failed to allocate memory in get_old_sockets() !\n");
- break;
- }
- xfer_sock->fd = -1;
-
- socklen = sizeof(xfer_sock->addr);
- if (getsockname(fd, (struct sockaddr *)&xfer_sock->addr, &socklen) != 0) {
- ha_warning("Failed to get socket address\n");
- free(xfer_sock);
- xfer_sock = NULL;
- continue;
- }
- if (curoff >= maxoff) {
- ha_warning("Inconsistency while transferring sockets\n");
- goto out;
- }
- len = tmpbuf[curoff++];
- if (len > 0) {
- /* We have a namespace */
- if (curoff + len > maxoff) {
- ha_warning("Inconsistency while transferring sockets\n");
- goto out;
- }
- xfer_sock->namespace = malloc(len + 1);
- if (!xfer_sock->namespace) {
- ha_warning("Failed to allocate memory while transferring sockets\n");
- goto out;
- }
- memcpy(xfer_sock->namespace, &tmpbuf[curoff], len);
- xfer_sock->namespace[len] = 0;
- xfer_sock->ns_namelen = len;
- curoff += len;
- }
- if (curoff >= maxoff) {
- ha_warning("Inconsistency while transferring sockets\n");
- goto out;
- }
- len = tmpbuf[curoff++];
- if (len > 0) {
- /* We have an interface */
- if (curoff + len > maxoff) {
- ha_warning("Inconsistency while transferring sockets\n");
- goto out;
- }
- xfer_sock->iface = malloc(len + 1);
- if (!xfer_sock->iface) {
- ha_warning("Failed to allocate memory while transferring sockets\n");
- goto out;
- }
- memcpy(xfer_sock->iface, &tmpbuf[curoff], len);
- xfer_sock->iface[len] = 0;
- xfer_sock->if_namelen = len;
- curoff += len;
- }
- if (curoff + sizeof(int) > maxoff) {
- ha_warning("Inconsistency while transferring sockets\n");
- goto out;
- }
-
- /* we used to have 32 bits of listener options here but we don't
- * use them anymore.
- */
- curoff += sizeof(int);
-
- /* determine the foreign status directly from the socket itself */
- if (sock_inet_is_foreign(fd, xfer_sock->addr.ss_family))
- xfer_sock->options |= LI_O_FOREIGN;
-
- /* keep only the v6only flag depending on what's currently
- * active on the socket, and always drop the v4v6 one.
- */
-#if defined(IPV6_V6ONLY)
- {
- int val = 0;
- socklen_t len = sizeof(val);
-
- if (xfer_sock->addr.ss_family == AF_INET6 &&
- getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &len) == 0 &&
- val > 0)
- xfer_sock->options |= LI_O_V6ONLY;
- }
-#endif
-
- xfer_sock->fd = fd;
- if (xfer_sock_list)
- xfer_sock_list->prev = xfer_sock;
- xfer_sock->next = xfer_sock_list;
- xfer_sock->prev = NULL;
- xfer_sock_list = xfer_sock;
- xfer_sock = NULL;
- }
-
- ret2 = 0;
-out:
- /* If we failed midway make sure to close the remaining
- * file descriptors
- */
- if (tmpfd != NULL && i < got_fd) {
- for (; i < got_fd; i++) {
- close(tmpfd[i]);
- }
- }
- free(tmpbuf);
- free(tmpfd);
- free(cmsgbuf);
- if (sock != -1)
- close(sock);
- if (xfer_sock) {
- free(xfer_sock->namespace);
- free(xfer_sock->iface);
- if (xfer_sock->fd != -1)
- close(xfer_sock->fd);
- free(xfer_sock);
- }
- return (ret2);
-}
-
/*
* copy and cleanup the current argv
* Remove the -sf /-st / -x parameters
@@ -3281,7 +3042,7 @@
if (old_unixsocket) {
if (strcmp("/dev/null", old_unixsocket) != 0) {
- if (get_old_sockets(old_unixsocket) != 0) {
+ if (sock_get_old_sockets(old_unixsocket) != 0) {
ha_alert("Failed to get the sockets from the old process!\n");
if (!(global.mode & MODE_MWORKER))
exit(1);
diff --git a/src/sock.c b/src/sock.c
index 5bb6e13..b8a729b 100644
--- a/src/sock.c
+++ b/src/sock.c
@@ -22,6 +22,8 @@
#include <sys/socket.h>
#include <sys/types.h>
+#include <net/if.h>
+
#include <haproxy/api.h>
#include <haproxy/connection.h>
#include <haproxy/listener-t.h>
@@ -81,6 +83,266 @@
return getsockname(fd, sa, &salen);
}
+/* Try to retrieve exported sockets from worker at CLI <unixsocket>. These
+ * ones will be placed into the xfer_sock_list for later use by function
+ * sock_find_compatible_fd(). Returns 0 on success, -1 on failure.
+ */
+int sock_get_old_sockets(const char *unixsocket)
+{
+ char *cmsgbuf = NULL, *tmpbuf = NULL;
+ int *tmpfd = NULL;
+ struct sockaddr_un addr;
+ struct cmsghdr *cmsg;
+ struct msghdr msghdr;
+ struct iovec iov;
+ struct xfer_sock_list *xfer_sock = NULL;
+ struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
+ int sock = -1;
+ int ret = -1;
+ int ret2 = -1;
+ int fd_nb;
+ int got_fd = 0;
+ int cur_fd = 0;
+ size_t maxoff = 0, curoff = 0;
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ cmsgbuf = malloc(CMSG_SPACE(sizeof(int)) * MAX_SEND_FD);
+ if (!cmsgbuf) {
+ ha_warning("Failed to allocate memory to send sockets\n");
+ goto out;
+ }
+
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ ha_warning("Failed to connect to the old process socket '%s'\n", unixsocket);
+ goto out;
+ }
+
+ strncpy(addr.sun_path, unixsocket, sizeof(addr.sun_path) - 1);
+ addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
+ addr.sun_family = PF_UNIX;
+
+ ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) {
+ ha_warning("Failed to connect to the old process socket '%s'\n", unixsocket);
+ goto out;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+ iov.iov_base = &fd_nb;
+ iov.iov_len = sizeof(fd_nb);
+ msghdr.msg_iov = &iov;
+ msghdr.msg_iovlen = 1;
+
+ if (send(sock, "_getsocks\n", strlen("_getsocks\n"), 0) != strlen("_getsocks\n")) {
+ ha_warning("Failed to get the number of sockets to be transferred !\n");
+ goto out;
+ }
+
+ /* First, get the number of file descriptors to be received */
+ if (recvmsg(sock, &msghdr, MSG_WAITALL) != sizeof(fd_nb)) {
+ ha_warning("Failed to get the number of sockets to be transferred !\n");
+ goto out;
+ }
+
+ if (fd_nb == 0) {
+ ret2 = 0;
+ goto out;
+ }
+
+ tmpbuf = malloc(fd_nb * (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int)));
+ if (tmpbuf == NULL) {
+ ha_warning("Failed to allocate memory while receiving sockets\n");
+ goto out;
+ }
+
+ tmpfd = malloc(fd_nb * sizeof(int));
+ if (tmpfd == NULL) {
+ ha_warning("Failed to allocate memory while receiving sockets\n");
+ goto out;
+ }
+
+ msghdr.msg_control = cmsgbuf;
+ msghdr.msg_controllen = CMSG_SPACE(sizeof(int)) * MAX_SEND_FD;
+ iov.iov_len = MAX_SEND_FD * (1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof(int));
+
+ do {
+ int ret3;
+
+ iov.iov_base = tmpbuf + curoff;
+
+ ret = recvmsg(sock, &msghdr, 0);
+
+ if (ret == -1 && errno == EINTR)
+ continue;
+
+ if (ret <= 0)
+ break;
+
+ /* Send an ack to let the sender know we got the sockets
+ * and it can send some more
+ */
+ do {
+ ret3 = send(sock, &got_fd, sizeof(got_fd), 0);
+ } while (ret3 == -1 && errno == EINTR);
+
+ for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg != NULL; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ size_t totlen = cmsg->cmsg_len - CMSG_LEN(0);
+
+ if (totlen / sizeof(int) + got_fd > fd_nb) {
+ ha_warning("Got to many sockets !\n");
+ goto out;
+ }
+
+ /*
+ * Be paranoid and use memcpy() to avoid any
+ * potential alignment issue.
+ */
+ memcpy(&tmpfd[got_fd], CMSG_DATA(cmsg), totlen);
+ got_fd += totlen / sizeof(int);
+ }
+ }
+ curoff += ret;
+ } while (got_fd < fd_nb);
+
+ if (got_fd != fd_nb) {
+ ha_warning("We didn't get the expected number of sockets (expecting %d got %d)\n",
+ fd_nb, got_fd);
+ goto out;
+ }
+
+ maxoff = curoff;
+ curoff = 0;
+
+ for (cur_fd = 0; cur_fd < got_fd; cur_fd++) {
+ int fd = tmpfd[cur_fd];
+ socklen_t socklen;
+ int val;
+ int len;
+
+ xfer_sock = calloc(1, sizeof(*xfer_sock));
+ if (!xfer_sock) {
+ ha_warning("Failed to allocate memory in get_old_sockets() !\n");
+ break;
+ }
+ xfer_sock->fd = -1;
+
+ socklen = sizeof(xfer_sock->addr);
+ if (getsockname(fd, (struct sockaddr *)&xfer_sock->addr, &socklen) != 0) {
+ ha_warning("Failed to get socket address\n");
+ free(xfer_sock);
+ xfer_sock = NULL;
+ continue;
+ }
+
+ if (curoff >= maxoff) {
+ ha_warning("Inconsistency while transferring sockets\n");
+ goto out;
+ }
+
+ len = tmpbuf[curoff++];
+ if (len > 0) {
+ /* We have a namespace */
+ if (curoff + len > maxoff) {
+ ha_warning("Inconsistency while transferring sockets\n");
+ goto out;
+ }
+ xfer_sock->namespace = malloc(len + 1);
+ if (!xfer_sock->namespace) {
+ ha_warning("Failed to allocate memory while transferring sockets\n");
+ goto out;
+ }
+ memcpy(xfer_sock->namespace, &tmpbuf[curoff], len);
+ xfer_sock->namespace[len] = 0;
+ xfer_sock->ns_namelen = len;
+ curoff += len;
+ }
+
+ if (curoff >= maxoff) {
+ ha_warning("Inconsistency while transferring sockets\n");
+ goto out;
+ }
+
+ len = tmpbuf[curoff++];
+ if (len > 0) {
+ /* We have an interface */
+ if (curoff + len > maxoff) {
+ ha_warning("Inconsistency while transferring sockets\n");
+ goto out;
+ }
+ xfer_sock->iface = malloc(len + 1);
+ if (!xfer_sock->iface) {
+ ha_warning("Failed to allocate memory while transferring sockets\n");
+ goto out;
+ }
+ memcpy(xfer_sock->iface, &tmpbuf[curoff], len);
+ xfer_sock->iface[len] = 0;
+ xfer_sock->if_namelen = len;
+ curoff += len;
+ }
+
+ if (curoff + sizeof(int) > maxoff) {
+ ha_warning("Inconsistency while transferring sockets\n");
+ goto out;
+ }
+
+ /* we used to have 32 bits of listener options here but we don't
+ * use them anymore.
+ */
+ curoff += sizeof(int);
+
+ /* determine the foreign status directly from the socket itself */
+ if (sock_inet_is_foreign(fd, xfer_sock->addr.ss_family))
+ xfer_sock->options |= LI_O_FOREIGN;
+
+#if defined(IPV6_V6ONLY)
+ /* keep only the v6only flag depending on what's currently
+ * active on the socket, and always drop the v4v6 one.
+ */
+ socklen = sizeof(val);
+ if (xfer_sock->addr.ss_family == AF_INET6 &&
+ getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &socklen) == 0 && val > 0)
+ xfer_sock->options |= LI_O_V6ONLY;
+#endif
+
+ xfer_sock->fd = fd;
+ if (xfer_sock_list)
+ xfer_sock_list->prev = xfer_sock;
+ xfer_sock->next = xfer_sock_list;
+ xfer_sock->prev = NULL;
+ xfer_sock_list = xfer_sock;
+ xfer_sock = NULL;
+ }
+
+ ret2 = 0;
+out:
+ /* If we failed midway make sure to close the remaining
+ * file descriptors
+ */
+ if (tmpfd != NULL && cur_fd < got_fd) {
+ for (; cur_fd < got_fd; cur_fd++) {
+ close(tmpfd[cur_fd]);
+ }
+ }
+
+ free(tmpbuf);
+ free(tmpfd);
+ free(cmsgbuf);
+
+ if (sock != -1)
+ close(sock);
+
+ if (xfer_sock) {
+ free(xfer_sock->namespace);
+ free(xfer_sock->iface);
+ if (xfer_sock->fd != -1)
+ close(xfer_sock->fd);
+ free(xfer_sock);
+ }
+ return (ret2);
+}
+
/* When binding the listeners, check if a socket has been sent to us by the
* previous process that we could reuse, instead of creating a new one. Note
* that some address family-specific options are checked on the listener and