gui: Combine open_stat_pipe() and para_open_stat_pipe().
[paraslash.git] / net.c
diff --git a/net.c b/net.c
index d9b3a9d..59b7f36 100644 (file)
--- a/net.c
+++ b/net.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-2010 Andre Noll <maan@systemlinux.org>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -279,7 +279,7 @@ struct flowopts *flowopt_new(void)
  * Append new socket option to flowopt queue.
  *
  * \param fo   The flowopt queue to append to.
- * \param lev  Level at which \opt resides.
+ * \param lev  Level at which \opt resides.
  * \param opt  New option to add.
  * \param name Stringified name of \a opt.
  * \param val  The value to set \a opt to.
@@ -351,7 +351,6 @@ static void flowopt_cleanup(struct flowopts *fo)
 /**
  * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket.
  *
- * \param l3type The layer-3 type (\p AF_INET, \p AF_INET6, \p AF_UNSPEC).
  * \param l4type The layer-4 type (\p IPPROTO_xxx).
  * \param passive Whether this is a passive (1) or active (0) socket.
  * \param host Remote or local hostname or IPv/6 address string.
@@ -384,7 +383,7 @@ int makesock(unsigned l4type, bool passive,
        struct addrinfo *local = NULL, *src,
                        *remote = NULL, *dst, hints;
        unsigned int    l3type = AF_UNSPEC;
-       int             rc, sockfd = -1,
+       int             rc, on = 1, sockfd = -1,
                        socktype = sock_type(l4type);
        char port[6]; /* port number has at most 5 digits */
 
@@ -429,6 +428,19 @@ int makesock(unsigned l4type, bool passive,
                if (sockfd < 0)
                        goto get_next_dst;
 
+               /*
+                * Reuse the address on passive sockets to avoid failure on
+                * restart (protocols using listen()) and when creating
+                * multiple listener instances (UDP multicast).
+                */
+               if (passive && setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+                                                 &on, sizeof(on)) == -1) {
+                       rc = errno;
+                       close(sockfd);
+                       PARA_ERROR_LOG("can not set SO_REUSEADDR: %s\n",
+                                      strerror(rc));
+                       return -ERRNO_TO_PARA_ERROR(rc);
+               }
                flowopt_setopts(sockfd, fo);
 
                if (src) {
@@ -457,10 +469,11 @@ get_next_src:
        flowopt_cleanup(fo);
 
        if (src == NULL && dst == NULL) {
+               rc = errno;
                PARA_ERROR_LOG("can not create %s socket %s#%s.\n",
                        layer4_name(l4type), host? host : (passive?
                        "[loopback]" : "[localhost]"), port);
-               return -ERRNO_TO_PARA_ERROR(errno);
+               return -ERRNO_TO_PARA_ERROR(rc);
        }
        return sockfd;
 }
@@ -479,20 +492,14 @@ get_next_src:
  */
 int para_listen(unsigned l4type, uint16_t port, struct flowopts *fo)
 {
-       int fd, ret;
+       int ret, fd = makesock(l4type, 1, NULL, port, fo);
 
-       if (fo == NULL)
-               fo = flowopt_new();
-
-       /* Reuse the address to avoid failure on restart. */
-       OPT_ENABLE(fo, SOL_SOCKET, SO_REUSEADDR);
-
-       fd = makesock(l4type, 1, NULL, port, fo);
        if (fd > 0) {
                ret = listen(fd, BACKLOG);
                if (ret < 0) {
+                       ret = errno;
                        close(fd);
-                       return -ERRNO_TO_PARA_ERROR(errno);
+                       return -ERRNO_TO_PARA_ERROR(ret);
                }
                PARA_INFO_LOG("listening on %s port %u, fd %d\n",
                        layer4_name(l4type), port, fd);
@@ -749,50 +756,68 @@ int recv_buffer(int fd, char *buf, size_t size)
  * Wrapper around the accept system call.
  *
  * \param fd The listening socket.
+ * \param rfds An optional fd_set pointer.
  * \param addr Structure which is filled in with the address of the peer socket.
  * \param size Should contain the size of the structure pointed to by \a addr.
+ * \param new_fd Result pointer.
  *
- * Accept incoming connections on \a addr. Retry if interrupted.
+ * Accept incoming connections on \a addr, retry if interrupted. If \a rfds is
+ * not \p NULL, return 0 if \a fd is not set in \a rfds without calling accept().
  *
- * \return The new file descriptor on success, negative on errors.
+ * \return Negative on errors, zero if no connections are present to be accepted,
+ * one otherwise.
  *
  * \sa accept(2).
  */
-int para_accept(int fd, void *addr, socklen_t size)
+int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd)
 {
-       int new_fd;
+       int ret;
 
+       if (rfds && !FD_ISSET(fd, rfds))
+               return 0;
        do
-               new_fd = accept(fd, (struct sockaddr *) addr, &size);
-       while (new_fd < 0 && errno == EINTR);
-       return new_fd < 0? -ERRNO_TO_PARA_ERROR(errno) : new_fd;
+               ret = accept(fd, (struct sockaddr *) addr, &size);
+       while (ret < 0 && errno == EINTR);
+
+       if (ret >= 0) {
+               *new_fd = ret;
+               return 1;
+       }
+       if (errno == EAGAIN || errno == EWOULDBLOCK)
+               return 0;
+       return -ERRNO_TO_PARA_ERROR(errno);
 }
 
 /**
- * Probe the list of DCCP CCIDs supported locally by the host.
- * \param ccids Array to be filled in.
- * \param nccids Length of \a ccids.
- * \return Pointer to \a ccids, NULL on failure.
+ * Probe the list of DCCP CCIDs configured on this host.
+ * \param ccid_array Pointer to return statically allocated array in.
+ * \return Number of elements returned in \a ccid_array or error.
  *
  * NB: This feature is only available on Linux > 2.6.30; on older kernels
  * ENOPROTOOPT ("Protocol not available") will be returned.
  */
-const uint8_t *dccp_available_ccids(uint8_t *ccids, uint8_t *nccids)
+int dccp_available_ccids(uint8_t **ccid_array)
 {
-       int fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL);
+       static uint8_t ccids[DCCP_MAX_HOST_CCIDS];
+       socklen_t nccids = sizeof(ccids);
+       int ret, fd;
 
-       if (fd < 0)
-               return NULL;
+       ret = fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL);
+       if (ret < 0)
+               return ret;
 
        if (getsockopt(fd, SOL_DCCP, DCCP_SOCKOPT_AVAILABLE_CCIDS,
-                                       ccids, (socklen_t *)nccids) < 0) {
+                                               ccids, &nccids) < 0) {
+               ret = errno;
+               close(fd);
                PARA_ERROR_LOG("No DCCP_SOCKOPT_AVAILABLE_CCIDS: %s\n",
-                               strerror(errno));
-               *nccids = 0;
+                               strerror(ret));
+               return -ERRNO_TO_PARA_ERROR(ret);
        }
-       close(fd);
 
-       return *nccids ? ccids : NULL;
+       close(fd);
+       *ccid_array = ccids;
+       return nccids;
 }
 
 /**
@@ -1005,41 +1030,3 @@ int recv_cred_buffer(int fd, char *buf, size_t size)
        return result;
 }
 #endif /* HAVE_UCRED */
-
-/**
- * Receive a buffer and check for a pattern.
- *
- * \param fd The file descriptor to receive from.
- * \param pattern The expected pattern.
- * \param bufsize The size of the internal buffer.
- *
- * \return Positive if \a pattern was received, negative otherwise.
- *
- * This function tries to receive at most \a bufsize bytes from file descriptor
- * \a fd. If at least \p strlen(\a pattern) bytes were received, the beginning
- * of the received buffer is compared with \a pattern, ignoring case.
- *
- * \sa recv_buffer(), \sa strncasecmp(3).
- */
-int recv_pattern(int fd, const char *pattern, size_t bufsize)
-{
-       size_t len = strlen(pattern);
-       char *buf = para_malloc(bufsize + 1);
-       int ret = -E_RECV_PATTERN, n = recv_buffer(fd, buf, bufsize + 1);
-
-       if (n < len)
-               goto out;
-       if (strncasecmp(buf, pattern, len))
-               goto out;
-       ret = 1;
-out:
-       if (ret < 0) {
-               PARA_NOTICE_LOG("did not receive pattern '%s'\n", pattern);
-               if (n > 0)
-                       PARA_NOTICE_LOG("recvd %d bytes: %s\n", n, buf);
-               else if (n < 0)
-                       PARA_NOTICE_LOG("%s\n", para_strerror(-n));
-       }
-       free(buf);
-       return ret;
-}