summaryrefslogtreecommitdiffstats
path: root/xlators/protocol/server/src/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/protocol/server/src/server.c')
-rw-r--r--xlators/protocol/server/src/server.c1214
1 files changed, 1214 insertions, 0 deletions
diff --git a/xlators/protocol/server/src/server.c b/xlators/protocol/server/src/server.c
new file mode 100644
index 000000000..589bd7b36
--- /dev/null
+++ b/xlators/protocol/server/src/server.c
@@ -0,0 +1,1214 @@
+/*
+ Copyright (c) 2010-2013 Red Hat, Inc. <http://www.redhat.com>
+ This file is part of GlusterFS.
+
+ 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.
+*/
+
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+
+#include "server.h"
+#include "server-helpers.h"
+#include "glusterfs3-xdr.h"
+#include "call-stub.h"
+#include "statedump.h"
+#include "defaults.h"
+#include "authenticate.h"
+
+void
+grace_time_handler (void *data)
+{
+ client_t *client = NULL;
+ xlator_t *this = NULL;
+ gf_timer_t *timer = NULL;
+ server_ctx_t *serv_ctx = NULL;
+ gf_boolean_t cancelled = _gf_false;
+ gf_boolean_t detached = _gf_false;
+
+ client = data;
+ this = client->this;
+
+ GF_VALIDATE_OR_GOTO (THIS->name, this, out);
+
+ gf_log (this->name, GF_LOG_INFO, "grace timer expired for %s",
+ client->client_uid);
+
+ serv_ctx = server_ctx_get (client, this);
+
+ if (serv_ctx == NULL) {
+ gf_log (this->name, GF_LOG_INFO, "server_ctx_get() failed");
+ goto out;
+ }
+
+ LOCK (&serv_ctx->fdtable_lock);
+ {
+ if (serv_ctx->grace_timer) {
+ timer = serv_ctx->grace_timer;
+ serv_ctx->grace_timer = NULL;
+ }
+ }
+ UNLOCK (&serv_ctx->fdtable_lock);
+ if (timer) {
+ gf_timer_call_cancel (this->ctx, timer);
+ cancelled = _gf_true;
+ }
+ if (cancelled) {
+
+ /*
+ * client must not be destroyed in gf_client_put(),
+ * so take a ref.
+ */
+ gf_client_ref (client);
+ gf_client_put (client, &detached);
+ if (detached)//reconnection did not happen :-(
+ server_connection_cleanup (this, client,
+ INTERNAL_LOCKS | POSIX_LOCKS);
+ gf_client_unref (client);
+ }
+out:
+ return;
+}
+
+struct iobuf *
+gfs_serialize_reply (rpcsvc_request_t *req, void *arg, struct iovec *outmsg,
+ xdrproc_t xdrproc)
+{
+ struct iobuf *iob = NULL;
+ ssize_t retlen = 0;
+ ssize_t xdr_size = 0;
+
+ GF_VALIDATE_OR_GOTO ("server", req, ret);
+
+ /* First, get the io buffer into which the reply in arg will
+ * be serialized.
+ */
+ if (arg && xdrproc) {
+ xdr_size = xdr_sizeof (xdrproc, arg);
+ iob = iobuf_get2 (req->svc->ctx->iobuf_pool, xdr_size);
+ if (!iob) {
+ gf_log_callingfn (THIS->name, GF_LOG_ERROR,
+ "Failed to get iobuf");
+ goto ret;
+ };
+
+ iobuf_to_iovec (iob, outmsg);
+ /* Use the given serializer to translate the give C structure in arg
+ * to XDR format which will be written into the buffer in outmsg.
+ */
+ /* retlen is used to received the error since size_t is unsigned and we
+ * need -1 for error notification during encoding.
+ */
+
+ retlen = xdr_serialize_generic (*outmsg, arg, xdrproc);
+ if (retlen == -1) {
+ /* Failed to Encode 'GlusterFS' msg in RPC is not exactly
+ failure of RPC return values.. client should get
+ notified about this, so there are no missing frames */
+ gf_log_callingfn ("", GF_LOG_ERROR, "Failed to encode message");
+ req->rpc_err = GARBAGE_ARGS;
+ retlen = 0;
+ }
+ }
+ outmsg->iov_len = retlen;
+ret:
+ if (retlen == -1) {
+ iobuf_unref (iob);
+ iob = NULL;
+ }
+
+ return iob;
+}
+
+int
+server_submit_reply (call_frame_t *frame, rpcsvc_request_t *req, void *arg,
+ struct iovec *payload, int payloadcount,
+ struct iobref *iobref, xdrproc_t xdrproc)
+{
+ struct iobuf *iob = NULL;
+ int ret = -1;
+ struct iovec rsp = {0,};
+ server_state_t *state = NULL;
+ char new_iobref = 0;
+ client_t *client = NULL;
+ gf_boolean_t lk_heal = _gf_false;
+ server_conf_t *conf = NULL;
+ gf_barrier_t *barrier = NULL;
+ gf_barrier_payload_t *stub = NULL;
+ gf_boolean_t barriered = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("server", req, ret);
+
+ if (frame) {
+ state = CALL_STATE (frame);
+ frame->local = NULL;
+ client = frame->root->client;
+ conf = (server_conf_t *) client->this->private;
+ }
+
+ if (client)
+ lk_heal = ((server_conf_t *) client->this->private)->lk_heal;
+
+ if (!iobref) {
+ iobref = iobref_new ();
+ if (!iobref) {
+ goto ret;
+ }
+
+ new_iobref = 1;
+ }
+
+ iob = gfs_serialize_reply (req, arg, &rsp, xdrproc);
+ if (!iob) {
+ gf_log ("", GF_LOG_ERROR, "Failed to serialize reply");
+ goto ret;
+ }
+
+ iobref_add (iobref, iob);
+
+ if (conf)
+ barrier = conf->barrier;
+ if (barrier) {
+ /* todo: write's with fd flags set to O_SYNC and O_DIRECT */
+ LOCK (&barrier->lock);
+ {
+ if (is_fop_barriered (barrier->fops, req->procnum) &&
+ (barrier_add_to_queue (barrier))) {
+ stub = gf_barrier_payload (req, &rsp, frame,
+ payload,
+ payloadcount, iobref,
+ iob, new_iobref);
+ if (stub) {
+ gf_barrier_enqueue (barrier, stub);
+ barriered = _gf_true;
+ } else {
+ gf_log ("", GF_LOG_ERROR, "Failed to "
+ " barrier fop %"PRIu64,
+ ((uint64_t)1 << req->procnum));
+ }
+ }
+ }
+ UNLOCK (&barrier->lock);
+ if (barriered == _gf_true)
+ goto out;
+ }
+ /* Then, submit the message for transmission. */
+ ret = rpcsvc_submit_generic (req, &rsp, 1, payload, payloadcount,
+ iobref);
+
+ /* TODO: this is demo purpose only */
+ /* ret = rpcsvc_callback_submit (req->svc, req->trans, req->prog,
+ GF_CBK_NULL, &rsp, 1);
+ */
+ /* Now that we've done our job of handing the message to the RPC layer
+ * we can safely unref the iob in the hope that RPC layer must have
+ * ref'ed the iob on receiving into the txlist.
+ */
+ iobuf_unref (iob);
+ if (ret == -1) {
+ gf_log_callingfn ("", GF_LOG_ERROR, "Reply submission failed");
+ if (frame && client && !lk_heal) {
+ server_connection_cleanup (frame->this, client,
+ INTERNAL_LOCKS | POSIX_LOCKS);
+ } else {
+ gf_log_callingfn ("", GF_LOG_ERROR,
+ "Reply submission failed");
+ /* TODO: Failure of open(dir), create, inodelk, entrylk
+ or lk fops send failure must be handled specially. */
+ }
+ goto ret;
+ }
+
+ ret = 0;
+ret:
+ if (state) {
+ free_state (state);
+ }
+
+ if (frame) {
+ gf_client_unref (client);
+ STACK_DESTROY (frame->root);
+ }
+
+ if (new_iobref) {
+ iobref_unref (iobref);
+ }
+out:
+ return ret;
+}
+
+
+int
+server_priv_to_dict (xlator_t *this, dict_t *dict)
+{
+ server_conf_t *conf = NULL;
+ rpc_transport_t *xprt = NULL;
+ peer_info_t *peerinfo = NULL;
+ char key[32] = {0,};
+ int count = 0;
+ int ret = -1;
+
+ GF_VALIDATE_OR_GOTO (THIS->name, this, out);
+ GF_VALIDATE_OR_GOTO (THIS->name, dict, out);
+
+ conf = this->private;
+ if (!conf)
+ return 0;
+ //TODO: Dump only specific info to dict
+
+ pthread_mutex_lock (&conf->mutex);
+ {
+ list_for_each_entry (xprt, &conf->xprt_list, list) {
+ peerinfo = &xprt->peerinfo;
+ memset (key, 0, sizeof (key));
+ snprintf (key, sizeof (key), "client%d.hostname",
+ count);
+ ret = dict_set_str (dict, key, peerinfo->identifier);
+ if (ret)
+ goto unlock;
+
+ memset (key, 0, sizeof (key));
+ snprintf (key, sizeof (key), "client%d.bytesread",
+ count);
+ ret = dict_set_uint64 (dict, key,
+ xprt->total_bytes_read);
+ if (ret)
+ goto unlock;
+
+ memset (key, 0, sizeof (key));
+ snprintf (key, sizeof (key), "client%d.byteswrite",
+ count);
+ ret = dict_set_uint64 (dict, key,
+ xprt->total_bytes_write);
+ if (ret)
+ goto unlock;
+
+ count++;
+ }
+ }
+unlock:
+ pthread_mutex_unlock (&conf->mutex);
+ if (ret)
+ goto out;
+
+ ret = dict_set_int32 (dict, "clientcount", count);
+
+out:
+ return ret;
+}
+
+int
+server_priv (xlator_t *this)
+{
+ server_conf_t *conf = NULL;
+ rpc_transport_t *xprt = NULL;
+ char key[GF_DUMP_MAX_BUF_LEN] = {0,};
+ uint64_t total_read = 0;
+ uint64_t total_write = 0;
+ int32_t ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("server", this, out);
+
+ conf = this->private;
+ if (!conf)
+ return 0;
+
+ gf_proc_dump_build_key (key, "xlator.protocol.server", "priv");
+ gf_proc_dump_add_section (key);
+
+ ret = pthread_mutex_trylock (&conf->mutex);
+ if (ret != 0)
+ goto out;
+ {
+ list_for_each_entry (xprt, &conf->xprt_list, list) {
+ total_read += xprt->total_bytes_read;
+ total_write += xprt->total_bytes_write;
+ }
+ }
+ pthread_mutex_unlock (&conf->mutex);
+
+ gf_proc_dump_build_key(key, "server", "total-bytes-read");
+ gf_proc_dump_write(key, "%"PRIu64, total_read);
+
+ gf_proc_dump_build_key(key, "server", "total-bytes-write");
+ gf_proc_dump_write(key, "%"PRIu64, total_write);
+
+ ret = 0;
+out:
+ if (ret)
+ gf_proc_dump_write ("Unable to print priv",
+ "(Lock acquisition failed) %s",
+ this?this->name:"server");
+
+ return ret;
+}
+
+
+static int
+get_auth_types (dict_t *this, char *key, data_t *value, void *data)
+{
+ dict_t *auth_dict = NULL;
+ char *saveptr = NULL;
+ char *tmp = NULL;
+ char *key_cpy = NULL;
+ int32_t ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("server", this, out);
+ GF_VALIDATE_OR_GOTO ("server", key, out);
+ GF_VALIDATE_OR_GOTO ("server", data, out);
+
+ auth_dict = data;
+ key_cpy = gf_strdup (key);
+ GF_VALIDATE_OR_GOTO("server", key_cpy, out);
+
+ tmp = strtok_r (key_cpy, ".", &saveptr);
+ ret = strcmp (tmp, "auth");
+ if (ret == 0) {
+ tmp = strtok_r (NULL, ".", &saveptr);
+ if (strcmp (tmp, "ip") == 0) {
+ /* TODO: backward compatibility, remove when
+ newer versions are available */
+ tmp = "addr";
+ gf_log ("server", GF_LOG_WARNING,
+ "assuming 'auth.ip' to be 'auth.addr'");
+ }
+ ret = dict_set_dynptr (auth_dict, tmp, NULL, 0);
+ if (ret < 0) {
+ gf_log ("server", GF_LOG_DEBUG,
+ "failed to dict_set_dynptr");
+ }
+ }
+
+ GF_FREE (key_cpy);
+out:
+ return 0;
+}
+
+int
+_check_for_auth_option (dict_t *d, char *k, data_t *v,
+ void *tmp)
+{
+ int ret = 0;
+ xlator_t *xl = NULL;
+ char *tail = NULL;
+ char *tmp_addr_list = NULL;
+ char *addr = NULL;
+ char *tmp_str = NULL;
+
+ xl = tmp;
+
+ tail = strtail (k, "auth.");
+ if (!tail)
+ goto out;
+
+ /* fast fwd thru module type */
+ tail = strchr (tail, '.');
+ if (!tail)
+ goto out;
+ tail++;
+
+ tail = strtail (tail, xl->name);
+ if (!tail)
+ goto out;
+
+ if (*tail == '.') {
+ /* when we are here, the key is checked for
+ * valid auth.allow.<xlator>
+ * Now we verify the ip address
+ */
+ if (!strcmp (v->data, "*")) {
+ ret = 0;
+ goto out;
+ }
+
+ tmp_addr_list = gf_strdup (v->data);
+ addr = strtok_r (tmp_addr_list, ",", &tmp_str);
+ if (!addr)
+ addr = v->data;
+
+ while (addr) {
+ if (valid_internet_address (addr, _gf_true)) {
+ ret = 0;
+ } else {
+ ret = -1;
+ gf_log (xl->name, GF_LOG_ERROR,
+ "internet address '%s'"
+ " does not conform to"
+ " standards.", addr);
+ goto out;
+ }
+ if (tmp_str)
+ addr = strtok_r (NULL, ",", &tmp_str);
+ else
+ addr = NULL;
+ }
+
+ GF_FREE (tmp_addr_list);
+ tmp_addr_list = NULL;
+ }
+out:
+ return ret;
+}
+
+int
+validate_auth_options (xlator_t *this, dict_t *dict)
+{
+ int error = -1;
+ xlator_list_t *trav = NULL;
+
+ GF_VALIDATE_OR_GOTO ("server", this, out);
+ GF_VALIDATE_OR_GOTO ("server", dict, out);
+
+ trav = this->children;
+ while (trav) {
+ error = dict_foreach (dict, _check_for_auth_option,
+ trav->xlator);
+
+ if (-1 == error) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "volume '%s' defined as subvolume, but no "
+ "authentication defined for the same",
+ trav->xlator->name);
+ break;
+ }
+ trav = trav->next;
+ }
+
+out:
+ return error;
+}
+
+
+int
+server_rpc_notify (rpcsvc_t *rpc, void *xl, rpcsvc_event_t event,
+ void *data)
+{
+ gf_boolean_t detached = _gf_false;
+ xlator_t *this = NULL;
+ rpc_transport_t *trans = NULL;
+ server_conf_t *conf = NULL;
+ client_t *client = NULL;
+ server_ctx_t *serv_ctx = NULL;
+
+ if (!xl || !data) {
+ gf_log_callingfn ("server", GF_LOG_WARNING,
+ "Calling rpc_notify without initializing");
+ goto out;
+ }
+
+ this = xl;
+ trans = data;
+ conf = this->private;
+
+ switch (event) {
+ case RPCSVC_EVENT_ACCEPT:
+ {
+ /* Have a structure per new connection */
+ /* TODO: Should we create anything here at all ? * /
+ client->conn = create_server_conn_state (this, trans);
+ if (!client->conn)
+ goto out;
+
+ trans->protocol_private = client->conn;
+ */
+ INIT_LIST_HEAD (&trans->list);
+
+ pthread_mutex_lock (&conf->mutex);
+ {
+ list_add_tail (&trans->list, &conf->xprt_list);
+ }
+ pthread_mutex_unlock (&conf->mutex);
+
+ break;
+ }
+ case RPCSVC_EVENT_DISCONNECT:
+ /* transport has to be removed from the list upon disconnect
+ * irrespective of whether lock self heal is off or on, since
+ * new transport will be created upon reconnect.
+ */
+ pthread_mutex_lock (&conf->mutex);
+ {
+ list_del_init (&trans->list);
+ }
+ pthread_mutex_unlock (&conf->mutex);
+
+ client = trans->xl_private;
+ if (!client)
+ break;
+
+ gf_log (this->name, GF_LOG_INFO, "disconnecting connection"
+ "from %s", client->client_uid);
+
+ /* If lock self heal is off, then destroy the
+ conn object, else register a grace timer event */
+ if (!conf->lk_heal) {
+ gf_client_ref (client);
+ gf_client_put (client, &detached);
+ if (detached)
+ server_connection_cleanup (this, client,
+ INTERNAL_LOCKS | POSIX_LOCKS);
+ gf_client_unref (client);
+ break;
+ }
+ trans->xl_private = NULL;
+ server_connection_cleanup (this, client, INTERNAL_LOCKS);
+
+ serv_ctx = server_ctx_get (client, this);
+
+ if (serv_ctx == NULL) {
+ gf_log (this->name, GF_LOG_INFO,
+ "server_ctx_get() failed");
+ goto out;
+ }
+
+ LOCK (&serv_ctx->fdtable_lock);
+ {
+ if (!serv_ctx->grace_timer) {
+
+ gf_log (this->name, GF_LOG_INFO,
+ "starting a grace timer for %s",
+ client->client_uid);
+
+ serv_ctx->grace_timer =
+ gf_timer_call_after (this->ctx,
+ conf->grace_ts,
+ grace_time_handler,
+ client);
+ }
+ }
+ UNLOCK (&serv_ctx->fdtable_lock);
+ break;
+ case RPCSVC_EVENT_TRANSPORT_DESTROY:
+ /*- conn obj has been disassociated from trans on first
+ * disconnect.
+ * conn cleanup and destruction is handed over to
+ * grace_time_handler or the subsequent handler that 'owns'
+ * the conn. Nothing left to be done here. */
+ break;
+ default:
+ break;
+ }
+
+out:
+ return 0;
+}
+
+int32_t
+mem_acct_init (xlator_t *this)
+{
+ int ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("server", this, out);
+
+ ret = xlator_mem_acct_init (this, gf_server_mt_end + 1);
+
+ if (ret != 0) {
+ gf_log (this->name, GF_LOG_ERROR, "Memory accounting init"
+ "failed");
+ return ret;
+ }
+out:
+ return ret;
+}
+
+
+static int
+_delete_auth_opt (dict_t *this, char *key, data_t *value, void *data)
+{
+ char *auth_option_pattern[] = { "auth.addr.*.allow",
+ "auth.addr.*.reject",
+ NULL};
+ int i = 0;
+
+ for (i = 0; auth_option_pattern[i]; i++) {
+ if (fnmatch (auth_option_pattern[i], key, 0) == 0) {
+ dict_del (this, key);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+_copy_auth_opt (dict_t *unused, char *key, data_t *value, void *xl_dict)
+{
+ char *auth_option_pattern[] = { "auth.addr.*.allow",
+ "auth.addr.*.reject",
+ NULL};
+ int i = 0;
+
+ for (i = 0; auth_option_pattern [i]; i++) {
+ if (fnmatch (auth_option_pattern[i], key, 0) == 0) {
+ dict_set ((dict_t *)xl_dict, key, value);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+server_init_grace_timer (xlator_t *this, dict_t *options,
+ server_conf_t *conf)
+{
+ int32_t ret = -1;
+ int32_t grace_timeout = -1;
+ char *lk_heal = NULL;
+
+ GF_VALIDATE_OR_GOTO ("server", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, options, out);
+ GF_VALIDATE_OR_GOTO (this->name, conf, out);
+
+ conf->lk_heal = _gf_false;
+
+ ret = dict_get_str (options, "lk-heal", &lk_heal);
+ if (!ret)
+ gf_string2boolean (lk_heal, &conf->lk_heal);
+
+ gf_log (this->name, GF_LOG_DEBUG, "lk-heal = %s",
+ (conf->lk_heal) ? "on" : "off");
+
+ ret = dict_get_int32 (options, "grace-timeout", &grace_timeout);
+ if (!ret)
+ conf->grace_ts.tv_sec = grace_timeout;
+ else
+ conf->grace_ts.tv_sec = 10;
+
+ gf_log (this->name, GF_LOG_DEBUG, "Server grace timeout "
+ "value = %"PRIu64, conf->grace_ts.tv_sec);
+
+ conf->grace_ts.tv_nsec = 0;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+int
+reconfigure (xlator_t *this, dict_t *options)
+{
+
+ server_conf_t *conf =NULL;
+ rpcsvc_t *rpc_conf;
+ rpcsvc_listener_t *listeners;
+ int inode_lru_limit;
+ gf_boolean_t trace;
+ data_t *data;
+ int ret = 0;
+ char *statedump_path = NULL;
+ conf = this->private;
+
+ if (!conf) {
+ gf_log_callingfn (this->name, GF_LOG_DEBUG, "conf == null!!!");
+ goto out;
+ }
+ if (dict_get_int32 ( options, "inode-lru-limit", &inode_lru_limit) == 0){
+ conf->inode_lru_limit = inode_lru_limit;
+ gf_log (this->name, GF_LOG_TRACE, "Reconfigured inode-lru-limit"
+ " to %d", conf->inode_lru_limit);
+ }
+
+ data = dict_get (options, "trace");
+ if (data) {
+ ret = gf_string2boolean (data->data, &trace);
+ if (ret != 0) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "'trace' takes on only boolean values. "
+ "Neglecting option");
+ ret = -1;
+ goto out;
+ }
+ conf->trace = trace;
+ gf_log (this->name, GF_LOG_TRACE, "Reconfigured trace"
+ " to %d", conf->trace);
+
+ }
+
+ GF_OPTION_RECONF ("statedump-path", statedump_path,
+ options, path, out);
+ if (!statedump_path) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Error while reconfiguring statedump path");
+ ret = -1;
+ goto out;
+ }
+ gf_path_strip_trailing_slashes (statedump_path);
+ GF_FREE (this->ctx->statedump_path);
+ this->ctx->statedump_path = gf_strdup (statedump_path);
+
+ if (!conf->auth_modules)
+ conf->auth_modules = dict_new ();
+
+ dict_foreach (options, get_auth_types, conf->auth_modules);
+ ret = validate_auth_options (this, options);
+ if (ret == -1) {
+ /* logging already done in validate_auth_options function. */
+ goto out;
+ }
+ dict_foreach (this->options, _delete_auth_opt, this->options);
+ dict_foreach (options, _copy_auth_opt, this->options);
+
+ ret = gf_auth_init (this, conf->auth_modules);
+ if (ret) {
+ dict_unref (conf->auth_modules);
+ goto out;
+ }
+
+ rpc_conf = conf->rpc;
+ if (!rpc_conf) {
+ gf_log (this->name, GF_LOG_ERROR, "No rpc_conf !!!!");
+ goto out;
+ }
+
+ (void) rpcsvc_set_allow_insecure (rpc_conf, options);
+ (void) rpcsvc_set_root_squash (rpc_conf, options);
+ (void) rpcsvc_set_outstanding_rpc_limit (rpc_conf, options);
+ list_for_each_entry (listeners, &(rpc_conf->listeners), list) {
+ if (listeners->trans != NULL) {
+ if (listeners->trans->reconfigure )
+ listeners->trans->reconfigure (listeners->trans, options);
+ else
+ gf_log (this->name, GF_LOG_ERROR,
+ "Reconfigure not found for transport" );
+ }
+ }
+ ret = server_init_grace_timer (this, options, conf);
+
+out:
+ gf_log ("", GF_LOG_DEBUG, "returning %d", ret);
+ return ret;
+}
+
+static int32_t
+client_destroy_cbk (xlator_t *this, client_t *client)
+{
+ void *tmp = NULL;
+ server_ctx_t *ctx = NULL;
+
+ client_ctx_del (client, this, &tmp);
+
+ ctx = tmp;
+
+ if (ctx == NULL)
+ return 0;
+
+ gf_fd_fdtable_destroy (ctx->fdtable);
+ LOCK_DESTROY (&ctx->fdtable_lock);
+ GF_FREE (ctx);
+
+ return 0;
+}
+
+int
+init (xlator_t *this)
+{
+ int32_t ret = -1;
+ server_conf_t *conf = NULL;
+ rpcsvc_listener_t *listener = NULL;
+ char *statedump_path = NULL;
+ gf_barrier_t *barrier = NULL;
+ char *str = NULL;
+ GF_VALIDATE_OR_GOTO ("init", this, out);
+
+ if (this->children == NULL) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "protocol/server should have subvolume");
+ goto out;
+ }
+
+ if (this->parents != NULL) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "protocol/server should not have parent volumes");
+ goto out;
+ }
+
+ conf = GF_CALLOC (1, sizeof (server_conf_t),
+ gf_server_mt_server_conf_t);
+
+ GF_VALIDATE_OR_GOTO(this->name, conf, out);
+
+ INIT_LIST_HEAD (&conf->xprt_list);
+ pthread_mutex_init (&conf->mutex, NULL);
+
+ ret = server_init_grace_timer (this, this->options, conf);
+ if (ret)
+ goto out;
+
+ ret = server_build_config (this, conf);
+ if (ret)
+ goto out;
+
+ ret = dict_get_str (this->options, "config-directory", &conf->conf_dir);
+ if (ret)
+ conf->conf_dir = CONFDIR;
+
+ /*ret = dict_get_str (this->options, "statedump-path", &statedump_path);
+ if (!ret) {
+ gf_path_strip_trailing_slashes (statedump_path);
+ this->ctx->statedump_path = statedump_path;
+ }*/
+ GF_OPTION_INIT ("statedump-path", statedump_path, path, out);
+ if (statedump_path) {
+ gf_path_strip_trailing_slashes (statedump_path);
+ this->ctx->statedump_path = gf_strdup (statedump_path);
+ } else {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Error setting statedump path");
+ ret = -1;
+ goto out;
+ }
+
+ /* Authentication modules */
+ conf->auth_modules = dict_new ();
+ GF_VALIDATE_OR_GOTO(this->name, conf->auth_modules, out);
+
+ dict_foreach (this->options, get_auth_types, conf->auth_modules);
+ ret = validate_auth_options (this, this->options);
+ if (ret == -1) {
+ /* logging already done in validate_auth_options function. */
+ goto out;
+ }
+
+ ret = gf_auth_init (this, conf->auth_modules);
+ if (ret) {
+ dict_unref (conf->auth_modules);
+ goto out;
+ }
+
+ /* RPC related */
+ conf->rpc = rpcsvc_init (this, this->ctx, this->options, 0);
+ if (conf->rpc == NULL) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "creation of rpcsvc failed");
+ ret = -1;
+ goto out;
+ }
+
+ ret = rpcsvc_create_listeners (conf->rpc, this->options,
+ this->name);
+ if (ret < 1) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "creation of listener failed");
+ ret = -1;
+ goto out;
+ }
+
+ ret = rpcsvc_register_notify (conf->rpc, server_rpc_notify, this);
+ if (ret) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "registration of notify with rpcsvc failed");
+ goto out;
+ }
+
+ glusterfs3_3_fop_prog.options = this->options;
+ ret = rpcsvc_program_register (conf->rpc, &glusterfs3_3_fop_prog);
+ if (ret) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "registration of program (name:%s, prognum:%d, "
+ "progver:%d) failed", glusterfs3_3_fop_prog.progname,
+ glusterfs3_3_fop_prog.prognum,
+ glusterfs3_3_fop_prog.progver);
+ goto out;
+ }
+
+ gluster_handshake_prog.options = this->options;
+ ret = rpcsvc_program_register (conf->rpc, &gluster_handshake_prog);
+ if (ret) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "registration of program (name:%s, prognum:%d, "
+ "progver:%d) failed", gluster_handshake_prog.progname,
+ gluster_handshake_prog.prognum,
+ gluster_handshake_prog.progver);
+ rpcsvc_program_unregister (conf->rpc, &glusterfs3_3_fop_prog);
+ goto out;
+ }
+
+#ifndef GF_DARWIN_HOST_OS
+ {
+ struct rlimit lim;
+
+ lim.rlim_cur = 1048576;
+ lim.rlim_max = 1048576;
+
+ if (setrlimit (RLIMIT_NOFILE, &lim) == -1) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "WARNING: Failed to set 'ulimit -n 1M': %s",
+ strerror(errno));
+ lim.rlim_cur = 65536;
+ lim.rlim_max = 65536;
+
+ if (setrlimit (RLIMIT_NOFILE, &lim) == -1) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "Failed to set max open fd to 64k: %s",
+ strerror(errno));
+ } else {
+ gf_log (this->name, GF_LOG_TRACE,
+ "max open fd set to 64k");
+ }
+ }
+ }
+#endif
+ /* barrier related */
+ barrier = GF_CALLOC (1, sizeof (*barrier),1);
+ if (!barrier) {
+ gf_log (this->name, GF_LOG_WARNING,
+ "WARNING: Failed to allocate barrier");
+ ret = -1;
+ goto out;
+ }
+
+ LOCK_INIT (&barrier->lock);
+ INIT_LIST_HEAD (&barrier->queue);
+ barrier->on = _gf_false;
+
+ GF_OPTION_INIT ("barrier-queue-length", barrier->max_size,
+ int64, out);
+ GF_OPTION_INIT ("barrier-timeout", barrier->time_out,
+ uint64, out);
+
+ ret = dict_get_str (this->options, "barrier-fops", &str);
+ if (ret) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "setting barrier fops to default value");
+ }
+ ret = gf_barrier_fops_configure (this, barrier, str);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "invalid barrier fops specified");
+ goto out;
+ }
+
+ conf->barrier = barrier;
+ this->private = conf;
+
+ ret = 0;
+out:
+ if (ret) {
+ if (this != NULL) {
+ this->fini (this);
+ }
+
+ if (listener != NULL) {
+ rpcsvc_listener_destroy (listener);
+ }
+ }
+
+ return ret;
+}
+
+
+void
+fini (xlator_t *this)
+{
+#if 0
+ server_conf_t *conf = NULL;
+
+ conf = this->private;
+
+ if (conf) {
+ if (conf->rpc) {
+ /* TODO: memory leak here, have to free RPC */
+ /*
+ if (conf->rpc->conn) {
+ rpcsvc_conn_destroy (conf->rpc->conn);
+ }
+ rpcsvc_fini (conf->rpc);
+ */
+ ;
+ }
+
+ if (conf->auth_modules)
+ dict_unref (conf->auth_modules);
+
+ GF_FREE (conf);
+ }
+
+ this->private = NULL;
+#endif
+ return;
+}
+
+int
+notify (xlator_t *this, int32_t event, void *data, ...)
+{
+ int ret = 0;
+ int32_t val = 0;
+ dict_t *dict = NULL;
+ dict_t *output = NULL;
+ va_list ap;
+
+ dict = data;
+ va_start (ap, data);
+ output = va_arg (ap, dict_t*);
+ va_end (ap);
+
+ switch (event) {
+ case GF_EVENT_VOLUME_BARRIER_OP:
+ ret = dict_get_int32 (dict, "barrier", &val);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Wrong BARRIER event");
+ goto out;
+ }
+ /* !val un-barrier, if val, barrier */
+ if (val) {
+ ret = gf_barrier_start (this);
+ if (ret)
+ gf_log (this->name, GF_LOG_ERROR,
+ "Barrier start failed");
+ } else {
+ ret = gf_barrier_stop (this);
+ if (ret)
+ gf_log (this->name, GF_LOG_ERROR,
+ "Barrier stop failed");
+ }
+ ret = dict_set_int32 (output, "barrier-status", ret);
+ if (ret)
+ gf_log (this->name, GF_LOG_ERROR,
+ "Failed to set barrier-status in dict");
+ break;
+
+ /* todo: call default_notify to make other xlators handle it.*/
+ default:
+ default_notify (this, event, data);
+ break;
+ }
+out:
+ return ret;
+}
+
+
+struct xlator_fops fops;
+
+struct xlator_cbks cbks = {
+ .client_destroy = client_destroy_cbk,
+};
+
+struct xlator_dumpops dumpops = {
+ .priv = server_priv,
+ .fd = gf_client_dump_fdtables,
+ .inode = gf_client_dump_inodes,
+ .priv_to_dict = server_priv_to_dict,
+ .fd_to_dict = gf_client_dump_fdtables_to_dict,
+ .inode_to_dict = gf_client_dump_inodes_to_dict,
+};
+
+
+struct volume_options options[] = {
+ { .key = {"transport-type"},
+ .value = {"rpc", "rpc-over-rdma", "tcp", "socket", "ib-verbs",
+ "unix", "ib-sdp", "tcp/server", "ib-verbs/server", "rdma",
+ "rdma*([ \t]),*([ \t])socket",
+ "rdma*([ \t]),*([ \t])tcp",
+ "tcp*([ \t]),*([ \t])rdma",
+ "socket*([ \t]),*([ \t])rdma"},
+ .type = GF_OPTION_TYPE_STR
+ },
+ { .key = {"volume-filename.*"},
+ .type = GF_OPTION_TYPE_PATH,
+ },
+ { .key = {"transport.*"},
+ .type = GF_OPTION_TYPE_ANY,
+ },
+ { .key = {"rpc*"},
+ .type = GF_OPTION_TYPE_ANY,
+ },
+ { .key = {"inode-lru-limit"},
+ .type = GF_OPTION_TYPE_INT,
+ .min = 0,
+ .max = (1 * GF_UNIT_MB),
+ .default_value = "16384",
+ .description = "Specifies the maximum megabytes of memory to be "
+ "used in the inode cache."
+ },
+ { .key = {"verify-volfile-checksum"},
+ .type = GF_OPTION_TYPE_BOOL
+ },
+ { .key = {"trace"},
+ .type = GF_OPTION_TYPE_BOOL
+ },
+ { .key = {"config-directory",
+ "conf-dir"},
+ .type = GF_OPTION_TYPE_PATH,
+ },
+ { .key = {"rpc-auth-allow-insecure"},
+ .type = GF_OPTION_TYPE_BOOL,
+ },
+ { .key = {"root-squash"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .default_value = "off",
+ .description = "Map requests from uid/gid 0 to the anonymous "
+ "uid/gid. Note that this does not apply to any other"
+ "uids or gids that might be equally sensitive, such as"
+ "user bin or group staff."
+ },
+ { .key = {"statedump-path"},
+ .type = GF_OPTION_TYPE_PATH,
+ .default_value = DEFAULT_VAR_RUN_DIRECTORY,
+ .description = "Specifies directory in which gluster should save its"
+ " statedumps. By default it is the /tmp directory"
+ },
+ { .key = {"lk-heal"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .default_value = "off",
+ },
+ {.key = {"grace-timeout"},
+ .type = GF_OPTION_TYPE_INT,
+ .min = 10,
+ .max = 1800,
+ },
+ {.key = {"tcp-window-size"},
+ .type = GF_OPTION_TYPE_SIZET,
+ .min = GF_MIN_SOCKET_WINDOW_SIZE,
+ .max = GF_MAX_SOCKET_WINDOW_SIZE,
+ .description = "Specifies the window size for tcp socket."
+ },
+
+ /* The following two options are defined in addr.c, redifined here *
+ * for the sake of validation during volume set from cli */
+
+ { .key = {"auth.addr.*.allow"},
+ .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST,
+ .description = "Allow a comma separated list of addresses and/or "
+ "hostnames to connect to the server. By default, all"
+ " connections are allowed."
+ },
+ { .key = {"auth.addr.*.reject"},
+ .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST,
+ .description = "Reject a comma separated list of addresses and/or "
+ "hostnames to connect to the server. By default, all"
+ " connections are allowed."
+ },
+ {.key = {"barrier-timeout"},
+ .type = GF_OPTION_TYPE_INT,
+ .default_value = "60",
+ .min = 0,
+ .max = 360,
+ .description = "Barrier timeout in seconds",
+ },
+ {.key = {"barrier-queue-length"},
+ .type = GF_OPTION_TYPE_INT,
+ .default_value = "4096",
+ .min = 0,
+ .max = 16384,
+ .description = "Barrier queue length",
+ },
+ {.key = {"barrier-fops"},
+ .type = GF_OPTION_TYPE_STR,
+ .description = "Allow a comma seperated fop lists",
+ },
+ { .key = {NULL} },
+};