diff options
Diffstat (limited to 'xlators/nfs/lib/src/rpc-socket.c')
| -rw-r--r-- | xlators/nfs/lib/src/rpc-socket.c | 358 | 
1 files changed, 358 insertions, 0 deletions
diff --git a/xlators/nfs/lib/src/rpc-socket.c b/xlators/nfs/lib/src/rpc-socket.c new file mode 100644 index 00000000000..01f114a8530 --- /dev/null +++ b/xlators/nfs/lib/src/rpc-socket.c @@ -0,0 +1,358 @@ +/* +  Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> +  This file is part of GlusterFS. + +  GlusterFS is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published +  by the Free Software Foundation; either version 3 of the License, +  or (at your option) any later version. + +  GlusterFS is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program.  If not, see +  <http://www.gnu.org/licenses/>. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "rpc-socket.h" +#include "rpcsvc.h" +#include "dict.h" +#include "logging.h" +#include "byte-order.h" +#include "common-utils.h" +#include "compat-errno.h" + +#include <fcntl.h> +#include <errno.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> + +static int +rpcsvc_socket_server_get_local_socket (int addrfam, char *listenhost, +                                       uint16_t listenport, +                                       struct sockaddr *addr, +                                       socklen_t *addr_len) +{ +        struct addrinfo         hints, *res = 0; +        char                    service[NI_MAXSERV]; +        int                     ret = -1; + +        memset (service, 0, sizeof (service)); +        sprintf (service, "%d", listenport); + +        memset (&hints, 0, sizeof (hints)); +        addr->sa_family = hints.ai_family = addrfam; +        hints.ai_socktype = SOCK_STREAM; +        hints.ai_flags    = AI_ADDRCONFIG | AI_PASSIVE; + +        ret = getaddrinfo(listenhost, service, &hints, &res); +        if (ret != 0) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, +                        "getaddrinfo failed for host %s, service %s (%s)", +                        listenhost, service, gai_strerror (ret)); +                ret = -1; +                goto err; +        } + +        memcpy (addr, res->ai_addr, res->ai_addrlen); +        *addr_len = res->ai_addrlen; + +        freeaddrinfo (res); +        ret = 0; + +err: +        return ret; +} + + +int +rpcsvc_socket_listen (int addrfam, char *listenhost, uint16_t listenport) +{ +        int                     sock = -1; +        struct sockaddr_storage sockaddr; +        socklen_t               sockaddr_len; +        int                     flags = 0; +        int                     ret = -1; +        int                     opt = 1; + +        ret = rpcsvc_socket_server_get_local_socket (addrfam, listenhost, +                                                     listenport,SA (&sockaddr), +                                                     &sockaddr_len); + +        if (ret == -1) +                return ret; + +        sock = socket (SA (&sockaddr)->sa_family, SOCK_STREAM, 0); +        if (sock == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "socket creation failed" +                        " (%s)", strerror (errno)); +                goto err; +        } + +        flags = fcntl (sock, F_GETFL); +        if (flags == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "cannot get socket flags" +                        " (%s)", strerror(errno)); +                goto close_err; +        } + +        ret = fcntl (sock, F_SETFL, flags | O_NONBLOCK); +        if (ret == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "cannot set socket " +                        "non-blocking (%s)", strerror (errno)); +                goto close_err; +        } + +        ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)); +        if (ret == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "setsockopt() for " +                        "SO_REUSEADDR failed (%s)", strerror (errno)); +                goto close_err; +        } + +        ret = bind (sock, (struct sockaddr *)&sockaddr, sockaddr_len); +        if (ret == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "binding socket failed:" +                        " %s", strerror (errno)); +                if (errno == EADDRINUSE) +                        gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "Port is already" +                                " in use"); +                goto close_err; +        } + +        ret = listen (sock, 10); +        if (ret == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "could not listen on" +                        " socket (%s)", strerror (errno)); +                goto close_err; +        } + +        return sock; + +close_err: +        close (sock); +        sock = -1; + +err: +        return sock; +} + + +int +rpcsvc_socket_accept (int listenfd) +{ +        int                     new_sock = -1; +        struct sockaddr_storage new_sockaddr = {0, }; +        socklen_t               addrlen = sizeof (new_sockaddr); +        int                     flags = 0; +        int                     ret = -1; +        int                     on = 1; + +        new_sock = accept (listenfd, SA (&new_sockaddr), &addrlen); +        if (new_sock == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR,"accept on socket failed"); +                goto err; +        } + +        flags = fcntl (new_sock, F_GETFL); +        if (flags == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "cannot get socket flags" +                        " (%s)", strerror(errno)); +                goto close_err; +        } + +        ret = fcntl (new_sock, F_SETFL, flags | O_NONBLOCK); +        if (ret == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "cannot set socket " +                        "non-blocking (%s)", strerror (errno)); +                goto close_err; +        } + +#ifdef TCP_NODELAY +        ret = setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); +        if (ret == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "cannot set no-delay " +                        " socket option"); +        } +#endif + +        return new_sock; + +close_err: +        close (new_sock); +        new_sock = -1; + +err: +        return new_sock; +} + +ssize_t +rpcsvc_socket_read (int sockfd, char *readaddr, size_t readsize) +{ +        ssize_t         dataread = 0; +        ssize_t         readlen = -1; + +        if (!readaddr) +                return -1; + +        while (readsize > 0) { +                readlen = read (sockfd, readaddr, readsize); +                if (readlen == -1) { +                        if (errno != EAGAIN) { +                                dataread = -1; +                                break; +                        } else +                                break; +                } else if (readlen == 0) +                        break; + +                dataread += readlen; +                readaddr += readlen; +                readsize -= readlen; +        } + +        return dataread; +} + + +ssize_t +rpcsvc_socket_write (int sockfd, char *buffer, size_t size) +{ +        size_t          writelen = -1; +        ssize_t         written = 0; + +        if (!buffer) +                return -1; + +        while (size > 0) { +                writelen = write (sockfd, buffer, size); +                if (writelen == -1) { +                        if (errno != EAGAIN) { +                                written = -1; +                                break; +                        } else +                                break; +                } else if (writelen == 0) +                        break; + +                written += writelen; +                size -= writelen; +                buffer += writelen; +        } + +        return written; +} + + +int +rpcsvc_socket_peername (int sockfd, char *hostname, int hostlen) +{ +        struct sockaddr         sa; +        socklen_t               sl = sizeof (sa); +        int                     ret = EAI_FAIL; + +        if (!hostname) +                return ret; + +        ret = getpeername (sockfd, &sa, &sl); +        if (ret == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "Failed to get peer name:" +                        " %s", strerror (errno)); +                ret = EAI_FAIL; +                goto err; +        } + +        ret = getnameinfo (&sa, sl, hostname, hostlen, NULL, 0, 0); +        if (ret != 0) +                goto err; + +err: +        return ret; +} + + +int +rpcsvc_socket_peeraddr (int sockfd, char *addrstr, int addrlen, +                        struct sockaddr *returnsa, socklen_t sasize) +{ +        struct sockaddr         sa; +        int                     ret = EAI_FAIL; + +        if (returnsa) +                ret = getpeername (sockfd, returnsa, &sasize); +        else { +                sasize = sizeof (sa); +                ret = getpeername (sockfd, &sa, &sasize); +        } + +        if (ret == -1) { +                gf_log (GF_RPCSVC_SOCK, GF_LOG_ERROR, "Failed to get peer addr:" +                        " %s", strerror (errno)); +                ret = EAI_FAIL; +                goto err; +        } + +        /* If caller did not specify a string into which the address can be +         * stored, dont bother getting it. +         */ +        if (!addrstr) { +                ret = 0; +                goto err; +        } + +        if (returnsa) +                ret = getnameinfo (returnsa, sasize, addrstr, addrlen, NULL, 0, +                                   NI_NUMERICHOST); +        else +                ret = getnameinfo (&sa, sasize, addrstr, addrlen, NULL, 0, +                                   NI_NUMERICHOST); + +err: +        return ret; +} + + +int +rpcsvc_socket_block_tx (int sockfd) +{ +        int     ret = -1; +        int     on = 1; + +#ifdef TCP_CORK +        ret = setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, &on, sizeof(on)); +#endif + +#ifdef TCP_NOPUSH +        ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof(on)); +#endif + +        return ret; +} + + +int +rpcsvc_socket_unblock_tx (int sockfd) +{ +        int     ret = -1; +        int     off = 0; + +#ifdef TCP_CORK +        ret = setsockopt(sockfd, IPPROTO_TCP, TCP_CORK, &off, sizeof(off)); +#endif + +#ifdef TCP_NOPUSH +        ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NOPUSH, &off, sizeof(off)); +#endif +        return ret; +} +  | 
