diff options
Diffstat (limited to 'rpc/rpc-transport/socket/src/socket.c')
| -rw-r--r-- | rpc/rpc-transport/socket/src/socket.c | 2845 |
1 files changed, 2098 insertions, 747 deletions
diff --git a/rpc/rpc-transport/socket/src/socket.c b/rpc/rpc-transport/socket/src/socket.c index b017462a5..93da3f296 100644 --- a/rpc/rpc-transport/socket/src/socket.c +++ b/rpc/rpc-transport/socket/src/socket.c @@ -1,20 +1,11 @@ /* - Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.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/>. + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. */ @@ -37,101 +28,408 @@ /* ugly #includes below */ #include "protocol-common.h" #include "glusterfs3-xdr.h" +#include "xdr-nfs3.h" +#include "rpcsvc.h" #include <fcntl.h> #include <errno.h> #include <netinet/tcp.h> - +#include <rpc/xdr.h> +#include <sys/ioctl.h> #define GF_LOG_ERRNO(errno) ((errno == ENOTCONN) ? GF_LOG_DEBUG : GF_LOG_ERROR) #define SA(ptr) ((struct sockaddr *)ptr) -#define __socket_proto_reset_pending(priv) do { \ - memset (&priv->incoming.frag.vector, 0, \ - sizeof (priv->incoming.frag.vector)); \ - priv->incoming.frag.pending_vector = \ - &priv->incoming.frag.vector; \ - priv->incoming.frag.pending_vector->iov_base = \ - priv->incoming.frag.fragcurrent; \ - priv->incoming.pending_vector = \ - priv->incoming.frag.pending_vector; \ - } while (0); - - -#define __socket_proto_update_pending(priv) \ - do { \ - uint32_t remaining_fragsize = 0; \ - if (priv->incoming.frag.pending_vector->iov_len == 0) { \ - remaining_fragsize = RPC_FRAGSIZE (priv->incoming.fraghdr) \ - - priv->incoming.frag.bytes_read; \ - \ - priv->incoming.frag.pending_vector->iov_len = \ - remaining_fragsize > priv->incoming.frag.remaining_size \ - ? priv->incoming.frag.remaining_size : remaining_fragsize; \ - \ - priv->incoming.frag.remaining_size -= \ - priv->incoming.frag.pending_vector->iov_len; \ - } \ - } while (0); - -#define __socket_proto_update_priv_after_read(priv, ret, bytes_read) \ - { \ - priv->incoming.frag.fragcurrent += bytes_read; \ - priv->incoming.frag.bytes_read += bytes_read; \ - \ - if ((ret > 0) || (priv->incoming.frag.remaining_size != 0)) { \ - if (priv->incoming.frag.remaining_size != 0) { \ - __socket_proto_reset_pending (priv); \ - } \ - \ - gf_log (this->name, GF_LOG_TRACE, "partial read on non-blocking socket"); \ - \ - break; \ - } \ - } - -#define __socket_proto_init_pending(priv, size) \ - do { \ - uint32_t remaining_fragsize = 0; \ - remaining_fragsize = RPC_FRAGSIZE (priv->incoming.fraghdr) \ - - priv->incoming.frag.bytes_read; \ - \ - __socket_proto_reset_pending (priv); \ - \ - priv->incoming.frag.pending_vector->iov_len = \ - remaining_fragsize > size ? size : remaining_fragsize; \ - \ - priv->incoming.frag.remaining_size = \ - size - priv->incoming.frag.pending_vector->iov_len; \ - \ -} while (0); +#define SSL_ENABLED_OPT "transport.socket.ssl-enabled" +#define SSL_OWN_CERT_OPT "transport.socket.ssl-own-cert" +#define SSL_PRIVATE_KEY_OPT "transport.socket.ssl-private-key" +#define SSL_CA_LIST_OPT "transport.socket.ssl-ca-list" +#define OWN_THREAD_OPT "transport.socket.own-thread" + +/* TBD: do automake substitutions etc. (ick) to set these. */ +#if !defined(DEFAULT_CERT_PATH) +#define DEFAULT_CERT_PATH "/etc/ssl/glusterfs.pem" +#endif +#if !defined(DEFAULT_KEY_PATH) +#define DEFAULT_KEY_PATH "/etc/ssl/glusterfs.key" +#endif +#if !defined(DEFAULT_CA_PATH) +#define DEFAULT_CA_PATH "/etc/ssl/glusterfs.ca" +#endif + +#define POLL_MASK_INPUT (POLLIN | POLLPRI) +#define POLL_MASK_OUTPUT (POLLOUT) +#define POLL_MASK_ERROR (POLLERR | POLLHUP | POLLNVAL) + +typedef int SSL_unary_func (SSL *); +typedef int SSL_trinary_func (SSL *, void *, int); + +#define __socket_proto_reset_pending(priv) do { \ + struct gf_sock_incoming_frag *frag; \ + frag = &priv->incoming.frag; \ + \ + memset (&frag->vector, 0, sizeof (frag->vector)); \ + frag->pending_vector = &frag->vector; \ + frag->pending_vector->iov_base = frag->fragcurrent; \ + priv->incoming.pending_vector = frag->pending_vector; \ + } while (0) + + +#define __socket_proto_update_pending(priv) \ + do { \ + uint32_t remaining; \ + struct gf_sock_incoming_frag *frag; \ + frag = &priv->incoming.frag; \ + if (frag->pending_vector->iov_len == 0) { \ + remaining = (RPC_FRAGSIZE (priv->incoming.fraghdr) \ + - frag->bytes_read); \ + \ + frag->pending_vector->iov_len = \ + (remaining > frag->remaining_size) \ + ? frag->remaining_size : remaining; \ + \ + frag->remaining_size -= \ + frag->pending_vector->iov_len; \ + } \ + } while (0) + +#define __socket_proto_update_priv_after_read(priv, ret, bytes_read) \ + { \ + struct gf_sock_incoming_frag *frag; \ + frag = &priv->incoming.frag; \ + \ + frag->fragcurrent += bytes_read; \ + frag->bytes_read += bytes_read; \ + \ + if ((ret > 0) || (frag->remaining_size != 0)) { \ + if (frag->remaining_size != 0 && ret == 0) { \ + __socket_proto_reset_pending (priv); \ + } \ + \ + gf_log (this->name, GF_LOG_TRACE, \ + "partial read on non-blocking socket"); \ + \ + break; \ + } \ + } + +#define __socket_proto_init_pending(priv,size) \ + do { \ + uint32_t remaining = 0; \ + struct gf_sock_incoming_frag *frag; \ + frag = &priv->incoming.frag; \ + \ + remaining = (RPC_FRAGSIZE (priv->incoming.fraghdr) \ + - frag->bytes_read); \ + \ + __socket_proto_reset_pending (priv); \ + \ + frag->pending_vector->iov_len = \ + (remaining > size) ? size : remaining; \ + \ + frag->remaining_size = (size - frag->pending_vector->iov_len); \ + \ + } while(0) /* This will be used in a switch case and breaks from the switch case if all * the pending data is not read. */ -#define __socket_proto_read(priv, ret) \ - { \ - size_t bytes_read = 0; \ - \ - __socket_proto_update_pending (priv); \ - \ - ret = __socket_readv (this, \ - priv->incoming.pending_vector, 1, \ - &priv->incoming.pending_vector, \ - &priv->incoming.pending_count, \ - &bytes_read); \ - if (ret == -1) { \ - gf_log (this->name, GF_LOG_TRACE, \ - "reading from socket failed. Error (%s), " \ - "peer (%s)", strerror (errno), \ - this->peerinfo.identifier); \ - break; \ - } \ +#define __socket_proto_read(priv, ret) \ + { \ + size_t bytes_read = 0; \ + struct gf_sock_incoming *in; \ + in = &priv->incoming; \ + \ + __socket_proto_update_pending (priv); \ + \ + ret = __socket_readv (this, \ + in->pending_vector, 1, \ + &in->pending_vector, \ + &in->pending_count, \ + &bytes_read); \ + if (ret == -1) \ + break; \ __socket_proto_update_priv_after_read (priv, ret, bytes_read); \ - } + } + +static int socket_init (rpc_transport_t *this); + +static void +ssl_dump_error_stack (const char *caller) +{ + unsigned long errnum = 0; + char errbuf[120] = {0,}; + + /* OpenSSL docs explicitly give 120 as the error-string length. */ + + while ((errnum = ERR_get_error())) { + ERR_error_string(errnum,errbuf); + gf_log(caller,GF_LOG_ERROR," %s",errbuf); + } +} + +static int +ssl_do (rpc_transport_t *this, void *buf, size_t len, SSL_trinary_func *func) +{ + int r = (-1); + struct pollfd pfd = {-1,}; + socket_private_t *priv = NULL; + + GF_VALIDATE_OR_GOTO(this->name,this->private,out); + priv = this->private; + + for (;;) { + if (buf) { + if (priv->connected == -1) { + /* + * Fields in the SSL structure (especially + * the BIO pointers) are not valid at this + * point, so we'll segfault if we pass them + * to SSL_read/SSL_write. + */ + gf_log(this->name,GF_LOG_INFO, + "lost connection in %s", __func__); + break; + } + r = func(priv->ssl_ssl,buf,len); + } + else { + /* + * We actually need these functions to get to + * priv->connected == 1. + */ + r = ((SSL_unary_func *)func)(priv->ssl_ssl); + } + switch (SSL_get_error(priv->ssl_ssl,r)) { + case SSL_ERROR_NONE: + return r; + case SSL_ERROR_WANT_READ: + pfd.fd = priv->sock; + pfd.events = POLLIN; + if (poll(&pfd,1,-1) < 0) { + gf_log(this->name,GF_LOG_ERROR,"poll error %d", + errno); + } + break; + case SSL_ERROR_WANT_WRITE: + pfd.fd = priv->sock; + pfd.events = POLLOUT; + if (poll(&pfd,1,-1) < 0) { + gf_log(this->name,GF_LOG_ERROR,"poll error %d", + errno); + } + break; + case SSL_ERROR_SYSCALL: + /* This is what we get when remote disconnects. */ + gf_log(this->name,GF_LOG_DEBUG, + "syscall error (probably remote disconnect)"); + errno = ENODATA; + goto out; + default: + errno = EIO; + goto out; /* "break" would just loop again */ + } + } +out: + return -1; +} + +#define ssl_connect_one(t) ssl_do((t),NULL,0,(SSL_trinary_func *)SSL_connect) +#define ssl_accept_one(t) ssl_do((t),NULL,0,(SSL_trinary_func *)SSL_accept) +#define ssl_read_one(t,b,l) ssl_do((t),(b),(l),(SSL_trinary_func *)SSL_read) +#define ssl_write_one(t,b,l) ssl_do((t),(b),(l),(SSL_trinary_func *)SSL_write) + +static int +ssl_setup_connection (rpc_transport_t *this, int server) +{ + X509 *peer = NULL; + char peer_CN[256] = ""; + int ret = -1; + socket_private_t *priv = NULL; + + GF_VALIDATE_OR_GOTO(this->name,this->private,done); + priv = this->private; + priv->ssl_ssl = SSL_new(priv->ssl_ctx); + if (!priv->ssl_ssl) { + gf_log(this->name,GF_LOG_ERROR,"SSL_new failed"); + ssl_dump_error_stack(this->name); + goto done; + } + priv->ssl_sbio = BIO_new_socket(priv->sock,BIO_NOCLOSE); + if (!priv->ssl_sbio) { + gf_log(this->name,GF_LOG_ERROR,"BIO_new_socket failed"); + ssl_dump_error_stack(this->name); + goto free_ssl; + } + SSL_set_bio(priv->ssl_ssl,priv->ssl_sbio,priv->ssl_sbio); + + if (server) { + ret = ssl_accept_one(this); + } + else { + ret = ssl_connect_one(this); + } + + /* Make sure _the call_ succeeded. */ + if (ret < 0) { + goto ssl_error; + } + + /* Make sure _SSL verification_ succeeded, yielding an identity. */ + if (SSL_get_verify_result(priv->ssl_ssl) != X509_V_OK) { + goto ssl_error; + } + peer = SSL_get_peer_certificate(priv->ssl_ssl); + if (!peer) { + goto ssl_error; + } + + /* Finally, everything seems OK. */ + X509_NAME_get_text_by_NID(X509_get_subject_name(peer), + NID_commonName, peer_CN, sizeof(peer_CN)-1); + peer_CN[sizeof(peer_CN)-1] = '\0'; + gf_log(this->name,GF_LOG_INFO,"peer CN = %s", peer_CN); + return 0; + + /* Error paths. */ +ssl_error: + gf_log(this->name,GF_LOG_ERROR,"SSL connect error"); + ssl_dump_error_stack(this->name); +free_ssl: + SSL_free(priv->ssl_ssl); + priv->ssl_ssl = NULL; +done: + return ret; +} + + +static void +ssl_teardown_connection (socket_private_t *priv) +{ + SSL_shutdown(priv->ssl_ssl); + SSL_clear(priv->ssl_ssl); + SSL_free(priv->ssl_ssl); + priv->ssl_ssl = NULL; +} -int socket_init (rpc_transport_t *this); + +static ssize_t +__socket_ssl_readv (rpc_transport_t *this, struct iovec *opvector, int opcount) +{ + socket_private_t *priv = NULL; + int sock = -1; + int ret = -1; + + priv = this->private; + sock = priv->sock; + + if (priv->use_ssl) { + ret = ssl_read_one (this, opvector->iov_base, opvector->iov_len); + } else { + ret = readv (sock, opvector, opcount); + } + + return ret; +} + + +static ssize_t +__socket_ssl_read (rpc_transport_t *this, void *buf, size_t count) +{ + struct iovec iov = {0, }; + int ret = -1; + + iov.iov_base = buf; + iov.iov_len = count; + + ret = __socket_ssl_readv (this, &iov, 1); + + return ret; +} + + +static int +__socket_cached_read (rpc_transport_t *this, struct iovec *opvector, int opcount) +{ + socket_private_t *priv = NULL; + int sock = -1; + struct gf_sock_incoming *in = NULL; + int req_len = -1; + int ret = -1; + + priv = this->private; + sock = priv->sock; + in = &priv->incoming; + req_len = iov_length (opvector, opcount); + + if (in->record_state == SP_STATE_READING_FRAGHDR) { + in->ra_read = 0; + in->ra_served = 0; + in->ra_max = 0; + in->ra_buf = NULL; + goto uncached; + } + + if (!in->ra_max) { + /* first call after passing SP_STATE_READING_FRAGHDR */ + in->ra_max = min (RPC_FRAGSIZE (in->fraghdr), GF_SOCKET_RA_MAX); + /* Note that the in->iobuf is the primary iobuf into which + headers are read into. By using this itself as our + read-ahead cache, we can avoid memory copies in iov_load + */ + in->ra_buf = iobuf_ptr (in->iobuf); + } + + /* fill read-ahead */ + if (in->ra_read < in->ra_max) { + ret = __socket_ssl_read (this, &in->ra_buf[in->ra_read], + (in->ra_max - in->ra_read)); + if (ret > 0) + in->ra_read += ret; + + /* we proceed to test if there is still cached data to + be served even if readahead could not progress */ + } + + /* serve cached */ + if (in->ra_served < in->ra_read) { + ret = iov_load (opvector, opcount, &in->ra_buf[in->ra_served], + min (req_len, (in->ra_read - in->ra_served))); + + in->ra_served += ret; + /* Do not read uncached and cached in the same call */ + goto out; + } + + if (in->ra_read < in->ra_max) + /* If there was no cached data to be served, (and we are + guaranteed to have already performed an attempt to progress + readahead above), and we have not yet read out the full + readahead capacity, then bail out for now without doing + the uncached read below (as that will overtake future cached + read) + */ + goto out; +uncached: + ret = __socket_ssl_readv (this, opvector, opcount); +out: + return ret; +} + +static gf_boolean_t +__does_socket_rwv_error_need_logging (socket_private_t *priv, int write) +{ + int read = !write; + + if (priv->connected == -1) /* Didn't even connect, of course it fails */ + return _gf_false; + + if (read && (priv->read_fail_log == _gf_false)) + return _gf_false; + + return _gf_true; +} /* * return value: @@ -140,7 +438,7 @@ int socket_init (rpc_transport_t *this); * > 0 = incomplete */ -int +static int __socket_rwv (rpc_transport_t *this, struct iovec *vector, int count, struct iovec **pending_vector, int *pending_count, size_t *bytes, int write) @@ -152,36 +450,60 @@ __socket_rwv (rpc_transport_t *this, struct iovec *vector, int count, int opcount = 0; int moved = 0; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + priv = this->private; sock = priv->sock; - opvector = vector; - opcount = count; + opvector = vector; + opcount = count; if (bytes != NULL) { *bytes = 0; } - while (opcount) { + while (opcount > 0) { + if (opvector->iov_len == 0) { + gf_log(this->name,GF_LOG_DEBUG, + "would have passed zero length to read/write"); + ++opvector; + --opcount; + continue; + } if (write) { - ret = writev (sock, opvector, opcount); + if (priv->use_ssl) { + ret = ssl_write_one(this, + opvector->iov_base, opvector->iov_len); + } + else { + ret = writev (sock, opvector, opcount); + } if (ret == 0 || (ret == -1 && errno == EAGAIN)) { /* done for now */ break; } + this->total_bytes_write += ret; } else { - ret = readv (sock, opvector, opcount); + ret = __socket_cached_read (this, opvector, opcount); + + if (ret == 0) { + gf_log(this->name,GF_LOG_DEBUG,"EOF on socket"); + errno = ENODATA; + ret = -1; + } if (ret == -1 && errno == EAGAIN) { /* done for now */ break; } + this->total_bytes_read += ret; } if (ret == 0) { /* Mostly due to 'umount' in client */ - gf_log (this->name, GF_LOG_TRACE, + gf_log (this->name, GF_LOG_DEBUG, "EOF from peer %s", this->peerinfo.identifier); opcount = -1; errno = ENOTCONN; @@ -191,9 +513,18 @@ __socket_rwv (rpc_transport_t *this, struct iovec *vector, int count, if (errno == EINTR) continue; - gf_log (this->name, GF_LOG_TRACE, - "%s failed (%s)", write ? "writev" : "readv", - strerror (errno)); + if (__does_socket_rwv_error_need_logging (priv, + write)) { + gf_log (this->name, GF_LOG_WARNING, + "%s on %s failed (%s)", + write ? "writev":"readv", + this->peerinfo.identifier, + strerror (errno)); + } + + if (priv->use_ssl) { + ssl_dump_error_stack(this->name); + } opcount = -1; break; } @@ -205,6 +536,17 @@ __socket_rwv (rpc_transport_t *this, struct iovec *vector, int count, moved = 0; while (moved < ret) { + if (!opcount) { + gf_log(this->name,GF_LOG_DEBUG, + "ran out of iov, moved %d/%d", + moved, ret); + goto ran_out; + } + if (!opvector[0].iov_len) { + opvector++; + opcount--; + continue; + } if ((ret - moved) >= opvector[0].iov_len) { moved += opvector[0].iov_len; opvector++; @@ -214,24 +556,23 @@ __socket_rwv (rpc_transport_t *this, struct iovec *vector, int count, opvector[0].iov_base += (ret - moved); moved += (ret - moved); } - while (opcount && !opvector[0].iov_len) { - opvector++; - opcount--; - } } } +ran_out: + if (pending_vector) *pending_vector = opvector; if (pending_count) *pending_count = opcount; +out: return opcount; } -int +static int __socket_readv (rpc_transport_t *this, struct iovec *vector, int count, struct iovec **pending_vector, int *pending_count, size_t *bytes) @@ -239,56 +580,98 @@ __socket_readv (rpc_transport_t *this, struct iovec *vector, int count, int ret = -1; ret = __socket_rwv (this, vector, count, - pending_vector, pending_count, bytes, 0); + pending_vector, pending_count, bytes, 0); return ret; } -int +static int __socket_writev (rpc_transport_t *this, struct iovec *vector, int count, struct iovec **pending_vector, int *pending_count) { int ret = -1; ret = __socket_rwv (this, vector, count, - pending_vector, pending_count, NULL, 1); + pending_vector, pending_count, NULL, 1); return ret; } -int +static int +__socket_shutdown (rpc_transport_t *this) +{ + int ret = -1; + socket_private_t *priv = this->private; + + priv->connected = -1; + ret = shutdown (priv->sock, SHUT_RDWR); + if (ret) { + /* its already disconnected.. no need to understand + why it failed to shutdown in normal cases */ + gf_log (this->name, GF_LOG_DEBUG, + "shutdown() returned %d. %s", + ret, strerror (errno)); + } + + return ret; +} + +static int __socket_disconnect (rpc_transport_t *this) { - socket_private_t *priv = NULL; int ret = -1; + socket_private_t *priv = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; + gf_log (this->name, GF_LOG_TRACE, + "disconnecting %p, state=%u gen=%u sock=%d", this, + priv->ot_state, priv->ot_gen, priv->sock); + if (priv->sock != -1) { - ret = shutdown (priv->sock, SHUT_RDWR); - priv->connected = -1; - gf_log (this->name, GF_LOG_TRACE, - "shutdown() returned %d. set connection state to -1", - ret); + ret = __socket_shutdown(this); + if (priv->own_thread) { + /* + * Without this, reconnect (= disconnect + connect) + * won't work except by accident. + */ + close(priv->sock); + priv->sock = -1; + gf_log (this->name, GF_LOG_TRACE, + "OT_PLEASE_DIE on %p", this); + priv->ot_state = OT_PLEASE_DIE; + } + else if (priv->use_ssl) { + ssl_teardown_connection(priv); + } } +out: return ret; } -int +static int __socket_server_bind (rpc_transport_t *this) { socket_private_t *priv = NULL; int ret = -1; - int opt = 1; + int opt = 1; + int reuse_check_sock = -1; + struct sockaddr_storage unix_addr = {0}; - priv = this->private; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + + priv = this->private; ret = setsockopt (priv->sock, SOL_SOCKET, SO_REUSEADDR, - &opt, sizeof (opt)); + &opt, sizeof (opt)); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, @@ -296,8 +679,23 @@ __socket_server_bind (rpc_transport_t *this) strerror (errno)); } + /* reuse-address doesn't work for unix type sockets */ + if (AF_UNIX == SA (&this->myinfo.sockaddr)->sa_family) { + memcpy (&unix_addr, SA (&this->myinfo.sockaddr), + this->myinfo.sockaddr_len); + reuse_check_sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (reuse_check_sock >= 0) { + ret = connect (reuse_check_sock, SA (&unix_addr), + this->myinfo.sockaddr_len); + if ((ret == -1) && (ECONNREFUSED == errno)) { + unlink (((struct sockaddr_un*)&unix_addr)->sun_path); + } + close (reuse_check_sock); + } + } + ret = bind (priv->sock, (struct sockaddr *)&this->myinfo.sockaddr, - this->myinfo.sockaddr_len); + this->myinfo.sockaddr_len); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, @@ -309,11 +707,12 @@ __socket_server_bind (rpc_transport_t *this) } } +out: return ret; } -int +static int __socket_nonblock (int fd) { int flags = 0; @@ -327,23 +726,83 @@ __socket_nonblock (int fd) return ret; } - -int +static int __socket_nodelay (int fd) { int on = 1; int ret = -1; ret = setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, - &on, sizeof (on)); + &on, sizeof (on)); if (!ret) - gf_log ("", GF_LOG_TRACE, + gf_log (THIS->name, GF_LOG_TRACE, "NODELAY enabled for socket %d", fd); return ret; } -int + +static int +__socket_keepalive (int fd, int family, int keepalive_intvl, int keepalive_idle) +{ + int on = 1; + int ret = -1; + + ret = setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)); + if (ret == -1) { + gf_log ("socket", GF_LOG_WARNING, + "failed to set keep alive option on socket %d", fd); + goto err; + } + + if (keepalive_intvl == GF_USE_DEFAULT_KEEPALIVE) + goto done; + +#if !defined(GF_LINUX_HOST_OS) && !defined(__NetBSD__) +#ifdef GF_SOLARIS_HOST_OS + ret = setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive_intvl, + sizeof (keepalive_intvl)); +#else + ret = setsockopt (fd, IPPROTO_TCP, TCP_KEEPALIVE, &keepalive_intvl, + sizeof (keepalive_intvl)); +#endif + if (ret == -1) { + gf_log ("socket", GF_LOG_WARNING, + "failed to set keep alive interval on socket %d", fd); + goto err; + } +#else + if (family != AF_INET) + goto done; + + ret = setsockopt (fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepalive_idle, + sizeof (keepalive_intvl)); + if (ret == -1) { + gf_log ("socket", GF_LOG_WARNING, + "failed to set keep idle %d on socket %d, %s", + keepalive_idle, fd, strerror(errno)); + goto err; + } + ret = setsockopt (fd, IPPROTO_TCP , TCP_KEEPINTVL, &keepalive_intvl, + sizeof (keepalive_intvl)); + if (ret == -1) { + gf_log ("socket", GF_LOG_WARNING, + "failed to set keep interval %d on socket %d, %s", + keepalive_intvl, fd, strerror(errno)); + goto err; + } +#endif + +done: + gf_log (THIS->name, GF_LOG_TRACE, "Keep-alive enabled for socket %d, interval " + "%d, idle: %d", fd, keepalive_intvl, keepalive_idle); + +err: + return ret; +} + + +static int __socket_connect_finish (int fd) { int ret = -1; @@ -361,11 +820,14 @@ __socket_connect_finish (int fd) } -void +static void __socket_reset (rpc_transport_t *this) { socket_private_t *priv = NULL; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + priv = this->private; /* TODO: use mem-pool on incoming data */ @@ -379,6 +841,8 @@ __socket_reset (rpc_transport_t *this) iobuf_unref (priv->incoming.iobuf); } + GF_FREE (priv->incoming.request_info); + memset (&priv->incoming, 0, sizeof (priv->incoming)); event_unregister (this->ctx->event_pool, priv->sock, priv->idx); @@ -387,14 +851,41 @@ __socket_reset (rpc_transport_t *this) priv->sock = -1; priv->idx = -1; priv->connected = -1; + +out: + return; +} + + +static void +socket_set_lastfrag (uint32_t *fragsize) { + (*fragsize) |= 0x80000000U; +} + + +static void +socket_set_frag_header_size (uint32_t size, char *haddr) +{ + size = htonl (size); + memcpy (haddr, &size, sizeof (size)); } -struct ioq * +static void +socket_set_last_frag_header_size (uint32_t size, char *haddr) +{ + socket_set_lastfrag (&size); + socket_set_frag_header_size (size, haddr); +} + +static struct ioq * __socket_ioq_new (rpc_transport_t *this, rpc_transport_msg_t *msg) { struct ioq *entry = NULL; int count = 0; + uint32_t size = 0; + + GF_VALIDATE_OR_GOTO ("socket", this, out); /* TODO: use mem-pool */ entry = GF_CALLOC (1, sizeof (*entry), gf_common_mt_ioq); @@ -403,10 +894,28 @@ __socket_ioq_new (rpc_transport_t *this, rpc_transport_msg_t *msg) count = msg->rpchdrcount + msg->proghdrcount + msg->progpayloadcount; - assert (count <= MAX_IOVEC); + GF_ASSERT (count <= (MAX_IOVEC - 1)); + + size = iov_length (msg->rpchdr, msg->rpchdrcount) + + iov_length (msg->proghdr, msg->proghdrcount) + + iov_length (msg->progpayload, msg->progpayloadcount); + + if (size > RPC_MAX_FRAGMENT_SIZE) { + gf_log (this->name, GF_LOG_ERROR, + "msg size (%u) bigger than the maximum allowed size on " + "sockets (%u)", size, RPC_MAX_FRAGMENT_SIZE); + GF_FREE (entry); + return NULL; + } + + socket_set_last_frag_header_size (size, (char *)&entry->fraghdr); + + entry->vector[0].iov_base = (char *)&entry->fraghdr; + entry->vector[0].iov_len = sizeof (entry->fraghdr); + entry->count = 1; if (msg->rpchdr != NULL) { - memcpy (&entry->vector[0], msg->rpchdr, + memcpy (&entry->vector[1], msg->rpchdr, sizeof (struct iovec) * msg->rpchdrcount); entry->count += msg->rpchdrcount; } @@ -431,28 +940,37 @@ __socket_ioq_new (rpc_transport_t *this, rpc_transport_msg_t *msg) INIT_LIST_HEAD (&entry->list); +out: return entry; } -void +static void __socket_ioq_entry_free (struct ioq *entry) { + GF_VALIDATE_OR_GOTO ("socket", entry, out); + list_del_init (&entry->list); if (entry->iobref) iobref_unref (entry->iobref); /* TODO: use mem-pool */ GF_FREE (entry); + +out: + return; } -void +static void __socket_ioq_flush (rpc_transport_t *this) { socket_private_t *priv = NULL; struct ioq *entry = NULL; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + priv = this->private; while (!list_empty (&priv->ioq)) { @@ -460,65 +978,87 @@ __socket_ioq_flush (rpc_transport_t *this) __socket_ioq_entry_free (entry); } +out: return; } -int -__socket_ioq_churn_entry (rpc_transport_t *this, struct ioq *entry) +static int +__socket_ioq_churn_entry (rpc_transport_t *this, struct ioq *entry, int direct) { - int ret = -1; + int ret = -1; + socket_private_t *priv = NULL; + char a_byte = 0; ret = __socket_writev (this, entry->pending_vector, - entry->pending_count, + entry->pending_count, &entry->pending_vector, - &entry->pending_count); + &entry->pending_count); if (ret == 0) { /* current entry was completely written */ - assert (entry->pending_count == 0); + GF_ASSERT (entry->pending_count == 0); __socket_ioq_entry_free (entry); + priv = this->private; + if (priv->own_thread) { + /* + * The pipe should only remain readable if there are + * more entries after this, so drain the byte + * representing this entry. + */ + if (!direct && read(priv->pipe[0],&a_byte,1) < 1) { + gf_log(this->name,GF_LOG_WARNING, + "read error on pipe"); + } + } } return ret; } -int +static int __socket_ioq_churn (rpc_transport_t *this) { socket_private_t *priv = NULL; int ret = 0; struct ioq *entry = NULL; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + priv = this->private; while (!list_empty (&priv->ioq)) { /* pick next entry */ entry = priv->ioq_next; - ret = __socket_ioq_churn_entry (this, entry); + ret = __socket_ioq_churn_entry (this, entry, 0); if (ret != 0) break; } - if (list_empty (&priv->ioq)) { + if (!priv->own_thread && list_empty (&priv->ioq)) { /* all pending writes done, not interested in POLLOUT */ priv->idx = event_select_on (this->ctx->event_pool, - priv->sock, priv->idx, -1, 0); + priv->sock, priv->idx, -1, 0); } +out: return ret; } -int +static int socket_event_poll_err (rpc_transport_t *this) { socket_private_t *priv = NULL; int ret = -1; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + priv = this->private; pthread_mutex_lock (&priv->lock); @@ -530,16 +1070,20 @@ socket_event_poll_err (rpc_transport_t *this) rpc_transport_notify (this, RPC_TRANSPORT_DISCONNECT, this); +out: return ret; } -int +static int socket_event_poll_out (rpc_transport_t *this) { socket_private_t *priv = NULL; int ret = -1; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + priv = this->private; pthread_mutex_lock (&priv->lock); @@ -556,57 +1100,63 @@ socket_event_poll_out (rpc_transport_t *this) ret = rpc_transport_notify (this, RPC_TRANSPORT_MSG_SENT, NULL); +out: return ret; } -inline int +static inline int __socket_read_simple_msg (rpc_transport_t *this) { - socket_private_t *priv = NULL; - int ret = 0; - uint32_t remaining_size = 0; - size_t bytes_read = 0; + int ret = 0; + uint32_t remaining_size = 0; + size_t bytes_read = 0; + socket_private_t *priv = NULL; + struct gf_sock_incoming *in = NULL; + struct gf_sock_incoming_frag *frag = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; - switch (priv->incoming.frag.simple_state) { + in = &priv->incoming; + frag = &in->frag; + + switch (frag->simple_state) { case SP_STATE_SIMPLE_MSG_INIT: - remaining_size = RPC_FRAGSIZE (priv->incoming.fraghdr) - - priv->incoming.frag.bytes_read; + remaining_size = RPC_FRAGSIZE (in->fraghdr) - frag->bytes_read; __socket_proto_init_pending (priv, remaining_size); - priv->incoming.frag.simple_state = - SP_STATE_READING_SIMPLE_MSG; + frag->simple_state = SP_STATE_READING_SIMPLE_MSG; /* fall through */ case SP_STATE_READING_SIMPLE_MSG: ret = 0; - remaining_size = RPC_FRAGSIZE (priv->incoming.fraghdr) - - priv->incoming.frag.bytes_read; + remaining_size = RPC_FRAGSIZE (in->fraghdr) - frag->bytes_read; if (remaining_size > 0) { ret = __socket_readv (this, - priv->incoming.pending_vector, 1, - &priv->incoming.pending_vector, - &priv->incoming.pending_count, + in->pending_vector, 1, + &in->pending_vector, + &in->pending_count, &bytes_read); } if (ret == -1) { - gf_log (this->name, GF_LOG_TRACE, + gf_log (this->name, GF_LOG_WARNING, "reading from socket failed. Error (%s), " "peer (%s)", strerror (errno), this->peerinfo.identifier); break; } - priv->incoming.frag.bytes_read += bytes_read; - priv->incoming.frag.fragcurrent += bytes_read; + frag->bytes_read += bytes_read; + frag->fragcurrent += bytes_read; if (ret > 0) { gf_log (this->name, GF_LOG_TRACE, @@ -615,16 +1165,16 @@ __socket_read_simple_msg (rpc_transport_t *this) } if (ret == 0) { - priv->incoming.frag.simple_state - = SP_STATE_SIMPLE_MSG_INIT; + frag->simple_state = SP_STATE_SIMPLE_MSG_INIT; } } +out: return ret; } -inline int +static inline int __socket_read_simple_request (rpc_transport_t *this) { return __socket_read_simple_msg (this); @@ -635,9 +1185,14 @@ __socket_read_simple_request (rpc_transport_t *this) #define rpc_verf_addr(fragcurrent) (fragcurrent - 4) +#define rpc_msgtype_addr(buf) (buf + 4) + +#define rpc_prognum_addr(buf) (buf + RPC_MSGTYPE_SIZE + 4) +#define rpc_progver_addr(buf) (buf + RPC_MSGTYPE_SIZE + 8) +#define rpc_procnum_addr(buf) (buf + RPC_MSGTYPE_SIZE + 12) -inline int -__socket_read_vectored_request (rpc_transport_t *this) +static inline int +__socket_read_vectored_request (rpc_transport_t *this, rpcsvc_vector_sizer vector_sizer) { socket_private_t *priv = NULL; int ret = 0; @@ -645,13 +1200,27 @@ __socket_read_vectored_request (rpc_transport_t *this) char *addr = NULL; struct iobuf *iobuf = NULL; uint32_t remaining_size = 0; - uint32_t gluster_write_proc_len = 0; + ssize_t readsize = 0; + size_t size = 0; + struct gf_sock_incoming *in = NULL; + struct gf_sock_incoming_frag *frag = NULL; + sp_rpcfrag_request_state_t *request = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; - switch (priv->incoming.frag.call_body.request.vector_state) { + /* used to reduce the indirection */ + in = &priv->incoming; + frag = &in->frag; + request = &frag->call_body.request; + + switch (request->vector_state) { case SP_STATE_VECTORED_REQUEST_INIT: - addr = rpc_cred_addr (iobuf_ptr (priv->incoming.iobuf)); + request->vector_sizer_state = 0; + + addr = rpc_cred_addr (iobuf_ptr (in->iobuf)); /* also read verf flavour and verflen */ credlen = ntoh32 (*((uint32_t *)addr)) @@ -659,80 +1228,114 @@ __socket_read_vectored_request (rpc_transport_t *this) __socket_proto_init_pending (priv, credlen); - priv->incoming.frag.call_body.request.vector_state = - SP_STATE_READING_CREDBYTES; + request->vector_state = SP_STATE_READING_CREDBYTES; /* fall through */ case SP_STATE_READING_CREDBYTES: __socket_proto_read (priv, ret); - priv->incoming.frag.call_body.request.vector_state = - SP_STATE_READ_CREDBYTES; + request->vector_state = SP_STATE_READ_CREDBYTES; /* fall through */ case SP_STATE_READ_CREDBYTES: - addr = rpc_verf_addr (priv->incoming.frag.fragcurrent); - - /* FIXME: Also handle procedures other than glusterfs-write - * here - */ - /* also read proc-header */ - gluster_write_proc_len = sizeof (gfs3_write_req); - - verflen = ntoh32 (*((uint32_t *)addr)) - + gluster_write_proc_len; + addr = rpc_verf_addr (frag->fragcurrent); + verflen = ntoh32 (*((uint32_t *)addr)); + if (verflen == 0) { + request->vector_state = SP_STATE_READ_VERFBYTES; + goto sp_state_read_verfbytes; + } __socket_proto_init_pending (priv, verflen); - priv->incoming.frag.call_body.request.vector_state - = SP_STATE_READING_VERFBYTES; + request->vector_state = SP_STATE_READING_VERFBYTES; /* fall through */ case SP_STATE_READING_VERFBYTES: __socket_proto_read (priv, ret); - priv->incoming.frag.call_body.request.vector_state = - SP_STATE_READ_VERFBYTES; + request->vector_state = SP_STATE_READ_VERFBYTES; /* fall through */ case SP_STATE_READ_VERFBYTES: - if (priv->incoming.payload_vector.iov_base == NULL) { - iobuf = iobuf_get (this->ctx->iobuf_pool); +sp_state_read_verfbytes: + /* set the base_addr 'persistently' across multiple calls + into the state machine */ + in->proghdr_base_addr = frag->fragcurrent; + + request->vector_sizer_state = + vector_sizer (request->vector_sizer_state, + &readsize, in->proghdr_base_addr, + frag->fragcurrent); + __socket_proto_init_pending (priv, readsize); + + request->vector_state = SP_STATE_READING_PROGHDR; + + /* fall through */ + + case SP_STATE_READING_PROGHDR: + __socket_proto_read (priv, ret); + + request->vector_state = SP_STATE_READ_PROGHDR; + + /* fall through */ + + case SP_STATE_READ_PROGHDR: +sp_state_read_proghdr: + request->vector_sizer_state = + vector_sizer (request->vector_sizer_state, + &readsize, in->proghdr_base_addr, + frag->fragcurrent); + if (readsize == 0) { + request->vector_state = SP_STATE_READ_PROGHDR_XDATA; + goto sp_state_read_proghdr_xdata; + } + + __socket_proto_init_pending (priv, readsize); + + request->vector_state = SP_STATE_READING_PROGHDR_XDATA; + + /* fall through */ + + case SP_STATE_READING_PROGHDR_XDATA: + __socket_proto_read (priv, ret); + + request->vector_state = SP_STATE_READ_PROGHDR; + /* check if the vector_sizer() has more to say */ + goto sp_state_read_proghdr; + + case SP_STATE_READ_PROGHDR_XDATA: +sp_state_read_proghdr_xdata: + if (in->payload_vector.iov_base == NULL) { + + size = RPC_FRAGSIZE (in->fraghdr) - frag->bytes_read; + iobuf = iobuf_get2 (this->ctx->iobuf_pool, size); if (!iobuf) { - gf_log (this->name, GF_LOG_ERROR, - "unable to allocate IO buffer " - "for peer %s", - this->peerinfo.identifier); ret = -1; break; } - if (priv->incoming.iobref == NULL) { - priv->incoming.iobref = iobref_new (); - if (priv->incoming.iobref == NULL) { - gf_log (this->name, GF_LOG_ERROR, - "out of memory"); + if (in->iobref == NULL) { + in->iobref = iobref_new (); + if (in->iobref == NULL) { ret = -1; iobuf_unref (iobuf); break; } } - iobref_add (priv->incoming.iobref, iobuf); + iobref_add (in->iobref, iobuf); iobuf_unref (iobuf); - priv->incoming.payload_vector.iov_base - = iobuf_ptr (iobuf); + in->payload_vector.iov_base = iobuf_ptr (iobuf); - priv->incoming.frag.fragcurrent = iobuf_ptr (iobuf); + frag->fragcurrent = iobuf_ptr (iobuf); } - priv->incoming.frag.call_body.request.vector_state = - SP_STATE_READING_PROG; + request->vector_state = SP_STATE_READING_PROG; /* fall through */ @@ -743,114 +1346,138 @@ __socket_read_vectored_request (rpc_transport_t *this) ret = __socket_read_simple_msg (this); - remaining_size = RPC_FRAGSIZE (priv->incoming.fraghdr) - - priv->incoming.frag.bytes_read; + remaining_size = RPC_FRAGSIZE (in->fraghdr) - frag->bytes_read; - if ((ret == -1) - || ((ret == 0) - && (remaining_size == 0) - && RPC_LASTFRAG (priv->incoming.fraghdr))) { - priv->incoming.frag.call_body.request.vector_state - = SP_STATE_VECTORED_REQUEST_INIT; - priv->incoming.payload_vector.iov_len - = (unsigned long)priv->incoming.frag.fragcurrent - - (unsigned long) - priv->incoming.payload_vector.iov_base; + if ((ret == -1) || + ((ret == 0) && (remaining_size == 0) + && RPC_LASTFRAG (in->fraghdr))) { + request->vector_state = SP_STATE_VECTORED_REQUEST_INIT; + in->payload_vector.iov_len + = ((unsigned long)frag->fragcurrent + - (unsigned long)in->payload_vector.iov_base); } break; } +out: return ret; } - -#define rpc_msgtype_addr(buf) (buf + 4) - -#define rpc_prognum_addr(buf) (buf + RPC_MSGTYPE_SIZE + 4) - -#define rpc_procnum_addr(buf) (buf + RPC_MSGTYPE_SIZE + 12) - - -inline int +static inline int __socket_read_request (rpc_transport_t *this) { socket_private_t *priv = NULL; - uint32_t prognum = 0, procnum = 0; + uint32_t prognum = 0, procnum = 0, progver = 0; uint32_t remaining_size = 0; int ret = -1; char *buf = NULL; + rpcsvc_vector_sizer vector_sizer = NULL; + struct gf_sock_incoming *in = NULL; + struct gf_sock_incoming_frag *frag = NULL; + sp_rpcfrag_request_state_t *request = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; - switch (priv->incoming.frag.call_body.request.header_state) { + /* used to reduce the indirection */ + in = &priv->incoming; + frag = &in->frag; + request = &frag->call_body.request; + + switch (request->header_state) { case SP_STATE_REQUEST_HEADER_INIT: __socket_proto_init_pending (priv, RPC_CALL_BODY_SIZE); - priv->incoming.frag.call_body.request.header_state - = SP_STATE_READING_RPCHDR1; + request->header_state = SP_STATE_READING_RPCHDR1; /* fall through */ case SP_STATE_READING_RPCHDR1: __socket_proto_read (priv, ret); - priv->incoming.frag.call_body.request.header_state = - SP_STATE_READ_RPCHDR1; + request->header_state = SP_STATE_READ_RPCHDR1; /* fall through */ case SP_STATE_READ_RPCHDR1: - buf = rpc_prognum_addr (iobuf_ptr (priv->incoming.iobuf)); + buf = rpc_prognum_addr (iobuf_ptr (in->iobuf)); prognum = ntoh32 (*((uint32_t *)buf)); - buf = rpc_procnum_addr (iobuf_ptr (priv->incoming.iobuf)); + buf = rpc_progver_addr (iobuf_ptr (in->iobuf)); + progver = ntoh32 (*((uint32_t *)buf)); + + buf = rpc_procnum_addr (iobuf_ptr (in->iobuf)); procnum = ntoh32 (*((uint32_t *)buf)); - if ((prognum == GLUSTER3_1_FOP_PROGRAM) - && (procnum == GF_FOP_WRITE)) { - ret = __socket_read_vectored_request (this); + if (priv->is_server) { + /* this check is needed as rpcsvc and rpc-clnt + * actor structures are not same */ + vector_sizer = + rpcsvc_get_program_vector_sizer ((rpcsvc_t *)this->mydata, + prognum, progver, procnum); + } + + if (vector_sizer) { + ret = __socket_read_vectored_request (this, vector_sizer); } else { ret = __socket_read_simple_request (this); } - remaining_size = RPC_FRAGSIZE (priv->incoming.fraghdr) - - priv->incoming.frag.bytes_read; + remaining_size = RPC_FRAGSIZE (in->fraghdr) - frag->bytes_read; if ((ret == -1) || ((ret == 0) && (remaining_size == 0) - && (RPC_LASTFRAG (priv->incoming.fraghdr)))) { - priv->incoming.frag.call_body.request.header_state = - SP_STATE_REQUEST_HEADER_INIT; + && (RPC_LASTFRAG (in->fraghdr)))) { + request->header_state = SP_STATE_REQUEST_HEADER_INIT; } break; } +out: return ret; } -inline int +static inline int __socket_read_accepted_successful_reply (rpc_transport_t *this) { - socket_private_t *priv = NULL; - int ret = 0; - struct iobuf *iobuf = NULL; - uint32_t gluster_read_rsp_hdr_len = 0; + socket_private_t *priv = NULL; + int ret = 0; + struct iobuf *iobuf = NULL; + gfs3_read_rsp read_rsp = {0, }; + ssize_t size = 0; + ssize_t default_read_size = 0; + char *proghdr_buf = NULL; + XDR xdr; + struct gf_sock_incoming *in = NULL; + struct gf_sock_incoming_frag *frag = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; - switch (priv->incoming.frag.call_body.reply.accepted_success_state) { + /* used to reduce the indirection */ + in = &priv->incoming; + frag = &in->frag; + + switch (frag->call_body.reply.accepted_success_state) { case SP_STATE_ACCEPTED_SUCCESS_REPLY_INIT: - gluster_read_rsp_hdr_len = sizeof (gfs3_read_rsp); + default_read_size = xdr_sizeof ((xdrproc_t) xdr_gfs3_read_rsp, + &read_rsp); - __socket_proto_init_pending (priv, gluster_read_rsp_hdr_len); + proghdr_buf = frag->fragcurrent; - priv->incoming.frag.call_body.reply.accepted_success_state + __socket_proto_init_pending (priv, default_read_size); + + frag->call_body.reply.accepted_success_state = SP_STATE_READING_PROC_HEADER; /* fall through */ @@ -858,44 +1485,79 @@ __socket_read_accepted_successful_reply (rpc_transport_t *this) case SP_STATE_READING_PROC_HEADER: __socket_proto_read (priv, ret); - priv->incoming.frag.call_body.reply.accepted_success_state - = SP_STATE_READ_PROC_HEADER; + /* there can be 'xdata' in read response, figure it out */ + xdrmem_create (&xdr, proghdr_buf, default_read_size, + XDR_DECODE); - /* fall through */ + /* This will fail if there is xdata sent from server, if not, + well and good, we don't need to worry about */ + xdr_gfs3_read_rsp (&xdr, &read_rsp); - case SP_STATE_READ_PROC_HEADER: - if (priv->incoming.payload_vector.iov_base == NULL) { - iobuf = iobuf_get (this->ctx->iobuf_pool); + free (read_rsp.xdata.xdata_val); + + /* need to round off to proper roof (%4), as XDR packing pads + the end of opaque object with '0' */ + size = roof (read_rsp.xdata.xdata_len, 4); + + if (!size) { + frag->call_body.reply.accepted_success_state + = SP_STATE_READ_PROC_OPAQUE; + goto read_proc_opaque; + } + + __socket_proto_init_pending (priv, size); + + frag->call_body.reply.accepted_success_state + = SP_STATE_READING_PROC_OPAQUE; + + case SP_STATE_READING_PROC_OPAQUE: + __socket_proto_read (priv, ret); + + frag->call_body.reply.accepted_success_state + = SP_STATE_READ_PROC_OPAQUE; + + case SP_STATE_READ_PROC_OPAQUE: + read_proc_opaque: + if (in->payload_vector.iov_base == NULL) { + + size = (RPC_FRAGSIZE (in->fraghdr) - frag->bytes_read); + + iobuf = iobuf_get2 (this->ctx->iobuf_pool, size); if (iobuf == NULL) { ret = -1; goto out; } - if (priv->incoming.iobref == NULL) { - priv->incoming.iobref = iobref_new (); - if (priv->incoming.iobref == NULL) { + if (in->iobref == NULL) { + in->iobref = iobref_new (); + if (in->iobref == NULL) { ret = -1; iobuf_unref (iobuf); goto out; } } - iobref_add (priv->incoming.iobref, iobuf); + iobref_add (in->iobref, iobuf); iobuf_unref (iobuf); - priv->incoming.payload_vector.iov_base - = iobuf_ptr (iobuf); + in->payload_vector.iov_base = iobuf_ptr (iobuf); + + in->payload_vector.iov_len = size; } - priv->incoming.frag.fragcurrent - = priv->incoming.payload_vector.iov_base; + frag->fragcurrent = in->payload_vector.iov_base; + frag->call_body.reply.accepted_success_state + = SP_STATE_READ_PROC_HEADER; + + /* fall through */ + + case SP_STATE_READ_PROC_HEADER: /* now read the entire remaining msg into new iobuf */ ret = __socket_read_simple_msg (this); if ((ret == -1) - || ((ret == 0) - && RPC_LASTFRAG (priv->incoming.fraghdr))) { - priv->incoming.frag.call_body.reply.accepted_success_state + || ((ret == 0) && RPC_LASTFRAG (in->fraghdr))) { + frag->call_body.reply.accepted_success_state = SP_STATE_ACCEPTED_SUCCESS_REPLY_INIT; } @@ -909,7 +1571,7 @@ out: #define rpc_reply_verflen_addr(fragcurrent) ((char *)fragcurrent - 4) #define rpc_reply_accept_status_addr(fragcurrent) ((char *)fragcurrent - 4) -inline int +static inline int __socket_read_accepted_reply (rpc_transport_t *this) { socket_private_t *priv = NULL; @@ -917,16 +1579,24 @@ __socket_read_accepted_reply (rpc_transport_t *this) char *buf = NULL; uint32_t verflen = 0, len = 0; uint32_t remaining_size = 0; + struct gf_sock_incoming *in = NULL; + struct gf_sock_incoming_frag *frag = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; + /* used to reduce the indirection */ + in = &priv->incoming; + frag = &in->frag; - switch (priv->incoming.frag.call_body.reply.accepted_state) { + switch (frag->call_body.reply.accepted_state) { case SP_STATE_ACCEPTED_REPLY_INIT: __socket_proto_init_pending (priv, RPC_AUTH_FLAVOUR_N_LENGTH_SIZE); - priv->incoming.frag.call_body.reply.accepted_state + frag->call_body.reply.accepted_state = SP_STATE_READING_REPLY_VERFLEN; /* fall through */ @@ -934,13 +1604,13 @@ __socket_read_accepted_reply (rpc_transport_t *this) case SP_STATE_READING_REPLY_VERFLEN: __socket_proto_read (priv, ret); - priv->incoming.frag.call_body.reply.accepted_state + frag->call_body.reply.accepted_state = SP_STATE_READ_REPLY_VERFLEN; /* fall through */ case SP_STATE_READ_REPLY_VERFLEN: - buf = rpc_reply_verflen_addr (priv->incoming.frag.fragcurrent); + buf = rpc_reply_verflen_addr (frag->fragcurrent); verflen = ntoh32 (*((uint32_t *) buf)); @@ -949,7 +1619,7 @@ __socket_read_accepted_reply (rpc_transport_t *this) __socket_proto_init_pending (priv, len); - priv->incoming.frag.call_body.reply.accepted_state + frag->call_body.reply.accepted_state = SP_STATE_READING_REPLY_VERFBYTES; /* fall through */ @@ -957,19 +1627,19 @@ __socket_read_accepted_reply (rpc_transport_t *this) case SP_STATE_READING_REPLY_VERFBYTES: __socket_proto_read (priv, ret); - priv->incoming.frag.call_body.reply.accepted_state + frag->call_body.reply.accepted_state = SP_STATE_READ_REPLY_VERFBYTES; - buf = rpc_reply_accept_status_addr (priv->incoming.frag.fragcurrent); + buf = rpc_reply_accept_status_addr (frag->fragcurrent); - priv->incoming.frag.call_body.reply.accept_status + frag->call_body.reply.accept_status = ntoh32 (*(uint32_t *) buf); /* fall through */ case SP_STATE_READ_REPLY_VERFBYTES: - if (priv->incoming.frag.call_body.reply.accept_status + if (frag->call_body.reply.accept_status == SUCCESS) { ret = __socket_read_accepted_successful_reply (this); } else { @@ -979,25 +1649,25 @@ __socket_read_accepted_reply (rpc_transport_t *this) ret = __socket_read_simple_msg (this); } - remaining_size = RPC_FRAGSIZE (priv->incoming.fraghdr) - - priv->incoming.frag.bytes_read; + remaining_size = RPC_FRAGSIZE (in->fraghdr) + - frag->bytes_read; if ((ret == -1) - || ((ret == 0) - && (remaining_size == 0) - && (RPC_LASTFRAG (priv->incoming.fraghdr)))) { - priv->incoming.frag.call_body.reply.accepted_state + || ((ret == 0) && (remaining_size == 0) + && (RPC_LASTFRAG (in->fraghdr)))) { + frag->call_body.reply.accepted_state = SP_STATE_ACCEPTED_REPLY_INIT; } break; } +out: return ret; } -inline int +static inline int __socket_read_denied_reply (rpc_transport_t *this) { return __socket_read_simple_msg (this); @@ -1007,22 +1677,29 @@ __socket_read_denied_reply (rpc_transport_t *this) #define rpc_reply_status_addr(fragcurrent) ((char *)fragcurrent - 4) -inline int +static inline int __socket_read_vectored_reply (rpc_transport_t *this) { socket_private_t *priv = NULL; int ret = 0; char *buf = NULL; uint32_t remaining_size = 0; + struct gf_sock_incoming *in = NULL; + struct gf_sock_incoming_frag *frag = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; + in = &priv->incoming; + frag = &in->frag; - switch (priv->incoming.frag.call_body.reply.status_state) { + switch (frag->call_body.reply.status_state) { case SP_STATE_ACCEPTED_REPLY_INIT: __socket_proto_init_pending (priv, RPC_REPLY_STATUS_SIZE); - priv->incoming.frag.call_body.reply.status_state + frag->call_body.reply.status_state = SP_STATE_READING_REPLY_STATUS; /* fall through */ @@ -1030,46 +1707,43 @@ __socket_read_vectored_reply (rpc_transport_t *this) case SP_STATE_READING_REPLY_STATUS: __socket_proto_read (priv, ret); - buf = rpc_reply_status_addr (priv->incoming.frag.fragcurrent); + buf = rpc_reply_status_addr (frag->fragcurrent); - priv->incoming.frag.call_body.reply.accept_status + frag->call_body.reply.accept_status = ntoh32 (*((uint32_t *) buf)); - priv->incoming.frag.call_body.reply.status_state + frag->call_body.reply.status_state = SP_STATE_READ_REPLY_STATUS; /* fall through */ case SP_STATE_READ_REPLY_STATUS: - if (priv->incoming.frag.call_body.reply.accept_status - == MSG_ACCEPTED) { + if (frag->call_body.reply.accept_status == MSG_ACCEPTED) { ret = __socket_read_accepted_reply (this); } else { ret = __socket_read_denied_reply (this); } - remaining_size = RPC_FRAGSIZE (priv->incoming.fraghdr) - - priv->incoming.frag.bytes_read; + remaining_size = RPC_FRAGSIZE (in->fraghdr) - frag->bytes_read; if ((ret == -1) - || ((ret == 0) - && (remaining_size == 0) - && (RPC_LASTFRAG (priv->incoming.fraghdr)))) { - priv->incoming.frag.call_body.reply.status_state + || ((ret == 0) && (remaining_size == 0) + && (RPC_LASTFRAG (in->fraghdr)))) { + frag->call_body.reply.status_state = SP_STATE_ACCEPTED_REPLY_INIT; - priv->incoming.payload_vector.iov_len - = (unsigned long)priv->incoming.frag.fragcurrent - - (unsigned long) - priv->incoming.payload_vector.iov_base; + in->payload_vector.iov_len + = (unsigned long)frag->fragcurrent + - (unsigned long)in->payload_vector.iov_base; } break; } +out: return ret; } -inline int +static inline int __socket_read_simple_reply (rpc_transport_t *this) { return __socket_read_simple_msg (this); @@ -1077,49 +1751,64 @@ __socket_read_simple_reply (rpc_transport_t *this) #define rpc_xid_addr(buf) (buf) -inline int +static inline int __socket_read_reply (rpc_transport_t *this) { socket_private_t *priv = NULL; char *buf = NULL; int32_t ret = -1; rpc_request_info_t *request_info = NULL; + char map_xid = 0; + struct gf_sock_incoming *in = NULL; + struct gf_sock_incoming_frag *frag = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; + in = &priv->incoming; + frag = &in->frag; - buf = rpc_xid_addr (iobuf_ptr (priv->incoming.iobuf)); + buf = rpc_xid_addr (iobuf_ptr (in->iobuf)); - request_info = GF_CALLOC (1, sizeof (*request_info), gf_common_mt_rpc_trans_reqinfo_t); - if (request_info == NULL) { - gf_log (this->name, GF_LOG_ERROR, "out of memory"); - goto out; + if (in->request_info == NULL) { + in->request_info = GF_CALLOC (1, sizeof (*request_info), + gf_common_mt_rpc_trans_reqinfo_t); + if (in->request_info == NULL) { + goto out; + } + + map_xid = 1; } - priv->incoming.request_info = request_info; + request_info = in->request_info; - request_info->xid = ntoh32 (*((uint32_t *) buf)); + if (map_xid) { + request_info->xid = ntoh32 (*((uint32_t *) buf)); - /* release priv->lock, so as to avoid deadlock b/w conn->lock and - * priv->lock, since we are doing an upcall here. - */ - pthread_mutex_unlock (&priv->lock); - { - ret = rpc_transport_notify (this, RPC_TRANSPORT_MAP_XID_REQUEST, - priv->incoming.request_info); - } - pthread_mutex_lock (&priv->lock); + /* release priv->lock, so as to avoid deadlock b/w conn->lock + * and priv->lock, since we are doing an upcall here. + */ + pthread_mutex_unlock (&priv->lock); + { + ret = rpc_transport_notify (this, + RPC_TRANSPORT_MAP_XID_REQUEST, + in->request_info); + } + pthread_mutex_lock (&priv->lock); - if (ret == -1) { - goto out; + if (ret == -1) { + gf_log (this->name, GF_LOG_WARNING, + "notify for event MAP_XID failed"); + goto out; + } } - if ((request_info->prognum == GLUSTER3_1_FOP_PROGRAM) + if ((request_info->prognum == GLUSTER_FOP_PROGRAM) && (request_info->procnum == GF_FOP_READ)) { - if (request_info->rsp.rsp_payload_count != 0) { - priv->incoming.iobref - = iobref_ref (request_info->rsp.rsp_iobref); - priv->incoming.payload_vector - = *request_info->rsp.rsp_payload; + if (map_xid && request_info->rsp.rsp_payload_count != 0) { + in->iobref = iobref_ref (request_info->rsp.rsp_iobref); + in->payload_vector = *request_info->rsp.rsp_payload; } ret = __socket_read_vectored_reply (this); @@ -1132,82 +1821,105 @@ out: /* returns the number of bytes yet to be read in a fragment */ -inline int +static inline int __socket_read_frag (rpc_transport_t *this) { socket_private_t *priv = NULL; int32_t ret = 0; char *buf = NULL; uint32_t remaining_size = 0; + struct gf_sock_incoming *in = NULL; + struct gf_sock_incoming_frag *frag = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; + /* used to reduce the indirection */ + in = &priv->incoming; + frag = &in->frag; - switch (priv->incoming.frag.state) { + switch (frag->state) { case SP_STATE_NADA: __socket_proto_init_pending (priv, RPC_MSGTYPE_SIZE); - priv->incoming.frag.state = SP_STATE_READING_MSGTYPE; + frag->state = SP_STATE_READING_MSGTYPE; /* fall through */ case SP_STATE_READING_MSGTYPE: __socket_proto_read (priv, ret); - priv->incoming.frag.state = SP_STATE_READ_MSGTYPE; + frag->state = SP_STATE_READ_MSGTYPE; /* fall through */ case SP_STATE_READ_MSGTYPE: - buf = rpc_msgtype_addr (iobuf_ptr (priv->incoming.iobuf)); - priv->incoming.msg_type = ntoh32 (*((uint32_t *)buf)); + buf = rpc_msgtype_addr (iobuf_ptr (in->iobuf)); + in->msg_type = ntoh32 (*((uint32_t *)buf)); - if (priv->incoming.msg_type == CALL) { + if (in->msg_type == CALL) { ret = __socket_read_request (this); - } else if (priv->incoming.msg_type == REPLY) { + } else if (in->msg_type == REPLY) { ret = __socket_read_reply (this); + } else if (in->msg_type == GF_UNIVERSAL_ANSWER) { + gf_log ("rpc", GF_LOG_ERROR, + "older version of protocol/process trying to " + "connect from %s. use newer version on that node", + this->peerinfo.identifier); } else { gf_log ("rpc", GF_LOG_ERROR, - "wrong MSG-TYPE (%d) received", - priv->incoming.msg_type); + "wrong MSG-TYPE (%d) received from %s", + in->msg_type, + this->peerinfo.identifier); ret = -1; } - remaining_size = RPC_FRAGSIZE (priv->incoming.fraghdr) - - priv->incoming.frag.bytes_read; + remaining_size = RPC_FRAGSIZE (in->fraghdr) - frag->bytes_read; if ((ret == -1) - || ((ret == 0) - && (remaining_size == 0) - && (RPC_LASTFRAG (priv->incoming.fraghdr)))) { - priv->incoming.frag.state = SP_STATE_NADA; + || ((ret == 0) && (remaining_size == 0) + && (RPC_LASTFRAG (in->fraghdr)))) { + frag->state = SP_STATE_NADA; } break; } +out: return ret; } -inline +static inline void __socket_reset_priv (socket_private_t *priv) { - if (priv->incoming.iobref) { - iobref_unref (priv->incoming.iobref); - priv->incoming.iobref = NULL; + struct gf_sock_incoming *in = NULL; + + /* used to reduce the indirection */ + in = &priv->incoming; + + if (in->iobref) { + iobref_unref (in->iobref); + in->iobref = NULL; } - if (priv->incoming.iobuf) { - iobuf_unref (priv->incoming.iobuf); + if (in->iobuf) { + iobuf_unref (in->iobuf); } - memset (&priv->incoming.payload_vector, 0, - sizeof (priv->incoming.payload_vector)); + if (in->request_info != NULL) { + GF_FREE (in->request_info); + in->request_info = NULL; + } + + memset (&in->payload_vector, 0, + sizeof (in->payload_vector)); - priv->incoming.iobuf = NULL; + in->iobuf = NULL; } -int +static int __socket_proto_state_machine (rpc_transport_t *this, rpc_transport_pollin_t **pollin) { @@ -1216,52 +1928,40 @@ __socket_proto_state_machine (rpc_transport_t *this, struct iobuf *iobuf = NULL; struct iobref *iobref = NULL; struct iovec vector[2]; + struct gf_sock_incoming *in = NULL; + struct gf_sock_incoming_frag *frag = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; - while (priv->incoming.record_state != SP_STATE_COMPLETE) { - switch (priv->incoming.record_state) { + /* used to reduce the indirection */ + in = &priv->incoming; + frag = &in->frag; - case SP_STATE_NADA: - iobuf = iobuf_get (this->ctx->iobuf_pool); - if (!iobuf) { - gf_log (this->name, GF_LOG_ERROR, - "unable to allocate IO buffer " - "for peer %s", - this->peerinfo.identifier); - ret = -ENOMEM; - goto out; - } + while (in->record_state != SP_STATE_COMPLETE) { + switch (in->record_state) { - priv->incoming.iobuf = iobuf; - priv->incoming.iobuf_size = 0; - priv->incoming.total_bytes_read = 0; - priv->incoming.payload_vector.iov_len = 0; + case SP_STATE_NADA: + in->total_bytes_read = 0; + in->payload_vector.iov_len = 0; - priv->incoming.pending_vector = priv->incoming.vector; - priv->incoming.pending_vector->iov_base = - &priv->incoming.fraghdr; + in->pending_vector = in->vector; + in->pending_vector->iov_base = &in->fraghdr; - priv->incoming.frag.fragcurrent = iobuf_ptr (iobuf); - priv->incoming.pending_vector->iov_len = - sizeof (priv->incoming.fraghdr); + in->pending_vector->iov_len = sizeof (in->fraghdr); - priv->incoming.record_state = SP_STATE_READING_FRAGHDR; + in->record_state = SP_STATE_READING_FRAGHDR; /* fall through */ case SP_STATE_READING_FRAGHDR: - ret = __socket_readv (this, - priv->incoming.pending_vector, 1, - &priv->incoming.pending_vector, - &priv->incoming.pending_count, + ret = __socket_readv (this, in->pending_vector, 1, + &in->pending_vector, + &in->pending_count, NULL); - if (ret == -1) { - gf_log (this->name, GF_LOG_TRACE, - "reading from socket failed. Error (%s), " - "peer (%s)", strerror (errno), - this->peerinfo.identifier); + if (ret == -1) goto out; - } if (ret > 0) { gf_log (this->name, GF_LOG_TRACE, "partial " @@ -1270,33 +1970,40 @@ __socket_proto_state_machine (rpc_transport_t *this, } if (ret == 0) { - priv->incoming.record_state = - SP_STATE_READ_FRAGHDR; + in->record_state = SP_STATE_READ_FRAGHDR; } /* fall through */ case SP_STATE_READ_FRAGHDR: - priv->incoming.fraghdr = ntoh32 (priv->incoming.fraghdr); - priv->incoming.record_state = SP_STATE_READING_FRAG; - priv->incoming.total_bytes_read - += RPC_FRAGSIZE(priv->incoming.fraghdr); + in->fraghdr = ntoh32 (in->fraghdr); + in->total_bytes_read += RPC_FRAGSIZE(in->fraghdr); + iobuf = iobuf_get2 (this->ctx->iobuf_pool, + (in->total_bytes_read + + sizeof (in->fraghdr))); + if (!iobuf) { + ret = -ENOMEM; + goto out; + } + + in->iobuf = iobuf; + in->iobuf_size = 0; + frag->fragcurrent = iobuf_ptr (iobuf); + in->record_state = SP_STATE_READING_FRAG; /* fall through */ case SP_STATE_READING_FRAG: ret = __socket_read_frag (this); - if ((ret == -1) - || (priv->incoming.frag.bytes_read != - RPC_FRAGSIZE (priv->incoming.fraghdr))) { + if ((ret == -1) || + (frag->bytes_read != RPC_FRAGSIZE (in->fraghdr))) { goto out; } - priv->incoming.frag.bytes_read = 0; + frag->bytes_read = 0; - if (!RPC_LASTFRAG (priv->incoming.fraghdr)) { - priv->incoming.record_state = - SP_STATE_READING_FRAGHDR; + if (!RPC_LASTFRAG (in->fraghdr)) { + in->record_state = SP_STATE_READING_FRAGHDR; break; } @@ -1305,70 +2012,65 @@ __socket_proto_state_machine (rpc_transport_t *this, */ if (pollin != NULL) { int count = 0; - priv->incoming.iobuf_size - = priv->incoming.total_bytes_read - - priv->incoming.payload_vector.iov_len; + in->iobuf_size = (in->total_bytes_read - + in->payload_vector.iov_len); memset (vector, 0, sizeof (vector)); - if (priv->incoming.iobref == NULL) { - priv->incoming.iobref = iobref_new (); - if (priv->incoming.iobref == NULL) { - gf_log (this->name, - GF_LOG_ERROR, - "out of memory"); + if (in->iobref == NULL) { + in->iobref = iobref_new (); + if (in->iobref == NULL) { ret = -1; goto out; } } - vector[count].iov_base - = iobuf_ptr (priv->incoming.iobuf); - vector[count].iov_len - = priv->incoming.iobuf_size; - - iobref = priv->incoming.iobref; + vector[count].iov_base = iobuf_ptr (in->iobuf); + vector[count].iov_len = in->iobuf_size; - iobref_add (iobref, - priv->incoming.iobuf); - iobuf_unref (priv->incoming.iobuf); - priv->incoming.iobuf = NULL; + iobref = in->iobref; count++; - if (priv->incoming.payload_vector.iov_base - != NULL) { - vector[count] - = priv->incoming.payload_vector; + if (in->payload_vector.iov_base != NULL) { + vector[count] = in->payload_vector; count++; } *pollin = rpc_transport_pollin_alloc (this, vector, count, + in->iobuf, iobref, - priv->incoming.request_info); + in->request_info); + iobuf_unref (in->iobuf); + in->iobuf = NULL; + if (*pollin == NULL) { + gf_log (this->name, GF_LOG_WARNING, + "transport pollin allocation failed"); ret = -1; goto out; } + if (in->msg_type == REPLY) + (*pollin)->is_reply = 1; - priv->incoming.request_info = NULL; + in->request_info = NULL; } - priv->incoming.record_state = SP_STATE_COMPLETE; + in->record_state = SP_STATE_COMPLETE; break; case SP_STATE_COMPLETE: /* control should not reach here */ - gf_log (this->name, GF_LOG_DEBUG, "control reached to " + gf_log (this->name, GF_LOG_WARNING, "control reached to " "SP_STATE_COMPLETE, which should not have " "happened"); break; } } - if (priv->incoming.record_state == SP_STATE_COMPLETE) { - priv->incoming.record_state = SP_STATE_NADA; + if (in->record_state == SP_STATE_COMPLETE) { + in->record_state = SP_STATE_NADA; __socket_reset_priv (priv); } @@ -1380,37 +2082,45 @@ out: } -int +static int socket_proto_state_machine (rpc_transport_t *this, rpc_transport_pollin_t **pollin) { socket_private_t *priv = NULL; - int ret = 0; + int ret = 0; - priv = this->private; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); - pthread_mutex_lock (&priv->lock); - { - ret = __socket_proto_state_machine (this, pollin); - } + priv = this->private; + + pthread_mutex_lock (&priv->lock); + { + ret = __socket_proto_state_machine (this, pollin); + } pthread_mutex_unlock (&priv->lock); - return ret; +out: + return ret; } -int +static int socket_event_poll_in (rpc_transport_t *this) { int ret = -1; rpc_transport_pollin_t *pollin = NULL; + socket_private_t *priv = this->private; ret = socket_proto_state_machine (this, &pollin); if (pollin != NULL) { + priv->ot_state = OT_CALLBACK; ret = rpc_transport_notify (this, RPC_TRANSPORT_MSG_RECEIVED, pollin); - + if (priv->ot_state == OT_CALLBACK) { + priv->ot_state = OT_RUNNING; + } rpc_transport_pollin_destroy (pollin); } @@ -1418,7 +2128,7 @@ socket_event_poll_in (rpc_transport_t *this) } -int +static int socket_connect_finish (rpc_transport_t *this) { int ret = -1; @@ -1426,55 +2136,57 @@ socket_connect_finish (rpc_transport_t *this) rpc_transport_event_t event = 0; char notify_rpc = 0; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + priv = this->private; pthread_mutex_lock (&priv->lock); { - if (priv->connected) - goto unlock; + if (priv->connected != 0) + goto unlock; + + get_transport_identifiers (this); - ret = __socket_connect_finish (priv->sock); + ret = __socket_connect_finish (priv->sock); - if (ret == -1 && errno == EINPROGRESS) - ret = 1; + if (ret == -1 && errno == EINPROGRESS) + ret = 1; - if (ret == -1 && errno != EINPROGRESS) { - if (!priv->connect_finish_log) { - gf_log (this->name, GF_LOG_ERROR, - "connection to %s failed (%s)", + if (ret == -1 && errno != EINPROGRESS) { + if (!priv->connect_finish_log) { + gf_log (this->name, GF_LOG_ERROR, + "connection to %s failed (%s)", this->peerinfo.identifier, - strerror (errno)); - priv->connect_finish_log = 1; - } - __socket_disconnect (this); - notify_rpc = 1; - event = RPC_TRANSPORT_DISCONNECT; - goto unlock; - } + strerror (errno)); + priv->connect_finish_log = 1; + } + __socket_disconnect (this); + goto unlock; + } - if (ret == 0) { - notify_rpc = 1; - - this->myinfo.sockaddr_len = - sizeof (this->myinfo.sockaddr); - - ret = getsockname (priv->sock, - SA (&this->myinfo.sockaddr), - &this->myinfo.sockaddr_len); - if (ret == -1) { - gf_log (this->name, GF_LOG_DEBUG, - "getsockname on (%d) failed (%s)", - priv->sock, strerror (errno)); - __socket_disconnect (this); - event = GF_EVENT_POLLERR; - goto unlock; - } + if (ret == 0) { + notify_rpc = 1; - priv->connected = 1; - priv->connect_finish_log = 0; - event = RPC_TRANSPORT_CONNECT; - get_transport_identifiers (this); - } + this->myinfo.sockaddr_len = + sizeof (this->myinfo.sockaddr); + + ret = getsockname (priv->sock, + SA (&this->myinfo.sockaddr), + &this->myinfo.sockaddr_len); + if (ret == -1) { + gf_log (this->name, GF_LOG_WARNING, + "getsockname on (%d) failed (%s)", + priv->sock, strerror (errno)); + __socket_disconnect (this); + event = GF_EVENT_POLLERR; + goto unlock; + } + + priv->connected = 1; + priv->connect_finish_log = 0; + event = RPC_TRANSPORT_CONNECT; + } } unlock: pthread_mutex_unlock (&priv->lock); @@ -1482,21 +2194,26 @@ unlock: if (notify_rpc) { rpc_transport_notify (this, event, this); } - +out: return 0; } /* reads rpc_requests during pollin */ -int +static int socket_event_handler (int fd, int idx, void *data, int poll_in, int poll_out, int poll_err) { - rpc_transport_t *this = NULL; + rpc_transport_t *this = NULL; socket_private_t *priv = NULL; - int ret = 0; + int ret = -1; this = data; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + GF_VALIDATE_OR_GOTO ("socket", this->xl, out); + + THIS = this->xl; priv = this->private; pthread_mutex_lock (&priv->lock); @@ -1505,9 +2222,7 @@ socket_event_handler (int fd, int idx, void *data, } pthread_mutex_unlock (&priv->lock); - if (!priv->connected) { - ret = socket_connect_finish (this); - } + ret = (priv->connected == 1) ? 0 : socket_connect_finish(this); if (!ret && poll_out) { ret = socket_event_poll_out (this); @@ -1518,16 +2233,203 @@ socket_event_handler (int fd, int idx, void *data, } if ((ret < 0) || poll_err) { - gf_log ("transport", GF_LOG_TRACE, "disconnecting now"); + /* Logging has happened already in earlier cases */ + gf_log ("transport", ((ret >= 0) ? GF_LOG_INFO : GF_LOG_DEBUG), + "disconnecting now"); socket_event_poll_err (this); rpc_transport_unref (this); + } + +out: + return ret; +} + + +static void * +socket_poller (void *ctx) +{ + rpc_transport_t *this = ctx; + socket_private_t *priv = this->private; + struct pollfd pfd[2] = {{0,},}; + gf_boolean_t to_write = _gf_false; + int ret = 0; + uint32_t gen = 0; + + priv->ot_state = OT_RUNNING; + + if (priv->use_ssl) { + if (ssl_setup_connection(this,priv->connected) < 0) { + gf_log (this->name,GF_LOG_ERROR, "%s setup failed", + priv->connected ? "server" : "client"); + goto err; + } } - return 0; + if (!priv->bio) { + ret = __socket_nonblock (priv->sock); + if (ret == -1) { + gf_log (this->name, GF_LOG_WARNING, + "NBIO on %d failed (%s)", + priv->sock, strerror (errno)); + goto err; + } + } + + if (priv->connected == 0) { + THIS = this->xl; + ret = socket_connect_finish (this); + if (ret != 0) { + gf_log (this->name, GF_LOG_WARNING, + "asynchronous socket_connect_finish failed"); + } + } + + ret = rpc_transport_notify (this->listener, + RPC_TRANSPORT_ACCEPT, this); + if (ret != 0) { + gf_log (this->name, GF_LOG_WARNING, + "asynchronous rpc_transport_notify failed"); + } + + gen = priv->ot_gen; + for (;;) { + pthread_mutex_lock(&priv->lock); + to_write = !list_empty(&priv->ioq); + pthread_mutex_unlock(&priv->lock); + pfd[0].fd = priv->pipe[0]; + pfd[0].events = POLL_MASK_ERROR; + pfd[0].revents = 0; + pfd[1].fd = priv->sock; + pfd[1].events = POLL_MASK_INPUT | POLL_MASK_ERROR; + pfd[1].revents = 0; + if (to_write) { + pfd[1].events |= POLL_MASK_OUTPUT; + } + else { + pfd[0].events |= POLL_MASK_INPUT; + } + if (poll(pfd,2,-1) < 0) { + gf_log(this->name,GF_LOG_ERROR,"poll failed"); + break; + } + if (pfd[0].revents & POLL_MASK_ERROR) { + gf_log(this->name,GF_LOG_ERROR, + "poll error on pipe"); + break; + } + /* Only glusterd actually seems to need this. */ + THIS = this->xl; + if (pfd[1].revents & POLL_MASK_INPUT) { + ret = socket_event_poll_in(this); + if (ret >= 0) { + /* Suppress errors while making progress. */ + pfd[1].revents &= ~POLL_MASK_ERROR; + } + else if (errno == ENOTCONN) { + ret = 0; + } + if (priv->ot_state == OT_PLEASE_DIE) { + gf_log (this->name, GF_LOG_TRACE, + "OT_IDLE on %p (input request)", + this); + priv->ot_state = OT_IDLE; + break; + } + } + else if (pfd[1].revents & POLL_MASK_OUTPUT) { + ret = socket_event_poll_out(this); + if (ret >= 0) { + /* Suppress errors while making progress. */ + pfd[1].revents &= ~POLL_MASK_ERROR; + } + else if (errno == ENOTCONN) { + ret = 0; + } + if (priv->ot_state == OT_PLEASE_DIE) { + gf_log (this->name, GF_LOG_TRACE, + "OT_IDLE on %p (output request)", + this); + priv->ot_state = OT_IDLE; + break; + } + } + else { + /* + * This usually means that we left poll() because + * somebody pushed a byte onto our pipe. That wakeup + * is why the pipe is there, but once awake we can do + * all the checking we need on the next iteration. + */ + ret = 0; + } + if (pfd[1].revents & POLL_MASK_ERROR) { + gf_log(this->name,GF_LOG_ERROR, + "poll error on socket"); + break; + } + if (ret < 0) { + gf_log(this->name,GF_LOG_ERROR, + "error in polling loop"); + break; + } + if (priv->ot_gen != gen) { + gf_log (this->name, GF_LOG_TRACE, + "generation mismatch, my %u != %u", + gen, priv->ot_gen); + return NULL; + } + } + +err: + /* All (and only) I/O errors should come here. */ + pthread_mutex_lock(&priv->lock); + if (priv->ssl_ssl) { + /* + * We're always responsible for this part, but only actually + * have to do it if we got far enough for ssl_ssl to be valid + * (i.e. errors in ssl_setup_connection don't count). + */ + ssl_teardown_connection(priv); + } + __socket_shutdown(this); + close(priv->sock); + priv->sock = -1; + priv->ot_state = OT_IDLE; + pthread_mutex_unlock(&priv->lock); + rpc_transport_notify (this->listener, RPC_TRANSPORT_DISCONNECT, + this); + rpc_transport_unref (this); + return NULL; } -int +static void +socket_spawn (rpc_transport_t *this) +{ + socket_private_t *priv = this->private; + + switch (priv->ot_state) { + case OT_IDLE: + case OT_PLEASE_DIE: + break; + default: + gf_log (this->name, GF_LOG_WARNING, + "refusing to start redundant poller"); + return; + } + + priv->ot_gen += 7; + priv->ot_state = OT_SPAWNING; + gf_log (this->name, GF_LOG_TRACE, + "spawning %p with gen %u", this, priv->ot_gen); + + if (gf_thread_create(&priv->thread,NULL,socket_poller,this) != 0) { + gf_log (this->name, GF_LOG_ERROR, + "could not create poll thread"); + } +} + +static int socket_server_event_handler (int fd, int idx, void *data, int poll_in, int poll_out, int poll_err) { @@ -1539,11 +2441,16 @@ socket_server_event_handler (int fd, int idx, void *data, struct sockaddr_storage new_sockaddr = {0, }; socklen_t addrlen = sizeof (new_sockaddr); socket_private_t *new_priv = NULL; - glusterfs_ctx_t *ctx = NULL; + glusterfs_ctx_t *ctx = NULL; this = data; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + GF_VALIDATE_OR_GOTO ("socket", this->xl, out); + + THIS = this->xl; priv = this->private; - ctx = this->ctx; + ctx = this->ctx; pthread_mutex_lock (&priv->lock); { @@ -1551,54 +2458,65 @@ socket_server_event_handler (int fd, int idx, void *data, if (poll_in) { new_sock = accept (priv->sock, SA (&new_sockaddr), - &addrlen); + &addrlen); - if (new_sock == -1) + if (new_sock == -1) { + gf_log (this->name, GF_LOG_WARNING, + "accept on %d failed (%s)", + priv->sock, strerror (errno)); goto unlock; - - if (!priv->bio) { - ret = __socket_nonblock (new_sock); - - if (ret == -1) { - gf_log (this->name, GF_LOG_DEBUG, - "NBIO on %d failed (%s)", - new_sock, strerror (errno)); - - close (new_sock); - goto unlock; - } } - if (priv->nodelay) { + if (priv->nodelay && (new_sockaddr.ss_family != AF_UNIX)) { ret = __socket_nodelay (new_sock); if (ret == -1) { - gf_log (this->name, GF_LOG_ERROR, + gf_log (this->name, GF_LOG_WARNING, "setsockopt() failed for " "NODELAY (%s)", strerror (errno)); } } + if (priv->keepalive && + new_sockaddr.ss_family != AF_UNIX) { + ret = __socket_keepalive (new_sock, + new_sockaddr.ss_family, + priv->keepaliveintvl, + priv->keepaliveidle); + if (ret == -1) + gf_log (this->name, GF_LOG_WARNING, + "Failed to set keep-alive: %s", + strerror (errno)); + } + new_trans = GF_CALLOC (1, sizeof (*new_trans), gf_common_mt_rpc_trans_t); if (!new_trans) goto unlock; - new_trans->fini = this->fini; + ret = pthread_mutex_init(&new_trans->lock, NULL); + if (ret == -1) { + gf_log (this->name, GF_LOG_WARNING, + "pthread_mutex_init() failed: %s", + strerror (errno)); + close (new_sock); + goto unlock; + } + new_trans->name = gf_strdup (this->name); memcpy (&new_trans->peerinfo.sockaddr, &new_sockaddr, - addrlen); + addrlen); new_trans->peerinfo.sockaddr_len = addrlen; new_trans->myinfo.sockaddr_len = - sizeof (new_trans->myinfo.sockaddr); + sizeof (new_trans->myinfo.sockaddr); ret = getsockname (new_sock, SA (&new_trans->myinfo.sockaddr), &new_trans->myinfo.sockaddr_len); if (ret == -1) { - gf_log (this->name, GF_LOG_DEBUG, + gf_log (this->name, GF_LOG_WARNING, "getsockname on %d failed (%s)", new_sock, strerror (errno)); close (new_sock); @@ -1606,51 +2524,108 @@ socket_server_event_handler (int fd, int idx, void *data, } get_transport_identifiers (new_trans); - socket_init (new_trans); + ret = socket_init(new_trans); + if (ret != 0) { + close(new_sock); + goto unlock; + } new_trans->ops = this->ops; new_trans->init = this->init; new_trans->fini = this->fini; new_trans->ctx = ctx; + new_trans->xl = this->xl; new_trans->mydata = this->mydata; new_trans->notify = this->notify; + new_trans->listener = this; new_priv = new_trans->private; + new_priv->use_ssl = priv->use_ssl; + new_priv->sock = new_sock; + new_priv->own_thread = priv->own_thread; + + new_priv->ssl_ctx = priv->ssl_ctx; + if (priv->use_ssl && !priv->own_thread) { + if (ssl_setup_connection(new_trans,1) < 0) { + gf_log(this->name,GF_LOG_ERROR, + "server setup failed"); + close(new_sock); + goto unlock; + } + } + + if (!priv->bio && !priv->own_thread) { + ret = __socket_nonblock (new_sock); + + if (ret == -1) { + gf_log (this->name, GF_LOG_WARNING, + "NBIO on %d failed (%s)", + new_sock, strerror (errno)); + + close (new_sock); + goto unlock; + } + } + pthread_mutex_lock (&new_priv->lock); { - new_priv->sock = new_sock; + /* + * In the own_thread case, this is used to + * indicate that we're initializing a server + * connection. + */ new_priv->connected = 1; + new_priv->is_server = _gf_true; rpc_transport_ref (new_trans); - new_priv->idx = - event_register (ctx->event_pool, - new_sock, - socket_event_handler, - new_trans, 1, 0); + if (new_priv->own_thread) { + if (pipe(new_priv->pipe) < 0) { + gf_log(this->name,GF_LOG_ERROR, + "could not create pipe"); + } + socket_spawn(new_trans); + } + else { + new_priv->idx = + event_register (ctx->event_pool, + new_sock, + socket_event_handler, + new_trans, + 1, 0); + if (new_priv->idx == -1) + ret = -1; + } - if (new_priv->idx == -1) - ret = -1; } pthread_mutex_unlock (&new_priv->lock); - if (ret == -1) + if (ret == -1) { + gf_log (this->name, GF_LOG_WARNING, + "failed to register the socket with event"); goto unlock; + } - ret = rpc_transport_notify (this, RPC_TRANSPORT_ACCEPT, - new_trans); + if (!priv->own_thread) { + ret = rpc_transport_notify (this, + RPC_TRANSPORT_ACCEPT, new_trans); + } } } unlock: pthread_mutex_unlock (&priv->lock); +out: return ret; } -int +static int socket_disconnect (rpc_transport_t *this) { socket_private_t *priv = NULL; int ret = -1; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + priv = this->private; pthread_mutex_lock (&priv->lock); @@ -1659,26 +2634,32 @@ socket_disconnect (rpc_transport_t *this) } pthread_mutex_unlock (&priv->lock); +out: return ret; } -int -socket_connect (rpc_transport_t *this) +static int +socket_connect (rpc_transport_t *this, int port) { int ret = -1; - int sock = -1; + int sock = -1; socket_private_t *priv = NULL; - struct sockaddr_storage sockaddr = {0, }; socklen_t sockaddr_len = 0; - glusterfs_ctx_t *ctx = NULL; + glusterfs_ctx_t *ctx = NULL; sa_family_t sa_family = {0, }; + char *local_addr = NULL; + union gf_sock_union sock_union; + struct sockaddr_in *addr = NULL; + + GF_VALIDATE_OR_GOTO ("socket", this, err); + GF_VALIDATE_OR_GOTO ("socket", this->private, err); priv = this->private; - ctx = this->ctx; + ctx = this->ctx; if (!priv) { - gf_log (this->name, GF_LOG_DEBUG, + gf_log_callingfn (this->name, GF_LOG_WARNING, "connect() called on uninitialized transport"); goto err; } @@ -1690,19 +2671,41 @@ socket_connect (rpc_transport_t *this) pthread_mutex_unlock (&priv->lock); if (sock != -1) { - gf_log (this->name, GF_LOG_TRACE, - "connect () called on transport already connected"); - ret = 0; + gf_log_callingfn (this->name, GF_LOG_TRACE, + "connect () called on transport already connected"); + errno = EINPROGRESS; + ret = -1; goto err; } - ret = socket_client_get_remote_sockaddr (this, SA (&sockaddr), + gf_log (this->name, GF_LOG_TRACE, + "connecting %p, state=%u gen=%u sock=%d", this, + priv->ot_state, priv->ot_gen, priv->sock); + + ret = socket_client_get_remote_sockaddr (this, &sock_union.sa, &sockaddr_len, &sa_family); if (ret == -1) { /* logged inside client_get_remote_sockaddr */ goto err; } + if (port > 0) { + sock_union.sin.sin_port = htons (port); + } + if (ntohs(sock_union.sin.sin_port) == GF_DEFAULT_SOCKET_LISTEN_PORT) { + if (priv->use_ssl) { + gf_log(this->name,GF_LOG_DEBUG, + "disabling SSL for portmapper connection"); + priv->use_ssl = _gf_false; + } + } + else { + if (priv->ssl_enabled && !priv->use_ssl) { + gf_log(this->name,GF_LOG_DEBUG, + "re-enabling SSL for I/O connection"); + priv->use_ssl = _gf_true; + } + } pthread_mutex_lock (&priv->lock); { if (priv->sock != -1) { @@ -1711,66 +2714,78 @@ socket_connect (rpc_transport_t *this) goto unlock; } - memcpy (&this->peerinfo.sockaddr, &sockaddr, sockaddr_len); + memcpy (&this->peerinfo.sockaddr, &sock_union.storage, + sockaddr_len); this->peerinfo.sockaddr_len = sockaddr_len; priv->sock = socket (sa_family, SOCK_STREAM, 0); if (priv->sock == -1) { gf_log (this->name, GF_LOG_ERROR, "socket creation failed (%s)", - strerror (errno)); + strerror (errno)); goto unlock; } /* Cant help if setting socket options fails. We can continue * working nonetheless. */ - if (setsockopt (priv->sock, SOL_SOCKET, SO_RCVBUF, - &priv->windowsize, - sizeof (priv->windowsize)) < 0) { - gf_log (this->name, GF_LOG_ERROR, - "setting receive window size failed: %d: %d: " - "%s", priv->sock, priv->windowsize, - strerror (errno)); - } - - if (setsockopt (priv->sock, SOL_SOCKET, SO_SNDBUF, - &priv->windowsize, - sizeof (priv->windowsize)) < 0) { - gf_log (this->name, GF_LOG_ERROR, - "setting send window size failed: %d: %d: " - "%s", priv->sock, priv->windowsize, - strerror (errno)); - } - + if (priv->windowsize != 0) { + if (setsockopt (priv->sock, SOL_SOCKET, SO_RCVBUF, + &priv->windowsize, + sizeof (priv->windowsize)) < 0) { + gf_log (this->name, GF_LOG_ERROR, + "setting receive window " + "size failed: %d: %d: %s", + priv->sock, priv->windowsize, + strerror (errno)); + } - if (priv->nodelay && priv->lowlat) { - ret = __socket_nodelay (priv->sock); - if (ret == -1) { + if (setsockopt (priv->sock, SOL_SOCKET, SO_SNDBUF, + &priv->windowsize, + sizeof (priv->windowsize)) < 0) { gf_log (this->name, GF_LOG_ERROR, - "setsockopt() failed for NODELAY (%s)", + "setting send window size " + "failed: %d: %d: %s", + priv->sock, priv->windowsize, strerror (errno)); } } - if (!priv->bio) { - ret = __socket_nonblock (priv->sock); + if (priv->nodelay && (sa_family != AF_UNIX)) { + ret = __socket_nodelay (priv->sock); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, - "NBIO on %d failed (%s)", + "NODELAY on %d failed (%s)", priv->sock, strerror (errno)); - close (priv->sock); - priv->sock = -1; - goto unlock; } } + if (priv->keepalive && sa_family != AF_UNIX) { + ret = __socket_keepalive (priv->sock, + sa_family, + priv->keepaliveintvl, + priv->keepaliveidle); + if (ret == -1) + gf_log (this->name, GF_LOG_ERROR, + "Failed to set keep-alive: %s", + strerror (errno)); + } + SA (&this->myinfo.sockaddr)->sa_family = - SA (&this->peerinfo.sockaddr)->sa_family; + SA (&this->peerinfo.sockaddr)->sa_family; + + /* If a source addr is explicitly specified, use it */ + ret = dict_get_str (this->options, + "transport.socket.source-addr", + &local_addr); + if (!ret && SA (&this->myinfo.sockaddr)->sa_family == AF_INET) { + addr = (struct sockaddr_in *)(&this->myinfo.sockaddr); + ret = inet_pton (AF_INET, local_addr, &(addr->sin_addr.s_addr)); + } ret = client_bind (this, SA (&this->myinfo.sockaddr), - &this->myinfo.sockaddr_len, priv->sock); + &this->myinfo.sockaddr_len, priv->sock); if (ret == -1) { gf_log (this->name, GF_LOG_WARNING, "client bind failed: %s", strerror (errno)); @@ -1779,26 +2794,86 @@ socket_connect (rpc_transport_t *this) goto unlock; } + if (!priv->use_ssl && !priv->bio && !priv->own_thread) { + ret = __socket_nonblock (priv->sock); + if (ret == -1) { + gf_log (this->name, GF_LOG_ERROR, + "NBIO on %d failed (%s)", + priv->sock, strerror (errno)); + close (priv->sock); + priv->sock = -1; + goto unlock; + } + } + ret = connect (priv->sock, SA (&this->peerinfo.sockaddr), - this->peerinfo.sockaddr_len); + this->peerinfo.sockaddr_len); - if (ret == -1 && errno != EINPROGRESS) { - gf_log (this->name, GF_LOG_ERROR, - "connection attempt failed (%s)", - strerror (errno)); + if (ret == -1 && ((errno != EINPROGRESS) && (errno != ENOENT))) { + /* For unix path based sockets, the socket path is + * cryptic (md5sum of path) and may not be useful for + * the user in debugging so log it in DEBUG + */ + gf_log (this->name, ((sa_family == AF_UNIX) ? + GF_LOG_DEBUG : GF_LOG_ERROR), + "connection attempt on %s failed, (%s)", + this->peerinfo.identifier, strerror (errno)); close (priv->sock); priv->sock = -1; goto unlock; } - priv->connected = 0; + if (priv->use_ssl && !priv->own_thread) { + ret = ssl_setup_connection(this,0); + if (ret < 0) { + gf_log(this->name,GF_LOG_ERROR, + "client setup failed"); + close(priv->sock); + priv->sock = -1; + goto unlock; + } + } + + if (!priv->bio && !priv->own_thread) { + ret = __socket_nonblock (priv->sock); + + if (ret == -1) { + gf_log (this->name, GF_LOG_ERROR, + "NBIO on %d failed (%s)", + priv->sock, strerror (errno)); + close (priv->sock); + priv->sock = -1; + goto unlock; + } + } + /* + * In the own_thread case, this is used to indicate that we're + * initializing a client connection. + */ + priv->connected = 0; + priv->is_server = _gf_false; rpc_transport_ref (this); - priv->idx = event_register (ctx->event_pool, priv->sock, - socket_event_handler, this, 1, 1); - if (priv->idx == -1) - ret = -1; + if (priv->own_thread) { + if (pipe(priv->pipe) < 0) { + gf_log(this->name,GF_LOG_ERROR, + "could not create pipe"); + } + + this->listener = this; + socket_spawn(this); + } + else { + priv->idx = event_register (ctx->event_pool, priv->sock, + socket_event_handler, + this, 1, 1); + if (priv->idx == -1) { + gf_log ("", GF_LOG_WARNING, + "failed to register the event"); + ret = -1; + } + } } unlock: pthread_mutex_unlock (&priv->lock); @@ -1808,21 +2883,24 @@ err: } -int +static int socket_listen (rpc_transport_t *this) { socket_private_t * priv = NULL; int ret = -1; - int sock = -1; + int sock = -1; struct sockaddr_storage sockaddr; - socklen_t sockaddr_len; + socklen_t sockaddr_len = 0; peer_info_t *myinfo = NULL; - glusterfs_ctx_t *ctx = NULL; + glusterfs_ctx_t *ctx = NULL; sa_family_t sa_family = {0, }; - priv = this->private; - myinfo = &this->myinfo; - ctx = this->ctx; + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + + priv = this->private; + myinfo = &this->myinfo; + ctx = this->ctx; pthread_mutex_lock (&priv->lock); { @@ -1831,8 +2909,8 @@ socket_listen (rpc_transport_t *this) pthread_mutex_unlock (&priv->lock); if (sock != -1) { - gf_log (this->name, GF_LOG_DEBUG, - "alreading listening"); + gf_log_callingfn (this->name, GF_LOG_DEBUG, + "already listening"); return ret; } @@ -1858,32 +2936,36 @@ socket_listen (rpc_transport_t *this) if (priv->sock == -1) { gf_log (this->name, GF_LOG_ERROR, "socket creation failed (%s)", - strerror (errno)); + strerror (errno)); goto unlock; } /* Cant help if setting socket options fails. We can continue * working nonetheless. */ - if (setsockopt (priv->sock, SOL_SOCKET, SO_RCVBUF, - &priv->windowsize, - sizeof (priv->windowsize)) < 0) { - gf_log (this->name, GF_LOG_ERROR, - "setting receive window size failed: %d: %d: " - "%s", priv->sock, priv->windowsize, - strerror (errno)); - } + if (priv->windowsize != 0) { + if (setsockopt (priv->sock, SOL_SOCKET, SO_RCVBUF, + &priv->windowsize, + sizeof (priv->windowsize)) < 0) { + gf_log (this->name, GF_LOG_ERROR, + "setting receive window size " + "failed: %d: %d: %s", priv->sock, + priv->windowsize, + strerror (errno)); + } - if (setsockopt (priv->sock, SOL_SOCKET, SO_SNDBUF, - &priv->windowsize, - sizeof (priv->windowsize)) < 0) { - gf_log (this->name, GF_LOG_ERROR, - "setting send window size failed: %d: %d: " - "%s", priv->sock, priv->windowsize, - strerror (errno)); + if (setsockopt (priv->sock, SOL_SOCKET, SO_SNDBUF, + &priv->windowsize, + sizeof (priv->windowsize)) < 0) { + gf_log (this->name, GF_LOG_ERROR, + "setting send window size failed:" + " %d: %d: %s", priv->sock, + priv->windowsize, + strerror (errno)); + } } - if (priv->nodelay) { + if (priv->nodelay && (sa_family != AF_UNIX)) { ret = __socket_nodelay (priv->sock); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, @@ -1914,12 +2996,15 @@ socket_listen (rpc_transport_t *this) goto unlock; } - ret = listen (priv->sock, 10); + if (priv->backlog) + ret = listen (priv->sock, priv->backlog); + else + ret = listen (priv->sock, 10); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, "could not set socket %d to listen mode (%s)", - priv->sock, strerror (errno)); + priv->sock, strerror (errno)); close (priv->sock); priv->sock = -1; goto unlock; @@ -1929,12 +3014,12 @@ socket_listen (rpc_transport_t *this) priv->idx = event_register (ctx->event_pool, priv->sock, socket_server_event_handler, - this, 1, 0); + this, 1, 0); if (priv->idx == -1) { - gf_log (this->name, GF_LOG_DEBUG, + gf_log (this->name, GF_LOG_WARNING, "could not register socket %d with events", - priv->sock); + priv->sock); ret = -1; close (priv->sock); priv->sock = -1; @@ -1944,75 +3029,12 @@ socket_listen (rpc_transport_t *this) unlock: pthread_mutex_unlock (&priv->lock); +out: return ret; } -/* TODO: implement per transfer limit */ -#if 0 -int -socket_submit (rpc_transport_t *this, char *buf, int len, - struct iovec *vector, int count, - struct iobref *iobref) -{ - socket_private_t *priv = NULL; - int ret = -1; - char need_poll_out = 0; - char need_append = 1; - struct ioq *entry = NULL; - glusterfs_ctx_t *ctx = NULL; - - priv = this->private; - ctx = this->ctx; - - pthread_mutex_lock (&priv->lock); - { - if (priv->connected != 1) { - if (!priv->submit_log && !priv->connect_finish_log) { - gf_log (this->name, GF_LOG_DEBUG, - "not connected (priv->connected = %d)", - priv->connected); - priv->submit_log = 1; - } - goto unlock; - } - - priv->submit_log = 0; - entry = __socket_ioq_new (this, buf, len, vector, count, iobref); - if (!entry) - goto unlock; - - if (list_empty (&priv->ioq)) { - ret = __socket_ioq_churn_entry (this, entry); - - if (ret == 0) - need_append = 0; - - if (ret > 0) - need_poll_out = 1; - } - - if (need_append) { - list_add_tail (&entry->list, &priv->ioq); - ret = 0; - } - - if (need_poll_out) { - /* first entry to wait. continue writing on POLLOUT */ - priv->idx = event_select_on (ctx->event_pool, - priv->sock, - priv->idx, -1, 1); - } - } -unlock: - pthread_mutex_unlock (&priv->lock); - - return ret; -} -#endif - - -int32_t +static int32_t socket_submit_request (rpc_transport_t *this, rpc_transport_req_t *req) { socket_private_t *priv = NULL; @@ -2020,16 +3042,20 @@ socket_submit_request (rpc_transport_t *this, rpc_transport_req_t *req) char need_poll_out = 0; char need_append = 1; struct ioq *entry = NULL; - glusterfs_ctx_t *ctx = NULL; + glusterfs_ctx_t *ctx = NULL; + char a_byte = 'j'; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; - ctx = this->ctx; + ctx = this->ctx; pthread_mutex_lock (&priv->lock); { if (priv->connected != 1) { if (!priv->submit_log && !priv->connect_finish_log) { - gf_log (this->name, GF_LOG_DEBUG, + gf_log (this->name, GF_LOG_INFO, "not connected (priv->connected = %d)", priv->connected); priv->submit_log = 1; @@ -2043,35 +3069,46 @@ socket_submit_request (rpc_transport_t *this, rpc_transport_req_t *req) goto unlock; if (list_empty (&priv->ioq)) { - ret = __socket_ioq_churn_entry (this, entry); + ret = __socket_ioq_churn_entry (this, entry, 1); - if (ret == 0) + if (ret == 0) { need_append = 0; - - if (ret > 0) + } + if (ret > 0) { need_poll_out = 1; + } } if (need_append) { list_add_tail (&entry->list, &priv->ioq); + if (priv->own_thread) { + /* + * Make sure the polling thread wakes up, by + * writing a byte to represent this entry. + */ + if (write(priv->pipe[1],&a_byte,1) < 1) { + gf_log(this->name,GF_LOG_WARNING, + "write error on pipe"); + } + } ret = 0; } - - if (need_poll_out) { + if (!priv->own_thread && need_poll_out) { /* first entry to wait. continue writing on POLLOUT */ priv->idx = event_select_on (ctx->event_pool, - priv->sock, + priv->sock, priv->idx, -1, 1); } } unlock: pthread_mutex_unlock (&priv->lock); +out: return ret; } -int32_t +static int32_t socket_submit_reply (rpc_transport_t *this, rpc_transport_reply_t *reply) { socket_private_t *priv = NULL; @@ -2079,64 +3116,79 @@ socket_submit_reply (rpc_transport_t *this, rpc_transport_reply_t *reply) char need_poll_out = 0; char need_append = 1; struct ioq *entry = NULL; - glusterfs_ctx_t *ctx = NULL; + glusterfs_ctx_t *ctx = NULL; + char a_byte = 'd'; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); priv = this->private; - ctx = this->ctx; + ctx = this->ctx; pthread_mutex_lock (&priv->lock); { if (priv->connected != 1) { if (!priv->submit_log && !priv->connect_finish_log) { - gf_log (this->name, GF_LOG_DEBUG, + gf_log (this->name, GF_LOG_INFO, "not connected (priv->connected = %d)", priv->connected); priv->submit_log = 1; } goto unlock; } + priv->submit_log = 0; entry = __socket_ioq_new (this, &reply->msg); if (!entry) goto unlock; + if (list_empty (&priv->ioq)) { - ret = __socket_ioq_churn_entry (this, entry); + ret = __socket_ioq_churn_entry (this, entry, 1); - if (ret == 0) + if (ret == 0) { need_append = 0; - - if (ret > 0) + } + if (ret > 0) { need_poll_out = 1; + } } if (need_append) { list_add_tail (&entry->list, &priv->ioq); + if (priv->own_thread) { + /* + * Make sure the polling thread wakes up, by + * writing a byte to represent this entry. + */ + if (write(priv->pipe[1],&a_byte,1) < 1) { + gf_log(this->name,GF_LOG_WARNING, + "write error on pipe"); + } + } ret = 0; } - - if (need_poll_out) { + if (!priv->own_thread && need_poll_out) { /* first entry to wait. continue writing on POLLOUT */ priv->idx = event_select_on (ctx->event_pool, - priv->sock, + priv->sock, priv->idx, -1, 1); } } - unlock: pthread_mutex_unlock (&priv->lock); +out: return ret; } -int32_t +static int32_t socket_getpeername (rpc_transport_t *this, char *hostname, int hostlen) { int32_t ret = -1; - if ((this == NULL) || (hostname == NULL)) { - goto out; - } + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", hostname, out); if (hostlen < (strlen (this->peerinfo.identifier) + 1)) { goto out; @@ -2149,35 +3201,34 @@ out: } -int32_t +static int32_t socket_getpeeraddr (rpc_transport_t *this, char *peeraddr, int addrlen, - struct sockaddr *sa, socklen_t salen) + struct sockaddr_storage *sa, socklen_t salen) { int32_t ret = -1; - if ((this == NULL) || (sa == NULL)) { - goto out; - } + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", sa, out); - *sa = *((struct sockaddr *)&this->peerinfo.sockaddr); + *sa = this->peerinfo.sockaddr; if (peeraddr != NULL) { ret = socket_getpeername (this, peeraddr, addrlen); } + ret = 0; out: return ret; } -int32_t +static int32_t socket_getmyname (rpc_transport_t *this, char *hostname, int hostlen) { int32_t ret = -1; - if ((this == NULL) || (hostname == NULL)) { - goto out; - } + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", hostname, out); if (hostlen < (strlen (this->myinfo.identifier) + 1)) { goto out; @@ -2190,17 +3241,16 @@ out: } -int32_t +static int32_t socket_getmyaddr (rpc_transport_t *this, char *myaddr, int addrlen, - struct sockaddr *sa, socklen_t salen) + struct sockaddr_storage *sa, socklen_t salen) { int32_t ret = 0; - if ((this == NULL) || (sa == NULL)) { - goto out; - } + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", sa, out); - *sa = *((struct sockaddr *)&this->myinfo.sockaddr); + *sa = this->myinfo.sockaddr; if (myaddr != NULL) { ret = socket_getmyname (this, myaddr, addrlen); @@ -2211,6 +3261,25 @@ out: } +static int +socket_throttle (rpc_transport_t *this, gf_boolean_t onoff) +{ + socket_private_t *priv = NULL; + + priv = this->private; + + /* The way we implement throttling is by taking off + POLLIN event from the polled flags. This way we + never get called with the POLLIN event and therefore + will never read() any more data until throttling + is turned off. + */ + priv->idx = event_select_on (this->ctx->event_pool, priv->sock, + priv->idx, (int) !onoff, -1); + return 0; +} + + struct rpc_transport_ops tops = { .listen = socket_listen, .connect = socket_connect, @@ -2220,40 +3289,101 @@ struct rpc_transport_ops tops = { .get_peername = socket_getpeername, .get_peeraddr = socket_getpeeraddr, .get_myname = socket_getmyname, - .get_myaddr = socket_getmyaddr + .get_myaddr = socket_getmyaddr, + .throttle = socket_throttle, }; - int +reconfigure (rpc_transport_t *this, dict_t *options) +{ + socket_private_t *priv = NULL; + gf_boolean_t tmp_bool = _gf_false; + char *optstr = NULL; + int ret = 0; + uint64_t windowsize = 0; + + GF_VALIDATE_OR_GOTO ("socket", this, out); + GF_VALIDATE_OR_GOTO ("socket", this->private, out); + + if (!this || !this->private) { + ret =-1; + goto out; + } + + priv = this->private; + + if (dict_get_str (this->options, "transport.socket.keepalive", + &optstr) == 0) { + if (gf_string2boolean (optstr, &tmp_bool) == -1) { + gf_log (this->name, GF_LOG_ERROR, + "'transport.socket.keepalive' takes only " + "boolean options, not taking any action"); + priv->keepalive = 1; + ret = -1; + goto out; + } + gf_log (this->name, GF_LOG_DEBUG, "Reconfigured transport.socket.keepalive"); + + priv->keepalive = tmp_bool; + } + else + priv->keepalive = 1; + + optstr = NULL; + if (dict_get_str (this->options, "tcp-window-size", + &optstr) == 0) { + if (gf_string2bytesize (optstr, &windowsize) != 0) { + gf_log (this->name, GF_LOG_ERROR, + "invalid number format: %s", optstr); + goto out; + } + } + + priv->windowsize = (int)windowsize; + + ret = 0; +out: + return ret; + +} + +static int socket_init (rpc_transport_t *this) { socket_private_t *priv = NULL; gf_boolean_t tmp_bool = 0; uint64_t windowsize = GF_DEFAULT_SOCKET_WINDOW_SIZE; char *optstr = NULL; + uint32_t keepalive = 0; + uint32_t backlog = 0; + int session_id = 0; if (this->private) { - gf_log (this->name, GF_LOG_DEBUG, - "double init attempted"); + gf_log_callingfn (this->name, GF_LOG_ERROR, + "double init attempted"); return -1; } priv = GF_CALLOC (1, sizeof (*priv), gf_common_mt_socket_private_t); if (!priv) { - gf_log (this->name, GF_LOG_ERROR, - "calloc (1, %"GF_PRI_SIZET") returned NULL", - sizeof (*priv)); return -1; } + memset(priv,0,sizeof(*priv)); pthread_mutex_init (&priv->lock, NULL); priv->sock = -1; priv->idx = -1; priv->connected = -1; - + priv->nodelay = 1; + priv->bio = 0; + priv->windowsize = GF_DEFAULT_SOCKET_WINDOW_SIZE; INIT_LIST_HEAD (&priv->ioq); + /* All the below section needs 'this->options' to be present */ + if (!this->options) + goto out; + if (dict_get (this->options, "non-blocking-io")) { optstr = data_to_str (dict_get (this->options, "non-blocking-io")); @@ -2261,10 +3391,10 @@ socket_init (rpc_transport_t *this) if (gf_string2boolean (optstr, &tmp_bool) == -1) { gf_log (this->name, GF_LOG_ERROR, "'non-blocking-io' takes only boolean options," - " not taking any action"); + " not taking any action"); tmp_bool = 1; } - priv->bio = 0; + if (!tmp_bool) { priv->bio = 1; gf_log (this->name, GF_LOG_WARNING, @@ -2275,7 +3405,6 @@ socket_init (rpc_transport_t *this) optstr = NULL; // By default, we enable NODELAY - priv->nodelay = 1; if (dict_get (this->options, "transport.socket.nodelay")) { optstr = data_to_str (dict_get (this->options, "transport.socket.nodelay")); @@ -2293,9 +3422,8 @@ socket_init (rpc_transport_t *this) } } - optstr = NULL; - if (dict_get_str (this->options, "transport.window-size", + if (dict_get_str (this->options, "tcp-window-size", &optstr) == 0) { if (gf_string2bytesize (optstr, &windowsize) != 0) { gf_log (this->name, GF_LOG_ERROR, @@ -2304,32 +3432,226 @@ socket_init (rpc_transport_t *this) } } - optstr = NULL; + priv->windowsize = (int)windowsize; - if (dict_get_str (this->options, "transport.socket.lowlat", + optstr = NULL; + /* Enable Keep-alive by default. */ + priv->keepalive = 1; + priv->keepaliveintvl = 2; + priv->keepaliveidle = 20; + if (dict_get_str (this->options, "transport.socket.keepalive", &optstr) == 0) { - priv->lowlat = 1; + if (gf_string2boolean (optstr, &tmp_bool) == -1) { + gf_log (this->name, GF_LOG_ERROR, + "'transport.socket.keepalive' takes only " + "boolean options, not taking any action"); + tmp_bool = 1; + } + + if (!tmp_bool) + priv->keepalive = 0; + } + + if (dict_get_uint32 (this->options, + "transport.socket.keepalive-interval", + &keepalive) == 0) { + priv->keepaliveintvl = keepalive; + } + + if (dict_get_uint32 (this->options, + "transport.socket.keepalive-time", + &keepalive) == 0) { + priv->keepaliveidle = keepalive; + } + + if (dict_get_uint32 (this->options, + "transport.socket.listen-backlog", + &backlog) == 0) { + priv->backlog = backlog; + } + + optstr = NULL; + + /* Check if socket read failures are to be logged */ + priv->read_fail_log = 1; + if (dict_get (this->options, "transport.socket.read-fail-log")) { + optstr = data_to_str (dict_get (this->options, "transport.socket.read-fail-log")); + if (gf_string2boolean (optstr, &tmp_bool) == -1) { + gf_log (this->name, GF_LOG_WARNING, + "'transport.socket.read-fail-log' takes only " + "boolean options; logging socket read fails"); + } + else if (tmp_bool == _gf_false) { + priv->read_fail_log = 0; + } } priv->windowsize = (int)windowsize; - this->private = priv; + priv->ssl_enabled = _gf_false; + if (dict_get_str(this->options,SSL_ENABLED_OPT,&optstr) == 0) { + if (gf_string2boolean (optstr, &priv->ssl_enabled) != 0) { + gf_log (this->name, GF_LOG_ERROR, + "invalid value given for ssl-enabled boolean"); + } + } + + priv->ssl_own_cert = DEFAULT_CERT_PATH; + if (dict_get_str(this->options,SSL_OWN_CERT_OPT,&optstr) == 0) { + if (!priv->ssl_enabled) { + gf_log(this->name,GF_LOG_WARNING, + "%s specified without %s (ignored)", + SSL_OWN_CERT_OPT, SSL_ENABLED_OPT); + } + priv->ssl_own_cert = optstr; + } + priv->ssl_own_cert = gf_strdup(priv->ssl_own_cert); + + priv->ssl_private_key = DEFAULT_KEY_PATH; + if (dict_get_str(this->options,SSL_PRIVATE_KEY_OPT,&optstr) == 0) { + if (!priv->ssl_enabled) { + gf_log(this->name,GF_LOG_WARNING, + "%s specified without %s (ignored)", + SSL_PRIVATE_KEY_OPT, SSL_ENABLED_OPT); + } + priv->ssl_private_key = optstr; + } + priv->ssl_private_key = gf_strdup(priv->ssl_private_key); + + priv->ssl_ca_list = DEFAULT_CA_PATH; + if (dict_get_str(this->options,SSL_CA_LIST_OPT,&optstr) == 0) { + if (!priv->ssl_enabled) { + gf_log(this->name,GF_LOG_WARNING, + "%s specified without %s (ignored)", + SSL_CA_LIST_OPT, SSL_ENABLED_OPT); + } + priv->ssl_ca_list = optstr; + } + priv->ssl_ca_list = gf_strdup(priv->ssl_ca_list); + + gf_log(this->name,GF_LOG_INFO,"SSL support is %s", + priv->ssl_enabled ? "ENABLED" : "NOT enabled"); + /* + * This might get overridden temporarily in socket_connect (q.v.) + * if we're using the glusterd portmapper. + */ + priv->use_ssl = priv->ssl_enabled; + + priv->own_thread = priv->use_ssl; + if (dict_get_str(this->options,OWN_THREAD_OPT,&optstr) == 0) { + if (gf_string2boolean (optstr, &priv->own_thread) != 0) { + gf_log (this->name, GF_LOG_ERROR, + "invalid value given for own-thread boolean"); + } + } + gf_log(this->name,GF_LOG_INFO,"using %s polling thread", + priv->own_thread ? "private" : "system"); + + if (priv->use_ssl) { + SSL_library_init(); + SSL_load_error_strings(); + priv->ssl_meth = (SSL_METHOD *)TLSv1_method(); + priv->ssl_ctx = SSL_CTX_new(priv->ssl_meth); + + if (SSL_CTX_set_cipher_list(priv->ssl_ctx, + "HIGH:-SSLv2") == 0) { + gf_log(this->name,GF_LOG_ERROR, + "failed to find any valid ciphers"); + goto err; + } + + if (!SSL_CTX_use_certificate_chain_file(priv->ssl_ctx, + priv->ssl_own_cert)) { + gf_log(this->name,GF_LOG_ERROR, + "could not load our cert"); + goto err; + } + + if (!SSL_CTX_use_PrivateKey_file(priv->ssl_ctx, + priv->ssl_private_key, + SSL_FILETYPE_PEM)) { + gf_log(this->name,GF_LOG_ERROR, + "could not load private key"); + goto err; + } + + if (!SSL_CTX_load_verify_locations(priv->ssl_ctx, + priv->ssl_ca_list,0)) { + gf_log(this->name,GF_LOG_ERROR, + "could not load CA list"); + goto err; + } + +#if (OPENSSL_VERSION_NUMBER < 0x00905100L) + SSL_CTX_set_verify_depth(ctx,1); +#endif + + priv->ssl_session_id = ++session_id; + SSL_CTX_set_session_id_context(priv->ssl_ctx, + (void *)&priv->ssl_session_id, + sizeof(priv->ssl_session_id)); + + SSL_CTX_set_verify(priv->ssl_ctx,SSL_VERIFY_PEER,0); + } + + if (priv->own_thread) { + priv->ot_state = OT_IDLE; + } + +out: + this->private = priv; return 0; + +err: + if (priv->ssl_own_cert) { + GF_FREE(priv->ssl_own_cert); + } + if (priv->ssl_private_key) { + GF_FREE(priv->ssl_private_key); + } + if (priv->ssl_ca_list) { + GF_FREE(priv->ssl_ca_list); + } + GF_FREE(priv); + return -1; } void fini (rpc_transport_t *this) { - socket_private_t *priv = this->private; + socket_private_t *priv = NULL; - gf_log (this->name, GF_LOG_TRACE, - "transport %p destroyed", this); + if (!this) + return; - pthread_mutex_destroy (&priv->lock); + priv = this->private; + if (priv) { + if (priv->sock != -1) { + pthread_mutex_lock (&priv->lock); + { + __socket_ioq_flush (this); + __socket_reset (this); + } + pthread_mutex_unlock (&priv->lock); + } + gf_log (this->name, GF_LOG_TRACE, + "transport %p destroyed", this); + + pthread_mutex_destroy (&priv->lock); + if (priv->ssl_private_key) { + GF_FREE(priv->ssl_private_key); + } + if (priv->ssl_own_cert) { + GF_FREE(priv->ssl_own_cert); + } + if (priv->ssl_ca_list) { + GF_FREE(priv->ssl_ca_list); + } + GF_FREE (priv); + } - GF_FREE (this->name); - GF_FREE (priv); + this->private = NULL; } @@ -2370,18 +3692,17 @@ struct volume_options options[] = { }, { .key = { "transport.address-family", "address-family" }, - .value = {"inet", "inet6", "inet/inet6", "inet6/inet", - "unix", "inet-sdp" }, + .value = {"inet", "inet6", "unix", "inet-sdp" }, .type = GF_OPTION_TYPE_STR }, { .key = {"non-blocking-io"}, .type = GF_OPTION_TYPE_BOOL }, - { .key = {"transport.window-size"}, + { .key = {"tcp-window-size"}, .type = GF_OPTION_TYPE_SIZET, .min = GF_MIN_SOCKET_WINDOW_SIZE, - .max = GF_MAX_SOCKET_WINDOW_SIZE, + .max = GF_MAX_SOCKET_WINDOW_SIZE }, { .key = {"transport.socket.nodelay"}, .type = GF_OPTION_TYPE_BOOL @@ -2389,5 +3710,35 @@ struct volume_options options[] = { { .key = {"transport.socket.lowlat"}, .type = GF_OPTION_TYPE_BOOL }, + { .key = {"transport.socket.keepalive"}, + .type = GF_OPTION_TYPE_BOOL + }, + { .key = {"transport.socket.keepalive-interval"}, + .type = GF_OPTION_TYPE_INT + }, + { .key = {"transport.socket.keepalive-time"}, + .type = GF_OPTION_TYPE_INT + }, + { .key = {"transport.socket.listen-backlog"}, + .type = GF_OPTION_TYPE_INT + }, + { .key = {"transport.socket.read-fail-log"}, + .type = GF_OPTION_TYPE_BOOL + }, + { .key = {SSL_ENABLED_OPT}, + .type = GF_OPTION_TYPE_BOOL + }, + { .key = {SSL_OWN_CERT_OPT}, + .type = GF_OPTION_TYPE_STR + }, + { .key = {SSL_PRIVATE_KEY_OPT}, + .type = GF_OPTION_TYPE_STR + }, + { .key = {SSL_CA_LIST_OPT}, + .type = GF_OPTION_TYPE_STR + }, + { .key = {OWN_THREAD_OPT}, + .type = GF_OPTION_TYPE_BOOL + }, { .key = {NULL} } }; |
