MINOR: cli: Add a command to send listening sockets.

Add a new command that will send all the listening sockets, via the
stats socket, and their properties.
This is a first step to workaround the linux problem when reloading
haproxy.
diff --git a/src/cli.c b/src/cli.c
index fa45db9..54fb438 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -24,6 +24,8 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <net/if.h>
+
 #include <common/cfgparse.h>
 #include <common/compat.h>
 #include <common/config.h>
@@ -1013,6 +1015,184 @@
 	return 0;
 }
 
+/* Send all the bound sockets, always returns 1 */
+static int _getsocks(char **args, struct appctx *appctx, void *private)
+{
+	char *cmsgbuf = NULL;
+	unsigned char *tmpbuf = NULL;
+	struct cmsghdr *cmsg;
+	struct stream_interface *si = appctx->owner;
+	struct connection *remote = objt_conn(si_opposite(si)->end);
+	struct msghdr msghdr;
+	struct iovec iov;
+	int *tmpfd;
+	int tot_fd_nb = 0;
+	struct proxy *px;
+	int i = 0;
+	int fd = remote->t.sock.fd;
+	int curoff = 0;
+	int old_fcntl;
+	int ret;
+
+	/* Temporary set the FD in blocking mode, that will make our life easier */
+	old_fcntl = fcntl(fd, F_GETFL);
+	if (old_fcntl < 0) {
+		Warning("Couldn't get the flags for the unix socket\n");
+		goto out;
+	}
+	cmsgbuf = malloc(CMSG_SPACE(sizeof(int) * MAX_SEND_FD));
+	if (!cmsgbuf) {
+		Warning("Failed to allocate memory to send sockets\n");
+		goto out;
+	}
+	if (fcntl(fd, F_SETFL, old_fcntl &~ O_NONBLOCK) == -1) {
+		Warning("Cannot make the unix socket blocking\n");
+		goto out;
+	}
+	iov.iov_base = &tot_fd_nb;
+	iov.iov_len = sizeof(tot_fd_nb);
+	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+		goto out;
+	memset(&msghdr, 0, sizeof(msghdr));
+	/*
+	 * First, calculates the total number of FD, so that we can let
+	 * the caller know how much he should expects.
+	 */
+	px = proxy;
+	while (px) {
+		struct listener *l;
+
+		list_for_each_entry(l, &px->conf.listeners, by_fe) {
+			/* Only transfer IPv4/IPv6 sockets */
+			if (l->proto->sock_family == AF_INET ||
+			    l->proto->sock_family == AF_INET6 ||
+			    l->proto->sock_family == AF_UNIX)
+				tot_fd_nb++;
+		}
+		px = px->next;
+	}
+	if (tot_fd_nb == 0)
+		goto out;
+
+	/* First send the total number of file descriptors, so that the
+	 * receiving end knows what to expect.
+	 */
+	msghdr.msg_iov = &iov;
+	msghdr.msg_iovlen = 1;
+	ret = sendmsg(fd, &msghdr, 0);
+	if (ret != sizeof(tot_fd_nb)) {
+		Warning("Failed to send the number of sockets to send\n");
+		goto out;
+	}
+
+	/* Now send the fds */
+	msghdr.msg_control = cmsgbuf;
+	msghdr.msg_controllen = CMSG_SPACE(sizeof(int) * MAX_SEND_FD);
+	cmsg = CMSG_FIRSTHDR(&msghdr);
+	cmsg->cmsg_len = CMSG_LEN(MAX_SEND_FD * sizeof(int));
+	cmsg->cmsg_level = SOL_SOCKET;
+	cmsg->cmsg_type = SCM_RIGHTS;
+	tmpfd = (int *)CMSG_DATA(cmsg);
+
+	px = proxy;
+	/* For each socket, e message is sent, containing the following :
+	 *  Size of the namespace name (or 0 if none), as an unsigned char.
+	 *  The namespace name, if any
+	 *  Size of the interface name (or 0 if none), as an unsigned char
+	 *  The interface name, if any
+	 *  Listener options, as an int.
+	 */
+	/* We will send sockets MAX_SEND_FD per MAX_SEND_FD, allocate a
+	 * buffer big enough to store the socket informations.
+	 */
+	tmpbuf = malloc(MAX_SEND_FD * (1 + NAME_MAX + 1 + IFNAMSIZ + sizeof(int)));
+	if (tmpbuf == NULL) {
+		Warning("Failed to allocate memory to transfer socket informations\n");
+		goto out;
+	}
+	iov.iov_base = tmpbuf;
+	while (px) {
+		struct listener *l;
+
+		list_for_each_entry(l, &px->conf.listeners, by_fe) {
+			int ret;
+			/* Only transfer IPv4/IPv6 sockets */
+			if (l->state >= LI_LISTEN &&
+			    (l->proto->sock_family == AF_INET ||
+			    l->proto->sock_family == AF_INET6 ||
+			    l->proto->sock_family == AF_UNIX)) {
+				memcpy(&tmpfd[i % MAX_SEND_FD], &l->fd, sizeof(l->fd));
+				if (!l->netns)
+					tmpbuf[curoff++] = 0;
+#ifdef CONFIG_HAP_NS
+				else {
+					char *name = l->netns->node.key;
+					unsigned char len = l->netns->name_len;
+					tmpbuf[curoff++] = len;
+					memcpy(tmpbuf + curoff, name, len);
+					curoff += len;
+				}
+#endif
+				if (l->interface) {
+					unsigned char len = strlen(l->interface);
+					tmpbuf[curoff++] = len;
+					memcpy(tmpbuf + curoff, l->interface, len);
+				curoff += len;
+				} else
+					tmpbuf[curoff++] = 0;
+				memcpy(tmpbuf + curoff, &l->options,
+				    sizeof(l->options));
+				curoff += sizeof(l->options);
+
+
+				i++;
+			} else
+				continue;
+			if ((!(i % MAX_SEND_FD))) {
+				iov.iov_len = curoff;
+				if (sendmsg(fd, &msghdr, 0) != curoff) {
+					Warning("Failed to transfer sockets\n");
+					printf("errno %d\n", errno);
+					goto out;
+				}
+				/* Wait for an ack */
+				do {
+					ret = recv(fd, &tot_fd_nb,
+					    sizeof(tot_fd_nb), 0);
+				} while (ret == -1 && errno == EINTR);
+				if (ret <= 0) {
+					Warning("Unexpected error while transferring sockets\n");
+					goto out;
+				}
+				curoff = 0;
+			}
+
+		}
+		px = px->next;
+	}
+	if (i % MAX_SEND_FD) {
+		iov.iov_len = curoff;
+		cmsg->cmsg_len = CMSG_LEN((i % MAX_SEND_FD) * sizeof(int));
+		msghdr.msg_controllen = CMSG_SPACE(sizeof(int) *  (i % MAX_SEND_FD));
+		if (sendmsg(fd, &msghdr, 0) != curoff) {
+			Warning("Failed to transfer sockets\n");
+			goto out;
+		}
+	}
+
+out:
+	if (old_fcntl >= 0 && fcntl(fd, F_SETFL, old_fcntl) == -1) {
+		Warning("Cannot make the unix socket non-blocking\n");
+		goto out;
+	}
+	appctx->st0 = CLI_ST_END;
+	free(cmsgbuf);
+	free(tmpbuf);
+	return 1;
+}
+
+
+
 static struct applet cli_applet = {
 	.obj_type = OBJ_TYPE_APPLET,
 	.name = "<CLI>", /* used for logging */
@@ -1027,6 +1207,7 @@
 	{ { "set", "timeout",  NULL }, "set timeout    : change a timeout setting", cli_parse_set_timeout, NULL, NULL },
 	{ { "show", "env",  NULL }, "show env [var] : dump environment variables known to the process", cli_parse_show_env, cli_io_handler_show_env, NULL },
 	{ { "show", "cli", "sockets",  NULL }, "show cli sockets : dump list of cli sockets", cli_parse_default, cli_io_handler_show_cli_sock, NULL },
+	{ { "_getsocks", NULL }, NULL,  _getsocks, NULL },
 	{{},}
 }};
 
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 27ff0fa..d68267e 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -150,6 +150,54 @@
  ********************************/
 
 
+static int uxst_find_compatible_fd(struct listener *l)
+{
+	struct xfer_sock_list *xfer_sock = xfer_sock_list;
+	int ret = -1;
+
+	while (xfer_sock) {
+		struct sockaddr_un *un1 = (void *)&l->addr;
+		struct sockaddr_un *un2 = (void *)&xfer_sock->addr;
+
+		/*
+		 * The bound socket's path as returned by getsockaddr
+		 * will be the temporary name <sockname>.XXXXX.tmp,
+		 * so we can't just compare the two names
+		 */
+		if (xfer_sock->addr.ss_family == AF_UNIX &&
+		    strncmp(un1->sun_path, un2->sun_path,
+		    strlen(un1->sun_path)) == 0) {
+			char *after_sockname = un2->sun_path +
+			    strlen(un1->sun_path);
+			/* Make a reasonnable effort to check that
+			 * it is indeed a haproxy-generated temporary
+			 * name, it's not perfect, but probably good enough.
+			 */
+			if (after_sockname[0] == '.') {
+				after_sockname++;
+				while (after_sockname[0] >= '0' &&
+				    after_sockname[0] <= '9')
+					after_sockname++;
+				if (!strcmp(after_sockname, ".tmp"))
+					break;
+			}
+		}
+		xfer_sock = xfer_sock->next;
+	}
+	if (xfer_sock != NULL) {
+		ret = xfer_sock->fd;
+		if (xfer_sock == xfer_sock_list)
+			xfer_sock_list = xfer_sock->next;
+		if (xfer_sock->prev)
+			xfer_sock->prev->next = xfer_sock->next;
+		if (xfer_sock->next)
+			xfer_sock->next->prev = xfer_sock->next->prev;
+		free(xfer_sock);
+	}
+	return ret;
+
+}
+
 /* This function creates a UNIX socket associated to the listener. It changes
  * the state from ASSIGNED to LISTEN. The socket is NOT enabled for polling.
  * The return value is composed from ERR_NONE, ERR_RETRYABLE and ERR_FATAL. It
@@ -179,6 +227,8 @@
 	if (listener->state != LI_ASSIGNED)
 		return ERR_NONE; /* already bound */
 		
+	if (listener->fd == -1)
+		listener->fd = uxst_find_compatible_fd(listener);
 	path = ((struct sockaddr_un *)&listener->addr)->sun_path;
 
 	/* if the listener already has an fd assigned, then we were offered the