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