* @param bind_address Local address to bind, or NULL to allow it to
* default.
**/
-static int open_socket_in(int type, int port, const char *bind_address,
- int af_hint)
+static int *open_socket_in(int type, int port, const char *bind_address,
+ int af_hint)
{
int one=1;
- int s;
+ int s, *sp, *socks, maxs;
struct addrinfo hints, *all_ai, *resp;
char portbuf[10];
int error;
if (error) {
rprintf(FERROR, RSYNC_NAME ": getaddrinfo: bind address %s: %s\n",
bind_address, gai_strerror(error));
- return -1;
+ return NULL;
+ }
+
+ /* Count max number of sockets we might open. */
+ for (maxs = 0, resp = all_ai; resp; resp = resp->ai_next, maxs++) {}
+ socks = new_array(int, maxs + 1);
+ if (!socks) {
+ rprintf(FERROR,
+ RSYNC_NAME "couldn't allocate memory for sockets");
+ return NULL;
}
/* We may not be able to create the socket, if for example the
* machine knows about IPv6 in the C library, but not in the
* kernel. */
+ sp = socks + 1; /* Leave room for count at start of array. */
for (resp = all_ai; resp; resp = resp->ai_next) {
s = socket(resp->ai_family, resp->ai_socktype,
resp->ai_protocol);
- if (s == -1)
+ if (s == -1) {
/* See if there's another address that will work... */
continue;
+ }
setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *)&one, sizeof one);
+#ifdef IPV6_V6ONLY
+ if (resp->ai_family == AF_INET6) {
+ setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *)&one, sizeof one);
+ }
+#endif
+
/* Now we've got a socket - we need to bind it. */
if (bind(s, resp->ai_addr, resp->ai_addrlen) < 0) {
/* Nope, try another */
continue;
}
- freeaddrinfo(all_ai);
- return s;
+ *sp++ = s;
}
+ *socks = sp - socks - 1; /* Save count. */
- rprintf(FERROR, RSYNC_NAME ": open inbound socket on port %d failed: "
- "%s\n",
- port,
- strerror(errno));
+ if (all_ai)
+ freeaddrinfo(all_ai);
- freeaddrinfo(all_ai);
- return -1;
+ if (*socks == 0) {
+ rprintf(FERROR,
+ RSYNC_NAME ": open inbound socket on port %d failed: "
+ "%s\n", port, strerror(errno));
+ free(socks);
+ return NULL;
+ }
+ return socks;
}
void start_accept_loop(int port, int (*fn)(int, int))
{
- int s;
+ fd_set deffds;
+ int *sp, maxfd, i, j;
extern char *bind_address;
extern int default_af_hint;
/* open an incoming socket */
- s = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
- if (s == -1)
+ sp = open_socket_in(SOCK_STREAM, port, bind_address, default_af_hint);
+ if (sp == NULL)
exit_cleanup(RERR_SOCKETIO);
/* ready to listen */
- if (listen(s, 5) == -1) {
- close(s);
- exit_cleanup(RERR_SOCKETIO);
+ FD_ZERO(&deffds);
+ maxfd = -1;
+ for (i = 1; i <= *sp; i++) {
+ if (listen(sp[i], 5) == -1) {
+ for (j = 1; j <= i; j++)
+ close(sp[j]);
+ free(sp);
+ exit_cleanup(RERR_SOCKETIO);
+ }
+ FD_SET(sp[i], &deffds);
+ if (maxfd < sp[i])
+ maxfd = sp[i];
}
forever */
log_close();
- FD_ZERO(&fds);
- FD_SET(s, &fds);
-
- if (select(s+1, &fds, NULL, NULL, NULL) != 1)
- continue;
+#ifdef FD_COPY
+ FD_COPY(&deffds, &fds);
+#else
+ fds = deffds;
+#endif
- if (!FD_ISSET(s, &fds))
+ if (select(maxfd + 1, &fds, NULL, NULL, NULL) != 1)
continue;
- fd = accept(s,(struct sockaddr *)&addr,&addrlen);
+ fd = -1;
+ for (i = 1; i <= *sp; i++) {
+ if (FD_ISSET(sp[i], &fds)) {
+ fd = accept(sp[i], (struct sockaddr *)&addr,
+ &addrlen);
+ break;
+ }
+ }
- if (fd == -1)
+ if (fd < 0)
continue;
signal(SIGCHLD, sigchld_handler);
if ((pid = fork()) == 0) {
int ret;
- close(s);
+ close(sp[i]);
/* open log file in child before possibly giving
up privileges */
log_open();
close(fd);
}
}
+ free(sp);
}