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 <[email protected]>

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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to