[Patch 1/2] cld: implement dynamic port

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch is needed to build on Fedora. The buildsystem can run two
builds on the same system in the same time (for example, one for 32-bit
buildroot, and another for 64-bit buildroot). Since we include
"make check" step, port numbers used by simultaneous builds conflict,
causing the build to fail.

Keeping a patch for this in Fedora is additional labor placed on
maintainer, and there's a remote possibility that it may be useful
for someone else, so let's include this feature.

Signed-off-by: Pete Zaitcev <zaitcev@xxxxxxxxxx>

diff --git a/include/cld_msg.h b/include/cld_msg.h
index e4c8f28..641b857 100644
--- a/include/cld_msg.h
+++ b/include/cld_msg.h
@@ -258,5 +258,6 @@ struct cld_msg_event {
 extern unsigned long long cld_sid2llu(const uint8_t *sid);
 extern void __cld_rand64(void *p);
 extern const char *cld_errstr(enum cle_err_codes ecode);
+extern int cld_readport(const char *fname);
 
 #endif /* __CLD_MSG_H__ */
diff --git a/lib/common.c b/lib/common.c
index a4eda54..68f60f8 100644
--- a/lib/common.c
+++ b/lib/common.c
@@ -1,5 +1,8 @@
 
 #include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
 #include <glib.h>
 #include <cld-private.h>
 #include "cld_msg.h"
@@ -53,3 +56,31 @@ const char *cld_errstr(enum cle_err_codes ecode)
 	return cld_errlist[ecode];
 }
 
+/*
+ * Read a port number from a port file, return the value or negative error.
+ */
+int cld_readport(const char *fname)
+{
+	enum { LEN = 11 };
+	char buf[LEN+1];
+	long port;
+	int fd;
+	int rc;
+
+	if ((fd = open(fname, O_RDONLY)) == -1)
+		return -errno;
+	rc = read(fd, buf, LEN);
+	close(fd);
+	if (rc < 0)
+		return -errno;
+	if (rc == 0)
+		return -EPIPE;
+	buf[rc] = 0;
+
+	port = strtol(buf, NULL, 10);
+	if (port <= 0 || port >= 65636)
+		return -EDOM;
+
+	return (int)port;
+}
+
diff --git a/server/cld.h b/server/cld.h
index 537f07a..e5ea9aa 100644
--- a/server/cld.h
+++ b/server/cld.h
@@ -113,6 +113,8 @@ struct server {
 	int			pid_fd;
 
 	char			*port;		/* bind port */
+	char			*port_file;	/* Port file to write */
+	bool			port_set;
 
 	struct cldb		cldb;		/* database info */
 
diff --git a/server/server.c b/server/server.c
index 9bf328b..ef8a060 100644
--- a/server/server.c
+++ b/server/server.c
@@ -32,6 +32,7 @@
 #include <argp.h>
 #include <netdb.h>
 #include <signal.h>
+#include <netinet/in.h>
 #include <openssl/sha.h>
 #include <openssl/hmac.h>
 #include <cld-private.h>
@@ -66,6 +67,8 @@ static struct argp_option options[] = {
 	{ "strict-free", 1001, NULL, 0,
 	  "For memory-checker runs.  When shutting down server, free local "
 	  "heap, rather than simply exit(2)ing and letting OS clean up." },
+	{ "port-file", 1002, "FILE", 0,
+	  "Write the listen port to FILE. Implies bind to zero." },
 	{ }
 };
 
@@ -600,7 +603,117 @@ static void net_close(void)
 	}
 }
 
-static int net_open(void)
+static int net_open_socket(int addr_fam, int sock_type, int sock_prot,
+			   int addr_len, void *addr_ptr)
+{
+	struct server_poll sp;
+	struct pollfd pfd;
+	int fd, on;
+	int rc;
+
+	fd = socket(addr_fam, sock_type, sock_prot);
+	if (fd < 0) {
+		syslogerr("tcp socket");
+		return -errno;
+	}
+
+	on = 1;
+	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+		syslogerr("setsockopt(SO_REUSEADDR)");
+		close(fd);
+		return -errno;
+	}
+
+	if (bind(fd, addr_ptr, addr_len) < 0) {
+		syslogerr("tcp bind");
+		close(fd);
+		return -errno;
+	}
+
+	rc = fsetflags("udp server", fd, O_NONBLOCK);
+	if (rc) {
+		close(fd);
+		return -errno;
+	}
+
+	sp.fd = fd;
+	sp.cb = udp_srv_event;
+	sp.userdata = NULL;
+	g_array_append_val(cld_srv.poll_data, sp);
+
+	pfd.fd = fd;
+	pfd.events = POLLIN;
+	pfd.revents = 0;
+	g_array_append_val(cld_srv.polls, pfd);
+
+	return fd;
+}
+
+static int net_open_any(void)
+{
+	struct sockaddr_in addr4;
+	struct sockaddr_in6 addr6;
+	FILE *portf;
+	int fd4, fd6;
+	socklen_t addr_len;
+	unsigned short port;
+	int rc;
+
+	port = 0;
+
+	/* Thanks to Linux, IPv6 must be bound first. */
+	memset(&addr6, 0, sizeof(addr6));
+	addr6.sin6_family = AF_INET6;
+	memcpy(&addr6.sin6_addr, &in6addr_any, sizeof(struct in6_addr));
+	fd6 = net_open_socket(AF_INET6, SOCK_DGRAM, 0, sizeof(addr6), &addr6);
+
+	if (fd6 >= 0) {
+		addr_len = sizeof(addr6);
+		if (getsockname(fd6, &addr6, &addr_len) != 0) {
+			rc = errno;
+			cldlog(LOG_ERR, "getsockname failed: %s", strerror(rc));
+			return -rc;
+		}
+		port = ntohs(addr6.sin6_port);
+	}
+
+	memset(&addr4, 0, sizeof(addr4));
+	addr4.sin_family = AF_INET;
+	addr4.sin_addr.s_addr = htonl(INADDR_ANY);
+	/* If IPv6 worked, we must use the same port number for IPv4 */
+	if (port)
+		addr4.sin_port = port;
+	fd4 = net_open_socket(AF_INET, SOCK_DGRAM, 0, sizeof(addr4), &addr4);
+
+	if (!port) {
+		if (fd4 < 0)
+			return fd4;
+
+		addr_len = sizeof(addr4);
+		if (getsockname(fd4, &addr4, &addr_len) != 0) {
+			rc = errno;
+			cldlog(LOG_ERR, "getsockname failed: %s", strerror(rc));
+			return -rc;
+		}
+		port = ntohs(addr4.sin_port);
+	}
+
+	cldlog(LOG_INFO, "Listening on port %u", port);
+
+	portf = fopen(cld_srv.port_file, "w");
+	if (portf == NULL) {
+		rc = errno;
+		cldlog(LOG_INFO, "Cannot create port file %s: %s",
+		       cld_srv.port_file, strerror(rc));
+		return -rc;
+	}
+	fprintf(portf, "%u\n", port);
+	fclose(portf);
+
+	return 0;
+}
+
+static int net_open_known(const char *portstr)
 {
 	int ipv6_found = 0;
 	int rc;
@@ -611,10 +724,10 @@ static int net_open(void)
 	hints.ai_socktype = SOCK_DGRAM;
 	hints.ai_flags = AI_PASSIVE;
 
-	rc = getaddrinfo(NULL, cld_srv.port, &hints, &res0);
+	rc = getaddrinfo(NULL, portstr, &hints, &res0);
 	if (rc) {
 		cldlog(LOG_ERR, "getaddrinfo(*:%s) failed: %s",
-		       cld_srv.port, gai_strerror(rc));
+		       portstr, gai_strerror(rc));
 		rc = -EINVAL;
 		goto err_addr;
 	}
@@ -637,50 +750,16 @@ static int net_open(void)
 #endif
 
 	for (res = res0; res; res = res->ai_next) {
-		struct server_poll sp;
-		struct pollfd pfd;
-		int fd, on;
 		char listen_host[65], listen_serv[65];
 
 		if (ipv6_found && res->ai_family == PF_INET)
 			continue;
 
-		fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-		if (fd < 0) {
-			syslogerr("tcp socket");
-			return -errno;
-		}
-
-		on = 1;
-		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on,
-			       sizeof(on)) < 0) {
-			syslogerr("setsockopt(SO_REUSEADDR)");
-			rc = -errno;
+		rc = net_open_socket(res->ai_family, res->ai_socktype,
+				     res->ai_protocol, 
+				     res->ai_addrlen, res->ai_addr);
+		if (rc < 0)
 			goto err_out;
-		}
-
-		if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
-			syslogerr("tcp bind");
-			close(fd);
-			rc = -errno;
-			goto err_out;
-		}
-
-		rc = fsetflags("udp server", fd, O_NONBLOCK);
-		if (rc) {
-			close(fd);
-			goto err_out;
-		}
-
-		sp.fd = fd;
-		sp.cb = udp_srv_event;
-		sp.userdata = NULL;
-		g_array_append_val(cld_srv.poll_data, sp);
-
-		pfd.fd = fd;
-		pfd.events = POLLIN;
-		pfd.revents = 0;
-		g_array_append_val(cld_srv.polls, pfd);
 
 		getnameinfo(res->ai_addr, res->ai_addrlen,
 			    listen_host, sizeof(listen_host),
@@ -701,6 +780,14 @@ err_addr:
 	return rc;
 }
 
+static int net_open(void)
+{
+	if (cld_srv.port_file)
+		return net_open_any();
+	else
+		return net_open_known(cld_srv.port);
+}
+
 static void segv_signal(int signo)
 {
 	cldlog(LOG_ERR, "SIGSEGV");
@@ -749,9 +836,10 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
 		cld_srv.flags |= SFL_FOREGROUND;
 		break;
 	case 'p':
-		if (atoi(arg) > 0 && atoi(arg) < 65536)
+		if (atoi(arg) > 0 && atoi(arg) < 65536) {
 			cld_srv.port = arg;
-		else {
+			cld_srv.port_set = true;
+		} else {
 			fprintf(stderr, "invalid port: '%s'\n", arg);
 			argp_usage(state);
 		}
@@ -763,6 +851,9 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
 	case 1001:			/* --strict-free */
 		strict_free = true;
 		break;
+	case 1002:
+		cld_srv.port_file = arg;
+		break;
 
 	case ARGP_KEY_ARG:
 		argp_usage(state);	/* too many args */
@@ -797,6 +888,11 @@ int main (int argc, char *argv[])
 		fprintf(stderr, "argp_parse failed: %s\n", strerror(aprc));
 		return 1;
 	}
+	if (cld_srv.port_set && cld_srv.port_file) {
+		fprintf(stderr, "Options --port and --port-file must not"
+			" be set together\n");
+		return 1;
+	}
 
 	/*
 	 * open syslog, background outselves, write PID file ASAP
diff --git a/test/it-works.c b/test/it-works.c
index 9bec2a7..bd2f965 100644
--- a/test/it-works.c
+++ b/test/it-works.c
@@ -72,9 +72,16 @@ static struct cldc_ops ops = {
 static int init(void)
 {
 	int rc;
+	int port;
 	struct cldc_call_opts copts;
 
-	rc = cldc_udp_new("localhost", 18181, &udp);
+	port = cld_readport("cld.port");	/* FIXME need test.h */
+	if (port < 0)
+		return port;
+	if (port == 0)
+		return -1;
+
+	rc = cldc_udp_new("localhost", port, &udp);
 	if (rc)
 		return rc;
 
diff --git a/test/load-file-event.c b/test/load-file-event.c
index 3274787..1620508 100644
--- a/test/load-file-event.c
+++ b/test/load-file-event.c
@@ -188,6 +188,7 @@ static struct cldc_ops ops = {
 static int init(char *name)
 {
 	int rc;
+	int port;
 	struct cldc_call_opts copts;
 
 	run.fname = name;
@@ -202,7 +203,13 @@ static int init(char *name)
 	run.len = rc;
 #endif
 
-	rc = cldc_udp_new("localhost", 18181, &run.udp);
+	port = cld_readport("cld.port");	/* FIXME need test.h */
+	if (port < 0)
+		return port;
+	if (port == 0)
+		return -1;
+
+	rc = cldc_udp_new("localhost", port, &run.udp);
 	if (rc)
 		return rc;
 
diff --git a/test/lock-file-event.c b/test/lock-file-event.c
index cbf1647..c69da66 100644
--- a/test/lock-file-event.c
+++ b/test/lock-file-event.c
@@ -208,11 +208,18 @@ static struct cldc_ops ops = {
 static int init(void)
 {
 	int rc;
+	int port;
 	struct cldc_call_opts copts;
 
 	memcpy(run.buf, TESTSTR, TESTLEN);
 
-	rc = cldc_udp_new("localhost", 18181, &run.udp);
+	port = cld_readport("cld.port");	/* FIXME need test.h */
+	if (port < 0)
+		return port;
+	if (port == 0)
+		return -1;
+
+	rc = cldc_udp_new("localhost", port, &run.udp);
 	if (rc)
 		return rc;
 
diff --git a/test/pid-exists b/test/pid-exists
index 351b4f1..34c7258 100755
--- a/test/pid-exists
+++ b/test/pid-exists
@@ -2,7 +2,13 @@
 
 if [ ! -f cld.pid ]
 then
-	echo "pid file not found."
+	echo "pid file not found." >&2
+	exit 1
+fi
+
+if [ ! -f cld.port ]
+then
+	echo "port file not found." >&2
 	exit 1
 fi
 
diff --git a/test/save-file-event.c b/test/save-file-event.c
index 01a0331..1b3418a 100644
--- a/test/save-file-event.c
+++ b/test/save-file-event.c
@@ -191,6 +191,7 @@ static struct cldc_ops ops = {
 static int init(char *name)
 {
 	int rc;
+	int port;
 	struct cldc_call_opts copts;
 
 	run.fname = name;
@@ -208,7 +209,13 @@ static int init(char *name)
 	run.len = TESTLEN;
 #endif
 
-	rc = cldc_udp_new("localhost", 18181, &run.udp);
+	port = cld_readport("cld.port");	/* FIXME need test.h */
+	if (port < 0)
+		return port;
+	if (port == 0)
+		return -1;
+
+	rc = cldc_udp_new("localhost", port, &run.udp);
 	if (rc)
 		return rc;
 
diff --git a/test/start-daemon b/test/start-daemon
index 4cb9fd7..25cdab4 100755
--- a/test/start-daemon
+++ b/test/start-daemon
@@ -6,7 +6,12 @@ then
 	exit 1
 fi
 
-../server/cld -P cld.pid -d "$PWD/data" -p 18181 -E
+## Static port
+cldport=18181
+echo $cldport > cld.port
+../server/cld -P cld.pid -d "$PWD/data" -p $cldport -E
+## Dynamic port
+# ../server/cld -P cld.pid -d "$PWD/data" --port-file=cld.port -E
 
 sleep 3
 
diff --git a/test/stop-daemon b/test/stop-daemon
index 27d985e..1949dc4 100755
--- a/test/stop-daemon
+++ b/test/stop-daemon
@@ -1,5 +1,7 @@
 #!/bin/sh
 
+rm -f cld.port
+
 if [ ! -f cld.pid ]
 then
 	echo no daemon pid file found.
--
To unsubscribe from this list: send the line "unsubscribe hail-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Fedora Clound]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux