summaryrefslogtreecommitdiffstats
path: root/xlators/nfs/lib/src/rpc-socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/nfs/lib/src/rpc-socket.c')
-rw-r--r--xlators/nfs/lib/src/rpc-socket.c358
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;
+}
+