/*
   Copyright (c) 2006-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.
*/

#include <time.h>
#include <grp.h>
#include <sys/uio.h>
#include <sys/resource.h>

#include <libgen.h>
#include <glusterfs/compat-uuid.h>

#include "glusterd.h"
#include "rpcsvc.h"
#include "fnmatch.h"
#include <glusterfs/xlator.h>
#include <glusterfs/call-stub.h>
#include <glusterfs/defaults.h>
#include <glusterfs/list.h>
#include <glusterfs/dict.h>
#include <glusterfs/options.h>
#include <glusterfs/compat.h>
#include <glusterfs/compat-errno.h>
#include <glusterfs/syscall.h>
#include "glusterd-statedump.h"
#include "glusterd-sm.h"
#include "glusterd-op-sm.h"
#include "glusterd-store.h"
#include "glusterd-hooks.h"
#include "glusterd-utils.h"
#include "glusterd-locks.h"
#include "glusterd-svc-mgmt.h"
#include "glusterd-shd-svc.h"
#ifdef BUILD_GNFS
#include "glusterd-nfs-svc.h"
#endif
#include "glusterd-bitd-svc.h"
#include "glusterd-scrub-svc.h"
#include "glusterd-quotad-svc.h"
#include "glusterd-snapd-svc.h"
#include "glusterd-messages.h"
#include <glusterfs/common-utils.h>
#include "glusterd-geo-rep.h"
#include <glusterfs/run.h>
#include "rpc-clnt-ping.h"
#include "rpc-common-xdr.h"

#include <glusterfs/syncop.h>

#include "glusterd-mountbroker.h"

extern struct rpcsvc_program gluster_handshake_prog;
extern struct rpcsvc_program gluster_cli_getspec_prog;
extern struct rpcsvc_program gluster_pmap_prog;
extern glusterd_op_info_t opinfo;
extern struct rpcsvc_program gd_svc_mgmt_prog;
extern struct rpcsvc_program gd_svc_mgmt_v3_prog;
extern struct rpcsvc_program gd_svc_peer_prog;
extern struct rpcsvc_program gd_svc_cli_prog;
extern struct rpcsvc_program gd_svc_cli_trusted_progs;
extern struct rpc_clnt_program gd_brick_prog;
extern struct rpcsvc_program glusterd_mgmt_hndsk_prog;

extern char snap_mount_dir[VALID_GLUSTERD_PATHMAX];

rpcsvc_cbk_program_t glusterd_cbk_prog = {
    .progname = "Gluster Callback",
    .prognum = GLUSTER_CBK_PROGRAM,
    .progver = GLUSTER_CBK_VERSION,
};

struct rpcsvc_program *gd_inet_programs[] = {
    &gd_svc_peer_prog,       &gd_svc_cli_trusted_progs, /* Must be index 1 for
                                                           secure_mgmt! */
    &gd_svc_mgmt_prog,       &gd_svc_mgmt_v3_prog,      &gluster_pmap_prog,
    &gluster_handshake_prog, &glusterd_mgmt_hndsk_prog,
};
int gd_inet_programs_count = (sizeof(gd_inet_programs) /
                              sizeof(gd_inet_programs[0]));

struct rpcsvc_program *gd_uds_programs[] = {
    &gd_svc_cli_prog,
    &gluster_cli_getspec_prog,
};
int gd_uds_programs_count = (sizeof(gd_uds_programs) /
                             sizeof(gd_uds_programs[0]));

const char *gd_op_list[GD_OP_MAX + 1] = {
    [GD_OP_NONE] = "Invalid op",
    [GD_OP_CREATE_VOLUME] = "Create",
    [GD_OP_START_BRICK] = "Start Brick",
    [GD_OP_STOP_BRICK] = "Stop Brick",
    [GD_OP_DELETE_VOLUME] = "Delete",
    [GD_OP_START_VOLUME] = "Start",
    [GD_OP_STOP_VOLUME] = "Stop",
    [GD_OP_DEFRAG_VOLUME] = "Rebalance",
    [GD_OP_ADD_BRICK] = "Add brick",
    [GD_OP_DETACH_TIER] = "Detach tier",
    [GD_OP_TIER_MIGRATE] = "Tier migration",
    [GD_OP_REMOVE_BRICK] = "Remove brick",
    [GD_OP_REPLACE_BRICK] = "Replace brick",
    [GD_OP_SET_VOLUME] = "Set",
    [GD_OP_RESET_VOLUME] = "Reset",
    [GD_OP_SYNC_VOLUME] = "Sync",
    [GD_OP_LOG_ROTATE] = "Log rotate",
    [GD_OP_GSYNC_SET] = "Geo-replication",
    [GD_OP_PROFILE_VOLUME] = "Profile",
    [GD_OP_QUOTA] = "Quota",
    [GD_OP_STATUS_VOLUME] = "Status",
    [GD_OP_REBALANCE] = "Rebalance",
    [GD_OP_HEAL_VOLUME] = "Heal",
    [GD_OP_STATEDUMP_VOLUME] = "Statedump",
    [GD_OP_LIST_VOLUME] = "Lists",
    [GD_OP_CLEARLOCKS_VOLUME] = "Clear locks",
    [GD_OP_DEFRAG_BRICK_VOLUME] = "Rebalance",
    [GD_OP_COPY_FILE] = "Copy File",
    [GD_OP_SYS_EXEC] = "Execute system commands",
    [GD_OP_GSYNC_CREATE] = "Geo-replication Create",
    [GD_OP_SNAP] = "Snapshot",
    [GD_OP_RESET_BRICK] = "Reset Brick",
    [GD_OP_MAX_OPVERSION] = "Maximum supported op-version",
    [GD_OP_MAX] = "Invalid op"};

static int
glusterd_opinfo_init()
{
    int32_t ret = -1;

    opinfo.op = GD_OP_NONE;

    return ret;
}

int
glusterd_uuid_init()
{
    int ret = -1;
    xlator_t *this = THIS;
    glusterd_conf_t *priv = NULL;

    priv = this->private;
    GF_ASSERT(priv);

    ret = glusterd_retrieve_uuid();
    if (ret == 0) {
        gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_RETRIEVED_UUID,
               "retrieved UUID: %s", uuid_utoa(priv->uuid));
        return 0;
    }

    ret = glusterd_uuid_generate_save();

    if (ret) {
        gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_UUID_GEN_STORE_FAIL,
               "Unable to generate and save new UUID");
        return ret;
    }

    return 0;
}

int
glusterd_uuid_generate_save()
{
    int ret = -1;
    glusterd_conf_t *priv = NULL;
    xlator_t *this = THIS;

    priv = this->private;
    GF_ASSERT(priv);

    gf_uuid_generate(priv->uuid);

    gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_GENERATED_UUID,
           "generated UUID: %s", uuid_utoa(priv->uuid));

    ret = glusterd_store_global_info(this);

    if (ret)
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_UUID_STORE_FAIL,
               "Unable to store the generated uuid %s", uuid_utoa(priv->uuid));

    return ret;
}

int
glusterd_options_init(xlator_t *this)
{
    int ret = -1;
    glusterd_conf_t *priv = NULL;
    char *initial_version = "0";

    priv = this->private;

    priv->opts = dict_new();
    if (!priv->opts) {
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_DICT_CREATE_FAIL, NULL);
        goto out;
    }

    ret = glusterd_store_retrieve_options(this);
    if (ret == 0) {
        goto out;
    }

    ret = dict_set_str(priv->opts, GLUSTERD_GLOBAL_OPT_VERSION,
                       initial_version);
    if (ret)
        goto out;

    ret = glusterd_store_options(this, priv->opts);
    if (ret) {
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_VERS_STORE_FAIL,
               "Unable to store version");
        return ret;
    }
out:
    return 0;
}

int
glusterd_client_statedump_submit_req(char *volname, char *target_ip, char *pid)
{
    gf_statedump statedump_req = {
        0,
    };
    glusterd_conf_t *conf = NULL;
    int ret = 0;
    char *end_ptr = NULL;
    rpc_transport_t *trans = NULL;
    char *ip_addr = NULL;
    xlator_t *this = THIS;
    char tmp[UNIX_PATH_MAX] = {
        0,
    };

    conf = this->private;
    GF_ASSERT(conf);

    if (target_ip == NULL || pid == NULL) {
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_INVALID_ARGUMENT, NULL);
        ret = -1;
        goto out;
    }

    statedump_req.pid = strtol(pid, &end_ptr, 10);

    gf_msg_debug(this->name, 0,
                 "Performing statedump on volume %s "
                 "client with pid:%d host:%s",
                 volname, statedump_req.pid, target_ip);

    pthread_mutex_lock(&conf->xprt_lock);
    {
        list_for_each_entry(trans, &conf->xprt_list, list)
        {
            /* check if this connection matches "all" or the
             * volname */
            if (strncmp(volname, "all", NAME_MAX) &&
                strncmp(trans->peerinfo.volname, volname, NAME_MAX)) {
                /* no match, try next trans */
                continue;
            }

            strcpy(tmp, trans->peerinfo.identifier);
            ip_addr = strtok(tmp, ":");
            if (gf_is_same_address(ip_addr, target_ip)) {
                /* Every gluster client would have
                 * connected to glusterd(volfile server). This
                 * connection is used to send the statedump
                 * request rpc to the application.
                 */
                gf_msg_trace(this->name, 0,
                             "Submitting "
                             "statedump rpc request for %s",
                             trans->peerinfo.identifier);
                rpcsvc_request_submit(conf->rpc, trans, &glusterd_cbk_prog,
                                      GF_CBK_STATEDUMP, &statedump_req,
                                      this->ctx, (xdrproc_t)xdr_gf_statedump);
            }
        }
    }
    pthread_mutex_unlock(&conf->xprt_lock);
out:
    return ret;
}

int
glusterd_fetchspec_notify(xlator_t *this)
{
    int ret = -1;
    glusterd_conf_t *priv = NULL;
    rpc_transport_t *trans = NULL;

    priv = this->private;

    pthread_mutex_lock(&priv->xprt_lock);
    {
        list_for_each_entry(trans, &priv->xprt_list, list)
        {
            rpcsvc_callback_submit(priv->rpc, trans, &glusterd_cbk_prog,
                                   GF_CBK_FETCHSPEC, NULL, 0, NULL);
        }
    }
    pthread_mutex_unlock(&priv->xprt_lock);

    ret = 0;

    return ret;
}

int
glusterd_fetchsnap_notify(xlator_t *this)
{
    int ret = -1;
    glusterd_conf_t *priv = NULL;
    rpc_transport_t *trans = NULL;

    priv = this->private;

    /*
     * TODO: As of now, the identification of the rpc clients in the
     * handshake protocol is not there. So among so many glusterfs processes
     * registered with glusterd, it is hard to identify one particular
     * process (in this particular case, the snap daemon). So the callback
     * notification is sent to all the transports from the transport list.
     * Only those processes which have a rpc client registered for this
     * callback will respond to the notification. Once the identification
     * of the rpc clients becomes possible, the below section can be changed
     * to send callback notification to only those rpc clients, which have
     * registered.
     */
    pthread_mutex_lock(&priv->xprt_lock);
    {
        list_for_each_entry(trans, &priv->xprt_list, list)
        {
            rpcsvc_callback_submit(priv->rpc, trans, &glusterd_cbk_prog,
                                   GF_CBK_GET_SNAPS, NULL, 0, NULL);
        }
    }
    pthread_mutex_unlock(&priv->xprt_lock);

    ret = 0;

    return ret;
}

int32_t
mem_acct_init(xlator_t *this)
{
    int ret = -1;

    if (!this)
        return ret;

    ret = xlator_mem_acct_init(this, gf_gld_mt_end);

    if (ret != 0) {
        gf_msg(this->name, GF_LOG_ERROR, ENOMEM, GD_MSG_NO_MEMORY,
               "Memory accounting init"
               " failed");
        return ret;
    }

    return ret;
}

int
glusterd_rpcsvc_notify(rpcsvc_t *rpc, void *xl, rpcsvc_event_t event,
                       void *data)
{
    xlator_t *this = NULL;
    rpc_transport_t *xprt = NULL;
    glusterd_conf_t *priv = NULL;

    if (!xl || !data) {
        gf_msg("glusterd", GF_LOG_WARNING, 0, GD_MSG_NO_INIT,
               "Calling rpc_notify without initializing");
        goto out;
    }

    this = xl;
    xprt = data;

    priv = this->private;

    switch (event) {
        case RPCSVC_EVENT_ACCEPT: {
            pthread_mutex_lock(&priv->xprt_lock);
            list_add_tail(&xprt->list, &priv->xprt_list);
            pthread_mutex_unlock(&priv->xprt_lock);
            break;
        }
        case RPCSVC_EVENT_DISCONNECT: {
            /* A DISCONNECT event could come without an ACCEPT event
             * happening for this transport. This happens when the server is
             * expecting encrypted connections by the client tries to
             * connect unecnrypted
             */
            if (list_empty(&xprt->list))
                break;

            pthread_mutex_lock(&priv->xprt_lock);
            list_del(&xprt->list);
            pthread_mutex_unlock(&priv->xprt_lock);
            pmap_port_remove(this, 0, NULL, xprt, _gf_false);
            break;
        }

        default:
            break;
    }

out:
    return 0;
}

static int32_t
glusterd_program_register(xlator_t *this, rpcsvc_t *svc, rpcsvc_program_t *prog)
{
    int32_t ret = -1;

    ret = rpcsvc_program_register(svc, prog, _gf_false);
    if (ret) {
        gf_msg_debug(this->name, 0,
                     "cannot register program (name: %s, prognum:%d, "
                     "progver:%d)",
                     prog->progname, prog->prognum, prog->progver);
        goto out;
    }

out:
    return ret;
}

int
glusterd_rpcsvc_options_build(dict_t *options)
{
    int ret = 0;
    uint32_t backlog = 0;

    ret = dict_get_uint32(options, "transport.listen-backlog", &backlog);

    if (ret) {
        backlog = GLUSTERFS_SOCKET_LISTEN_BACKLOG;
        ret = dict_set_uint32(options, "transport.listen-backlog", backlog);
        if (ret) {
            gf_smsg(THIS->name, GF_LOG_ERROR, -ret, GD_MSG_DICT_SET_FAILED,
                    "Key=transport.listen-backlog", NULL);
            goto out;
        }
    }

    gf_msg_debug("glusterd", 0, "listen-backlog value: %d", backlog);

out:
    return ret;
}

#if SYNCDAEMON_COMPILE
static int
glusterd_check_gsync_present(int *valid_state)
{
    char buff[PATH_MAX] = {
        0,
    };
    runner_t runner = {
        0,
    };
    char *ptr = NULL;
    int ret = 0;

    runinit(&runner);
    runner_add_args(&runner, GSYNCD_PREFIX "/gsyncd", "--version", NULL);
    runner_redir(&runner, STDOUT_FILENO, RUN_PIPE);
    ret = runner_start(&runner);
    if (ret == -1) {
        if (errno == ENOENT) {
            gf_msg("glusterd", GF_LOG_INFO, errno, GD_MSG_MODULE_NOT_INSTALLED,
                   GEOREP " module not installed in the system");
            *valid_state = 0;
        } else {
            gf_msg("glusterd", GF_LOG_ERROR, errno, GD_MSG_MODULE_NOT_WORKING,
                   GEOREP " module not working as desired");
            *valid_state = -1;
        }
        goto out;
    }

    ptr = fgets(buff, sizeof(buff), runner_chio(&runner, STDOUT_FILENO));
    if (ptr) {
        if (!strstr(buff, "gsyncd")) {
            ret = -1;
            gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_MODULE_NOT_WORKING,
                   GEOREP
                   " module not "
                   "working as desired");
            *valid_state = -1;
            goto out;
        }
    } else {
        ret = -1;
        gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_MODULE_NOT_WORKING,
               GEOREP
               " module not "
               "working as desired");
        *valid_state = -1;
        goto out;
    }

    ret = 0;
out:

    runner_end(&runner);

    gf_msg_debug("glusterd", 0, "Returning %d", ret);
    return ret;
}

static int
group_write_allow(char *path, gid_t gid)
{
    struct stat st = {
        0,
    };
    int ret = 0;

    ret = sys_stat(path, &st);
    if (ret == -1)
        goto out;
    GF_ASSERT(S_ISDIR(st.st_mode));

    ret = sys_chown(path, -1, gid);
    if (ret == -1)
        goto out;

    ret = sys_chmod(path, (st.st_mode & ~S_IFMT) | S_IWGRP | S_IXGRP | S_ISVTX);

out:
    if (ret == -1)
        gf_msg("glusterd", GF_LOG_CRITICAL, errno,
               GD_MSG_WRITE_ACCESS_GRANT_FAIL,
               "failed to set up write access to %s for group %d (%s)", path,
               gid, strerror(errno));
    return ret;
}

static int
glusterd_crt_georep_folders(char *georepdir, glusterd_conf_t *conf)
{
    char *greplg_s = NULL;
    struct group *gr = NULL;
    int ret = 0;
    int gr_ret = 0;
    int32_t len = 0;
    char logdir[PATH_MAX] = {0};

    GF_ASSERT(georepdir);
    GF_ASSERT(conf);

    if (strlen(conf->workdir) + 2 > PATH_MAX - SLEN(GEOREP)) {
        ret = -1;
        gf_msg("glusterd", GF_LOG_CRITICAL, 0, GD_MSG_DIRPATH_TOO_LONG,
               "directory path %s/" GEOREP " is longer than PATH_MAX",
               conf->workdir);
        goto out;
    }

    len = snprintf(georepdir, PATH_MAX, "%s/" GEOREP, conf->workdir);
    if ((len < 0) || (len >= PATH_MAX)) {
        gf_smsg("glusterd", GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
        ret = -1;
        goto out;
    }
    ret = mkdir_p(georepdir, 0755, _gf_true);
    if (-1 == ret) {
        gf_msg("glusterd", GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create " GEOREP " directory %s", georepdir);
        goto out;
    }

    ret = dict_get_str(THIS->options, GEOREP "-log-group", &greplg_s);
    if (ret) {
        gf_smsg("glusterd", GF_LOG_ERROR, -ret, GD_MSG_DICT_GET_FAILED,
                "Key=log-group", NULL);
        ret = 0;
    } else {
        gr = getgrnam(greplg_s);
        if (!gr) {
            gf_msg("glusterd", GF_LOG_CRITICAL, 0, GD_MSG_LOGGROUP_INVALID,
                   "group " GEOREP "-log-group %s does not exist", greplg_s);
            gr_ret = -1;
        }
    }
    if ((strlen(conf->logdir) + 2 + SLEN(GEOREP)) >= PATH_MAX) {
        ret = -1;
        gf_msg("glusterd", GF_LOG_CRITICAL, 0, GD_MSG_DIRPATH_TOO_LONG,
               "directory path %s/" GEOREP " is longer than PATH_MAX",
               conf->logdir);
        goto out;
    }
    len = snprintf(logdir, PATH_MAX, "%s/" GEOREP, conf->logdir);
    if ((len < 0) || (len >= PATH_MAX)) {
        ret = -1;
        goto out;
    }
    ret = mkdir_p(logdir, 0755, _gf_true);
    if (-1 == ret) {
        gf_msg("glusterd", GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create " GEOREP " log directory");
        goto out;
    }
    if (gr) {
        gr_ret = group_write_allow(logdir, gr->gr_gid);
    }

    if ((strlen(conf->logdir) + 2 + SLEN(GEOREP "-secondaries")) >= PATH_MAX) {
        ret = -1;
        gf_msg("glusterd", GF_LOG_CRITICAL, 0, GD_MSG_DIRPATH_TOO_LONG,
               "directory path  %s/" GEOREP
               "-secondaries"
               " is longer than PATH_MAX",
               conf->logdir);
        goto out;
    }
    len = snprintf(logdir, PATH_MAX, "%s/" GEOREP "-secondaries", conf->logdir);
    if ((len < 0) || (len >= PATH_MAX)) {
        gf_smsg("glusterd", GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
        ret = -1;
        goto out;
    }
    ret = mkdir_p(logdir, 0755, _gf_true);
    if (-1 == ret) {
        gf_msg("glusterd", GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create " GEOREP " secondary log directory");
        goto out;
    }
    if (gr && !gr_ret) {
        gr_ret = group_write_allow(logdir, gr->gr_gid);
    }

    /* MountBroker log file directory */
    if ((strlen(conf->logdir) + 2 + SLEN(GEOREP "-secondaries/mbr")) >=
        PATH_MAX) {
        ret = -1;
        gf_msg("glusterd", GF_LOG_CRITICAL, 0, GD_MSG_DIRPATH_TOO_LONG,
               "directory path  %s/" GEOREP
               "-secondaries/mbr"
               " is longer than PATH_MAX",
               conf->logdir);
        goto out;
    }

    len = snprintf(logdir, PATH_MAX, "%s/" GEOREP "-secondaries/mbr",
                   conf->logdir);
    if ((len < 0) || (len >= PATH_MAX)) {
        gf_smsg("glusterd", GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
        ret = -1;
        goto out;
    }

    ret = mkdir_p(logdir, 0755, _gf_true);
    if (-1 == ret) {
        gf_msg("glusterd", GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create " GEOREP
               " mountbroker secondary log directory");
        goto out;
    }
    if (gr && !gr_ret) {
        gr_ret = group_write_allow(logdir, gr->gr_gid);
    }
    if (gr_ret)
        ret = gr_ret;
out:
    gf_msg_debug("glusterd", 0, "Returning %d", ret);
    return ret;
}

static void
runinit_gsyncd_setrx(runner_t *runner, glusterd_conf_t *conf)
{
    runinit(runner);
    runner_add_args(runner, GSYNCD_PREFIX "/gsyncd", "-c", NULL);
    runner_argprintf(runner, "%s/" GSYNC_CONF_TEMPLATE, conf->workdir);
    runner_add_arg(runner, "--config-set-rx");
}

static int
configure_syncdaemon(glusterd_conf_t *conf)
#define RUN_GSYNCD_CMD                                                         \
    do {                                                                       \
        ret = runner_run_reuse(&runner);                                       \
        if (ret == -1) {                                                       \
            runner_log(&runner, "glusterd", GF_LOG_ERROR, "command failed");   \
            runner_end(&runner);                                               \
            goto out;                                                          \
        }                                                                      \
        runner_end(&runner);                                                   \
    } while (0)
{
    int ret = 0;
    runner_t runner = {
        0,
    };
    char georepdir[PATH_MAX] = {
        0,
    };
    int valid_state = 0;

    ret = setenv("_GLUSTERD_CALLED_", "1", 1);
    if (ret < 0) {
        ret = 0;
        goto out;
    }
    valid_state = -1;
    ret = glusterd_check_gsync_present(&valid_state);
    if (-1 == ret) {
        ret = valid_state;
        goto out;
    }

    glusterd_crt_georep_folders(georepdir, conf);
    if (ret) {
        ret = 0;
        goto out;
    }

    /************
     * primary pre-configuration
     ************/

    /* remote-gsyncd */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "remote-gsyncd", GSYNCD_PREFIX "/gsyncd", ".", ".",
                    NULL);
    RUN_GSYNCD_CMD;

    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "remote-gsyncd", "/nonexistent/gsyncd", ".",
                    "^ssh:", NULL);
    RUN_GSYNCD_CMD;

    /* gluster-command-dir */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "gluster-command-dir", SBIN_DIR "/", ".", ".",
                    NULL);
    RUN_GSYNCD_CMD;

    /* gluster-params */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "gluster-params", "aux-gfid-mount acl", ".", ".",
                    NULL);
    RUN_GSYNCD_CMD;

    /* ssh-command */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "ssh-command");
    runner_argprintf(&runner,
                     "ssh -oPasswordAuthentication=no "
                     "-oStrictHostKeyChecking=no "
                     "-i %s/secret.pem",
                     georepdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* ssh-command tar */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "ssh-command-tar");
    runner_argprintf(&runner,
                     "ssh -oPasswordAuthentication=no "
                     "-oStrictHostKeyChecking=no "
                     "-i %s/tar_ssh.pem",
                     georepdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* pid-file */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "pid-file");
    runner_argprintf(
        &runner, "%s/${primaryvol}_${remotehost}_${secondaryvol}/monitor.pid",
        georepdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* geo-rep working dir */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "georep-session-working-dir");
    runner_argprintf(&runner, "%s/${primaryvol}_${remotehost}_${secondaryvol}/",
                     georepdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* state-file */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "state-file");
    runner_argprintf(
        &runner,
        "%s/${primaryvol}_${remotehost}_${secondaryvol}/monitor.status",
        georepdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* state-detail-file */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "state-detail-file");
    runner_argprintf(&runner,
                     "%s/${primaryvol}_${remotehost}_${secondaryvol}/"
                     "${eSecondary}-detail.status",
                     georepdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* state-detail-file */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "state-detail-file");
    runner_argprintf(&runner,
                     "%s/${primaryvol}_${remotehost}_${secondaryvol}/"
                     "${eSecondary}-detail.status",
                     georepdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* state-socket */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "state-socket-unencoded");
    runner_argprintf(&runner, "%s/${primaryvol}/${eSecondary}.socket",
                     georepdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* socketdir */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "socketdir", GLUSTERD_SOCK_DIR, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* log-file */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "log-file");
    runner_argprintf(&runner, "%s/" GEOREP "/${primaryvol}/${eSecondary}.log",
                     conf->logdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* gluster-log-file */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "gluster-log-file");
    runner_argprintf(&runner,
                     "%s/" GEOREP
                     "/${primaryvol}/${eSecondary}${local_id}.gluster.log",
                     conf->logdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* ignore-deletes */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "ignore-deletes", "true", ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* special-sync-mode */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "special-sync-mode", "partial", ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* change-detector == changelog */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "change-detector", "changelog", ".", ".", NULL);
    RUN_GSYNCD_CMD;

    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "working-dir");
    runner_argprintf(&runner, "%s/${primaryvol}/${eSecondary}",
                     DEFAULT_VAR_RUN_DIRECTORY);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /************
     * secondary pre-configuration
     ************/

    /* secondary-gluster-command-dir */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "secondary-gluster-command-dir", SBIN_DIR "/", ".",
                    NULL);
    RUN_GSYNCD_CMD;

    /* gluster-params */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_args(&runner, "gluster-params", "aux-gfid-mount acl", ".", NULL);
    RUN_GSYNCD_CMD;

    /* log-file */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "log-file");
    runner_argprintf(
        &runner,
        "%s/" GEOREP
        "-secondaries/"
        "${session_owner}:${local_node}${local_id}.${secondaryvol}.log",
        conf->logdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* MountBroker log-file */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "log-file-mbr");
    runner_argprintf(
        &runner,
        "%s/" GEOREP
        "-secondaries/mbr/"
        "${session_owner}:${local_node}${local_id}.${secondaryvol}.log",
        conf->logdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

    /* gluster-log-file */
    runinit_gsyncd_setrx(&runner, conf);
    runner_add_arg(&runner, "gluster-log-file");
    runner_argprintf(
        &runner,
        "%s/" GEOREP
        "-secondaries/"
        "${session_owner}:${local_node}${local_id}.${secondaryvol}.gluster.log",
        conf->logdir);
    runner_add_args(&runner, ".", ".", NULL);
    RUN_GSYNCD_CMD;

out:
    return ret ? -1 : 0;
}
#undef RUN_GSYNCD_CMD
#else  /* SYNCDAEMON_COMPILE */
static int
configure_syncdaemon(glusterd_conf_t *conf)
{
    return 0;
}
#endif /* !SYNCDAEMON_COMPILE */

static int
check_prepare_mountbroker_root(char *mountbroker_root)
{
    int dfd0 = -1;
    int dfd = -1;
    int dfd2 = -1;
    struct stat st = {
        0,
    };
    struct stat st2 = {
        0,
    };
    int ret = 0;

    ret = open(mountbroker_root, O_RDONLY);
    if (ret != -1) {
        dfd = ret;
        ret = sys_fstat(dfd, &st);
    }
    if (ret == -1 || !S_ISDIR(st.st_mode)) {
        gf_msg("glusterd", GF_LOG_ERROR, errno, GD_MSG_DIR_OP_FAILED,
               "cannot access mountbroker-root directory %s", mountbroker_root);
        ret = -1;
        goto out;
    }
    if (st.st_uid != 0 || (st.st_mode & (S_IWGRP | S_IWOTH))) {
        gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DIR_PERM_LIBERAL,
               "permissions on mountbroker-root directory %s are "
               "too liberal",
               mountbroker_root);
        ret = -1;
        goto out;
    }
    if (!(st.st_mode & (S_IXGRP | S_IXOTH))) {
        gf_msg("glusterd", GF_LOG_WARNING, 0, GD_MSG_DIR_PERM_STRICT,
               "permissions on mountbroker-root directory %s are "
               "probably too strict",
               mountbroker_root);
    }

    dfd0 = dup(dfd);

    for (;;) {
        ret = sys_openat(dfd, "..", O_RDONLY, 0);
        if (ret != -1) {
            dfd2 = ret;
            ret = sys_fstat(dfd2, &st2);
        }
        if (ret == -1) {
            gf_msg("glusterd", GF_LOG_ERROR, errno, GD_MSG_DIR_OP_FAILED,
                   "error while checking mountbroker-root ancestors "
                   "%d (%s)",
                   errno, strerror(errno));
            goto out;
        }

        if (st2.st_ino == st.st_ino)
            break; /* arrived to root */

        if (st2.st_uid != 0 ||
            ((st2.st_mode & (S_IWGRP | S_IWOTH)) && !(st2.st_mode & S_ISVTX))) {
            gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_DIR_PERM_LIBERAL,
                   "permissions on ancestors of mountbroker-root "
                   "directory are too liberal");
            ret = -1;
            goto out;
        }
        if (!(st.st_mode & (S_IXGRP | S_IXOTH))) {
            gf_msg("glusterd", GF_LOG_WARNING, 0, GD_MSG_DIR_PERM_STRICT,
                   "permissions on ancestors of mountbroker-root "
                   "directory are probably too strict");
        }

        sys_close(dfd);
        dfd = dfd2;
        st = st2;
    }

    ret = sys_mkdirat(dfd0, MB_HIVE, 0711);
    if (ret == -1 && errno == EEXIST)
        ret = 0;
    if (ret != -1)
        ret = sys_fstatat(dfd0, MB_HIVE, &st, AT_SYMLINK_NOFOLLOW);
    if (ret == -1 || st.st_mode != (S_IFDIR | 0711)) {
        gf_msg("glusterd", GF_LOG_ERROR, errno, GD_MSG_CREATE_DIR_FAILED,
               "failed to set up mountbroker-root directory %s",
               mountbroker_root);
        ret = -1;
        goto out;
    }

    ret = 0;

out:
    if (dfd0 != -1)
        sys_close(dfd0);
    if (dfd != -1)
        sys_close(dfd);
    if (dfd2 != -1 && dfd != dfd2)
        sys_close(dfd2);

    return ret;
}

static int
_install_mount_spec(dict_t *opts, char *key, data_t *value, void *data)
{
    glusterd_conf_t *priv = THIS->private;
    char *label = NULL;
    gf_boolean_t georep = _gf_false;
    char *pdesc = value->data;
    char *volname = NULL;
    int rv = 0;
    gf_mount_spec_t *mspec = NULL;
    char *user = NULL;

    label = strtail(key, "mountbroker.");

    /* check for presence of geo-rep label */
    if (!label) {
        label = strtail(key, "mountbroker-" GEOREP ".");
        if (label)
            georep = _gf_true;
    }

    if (!label)
        return 0;

    mspec = GF_CALLOC(1, sizeof(*mspec), gf_gld_mt_mount_spec);
    if (!mspec) {
        gf_smsg(THIS->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL);
        goto err;
    }
    mspec->label = label;

    if (georep) {
        volname = gf_strdup(pdesc);
        if (!volname)
            goto err;
        user = strchr(volname, ':');
        if (user) {
            *user = '\0';
            user++;
        } else
            user = label;

        rv = make_georep_mountspec(mspec, volname, user, priv->logdir);

        GF_FREE(volname);
        if (rv != 0)
            goto err;
    } else if (parse_mount_pattern_desc(mspec, pdesc) != 0)
        goto err;

    cds_list_add_tail(&mspec->speclist, &priv->mount_specs);

    return 0;
err:

    gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_MOUNT_SPEC_INSTALL_FAIL,
           "adding %smount spec failed: label: %s desc: %s",
           georep ? GEOREP " " : "", label, pdesc ? pdesc : "");

    if (mspec) {
        if (mspec->patterns) {
            GF_FREE(mspec->patterns->components);
            GF_FREE(mspec->patterns);
        }
        GF_FREE(mspec);
    }

    return -1;
}

/* The glusterd unix domain socket listener only listens for cli */
rpcsvc_t *
glusterd_init_uds_listener(xlator_t *this)
{
    int ret = -1;
    dict_t *options = NULL;
    rpcsvc_t *rpc = NULL;
    data_t *sock_data = NULL;
    char sockfile[UNIX_PATH_MAX] = {0};
    int i = 0;

    GF_ASSERT(this);

    options = dict_new();
    if (!options) {
        gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_DICT_CREATE_FAIL, NULL);
        goto out;
    }

    sock_data = dict_get(this->options, "glusterd-sockfile");
    (void)snprintf(sockfile, sizeof(sockfile), "%s",
                   sock_data ? sock_data->data : DEFAULT_GLUSTERD_SOCKFILE);

    ret = rpcsvc_transport_unix_options_build(options, sockfile);
    if (ret)
        goto out;

    rpc = rpcsvc_init(this, this->ctx, options, 8);
    if (rpc == NULL) {
        ret = -1;
        goto out;
    }

    ret = rpcsvc_register_notify(rpc, glusterd_rpcsvc_notify, this);
    if (ret) {
        gf_msg_debug(this->name, 0, "Failed to register notify function");
        goto out;
    }

    ret = rpcsvc_create_listeners(rpc, options, this->name);
    if (ret != 1) {
        gf_msg_debug(this->name, 0, "Failed to create listener");
        goto out;
    }
    ret = 0;

    for (i = 0; i < gd_uds_programs_count; i++) {
        ret = glusterd_program_register(this, rpc, gd_uds_programs[i]);
        if (ret) {
            i--;
            for (; i >= 0; i--)
                rpcsvc_program_unregister(rpc, gd_uds_programs[i]);

            goto out;
        }
    }

out:
    if (options)
        dict_unref(options);

    if (ret) {
        gf_msg(this->name, GF_LOG_ERROR, 0,
               GD_MSG_GLUSTERD_SOCK_LISTENER_START_FAIL,
               "Failed to start glusterd "
               "unix domain socket listener.");
        if (rpc) {
            GF_FREE(rpc);
            rpc = NULL;
        }
    }
    return rpc;
}

void
glusterd_stop_uds_listener(xlator_t *this)
{
    glusterd_conf_t *conf = NULL;
    rpcsvc_listener_t *listener = NULL;
    rpcsvc_listener_t *next = NULL;
    data_t *sock_data = NULL;
    char sockfile[UNIX_PATH_MAX] = {0};

    GF_ASSERT(this);
    conf = this->private;

    (void)rpcsvc_program_unregister(conf->uds_rpc, &gd_svc_cli_prog);
    (void)rpcsvc_program_unregister(conf->uds_rpc, &gluster_handshake_prog);

    list_for_each_entry_safe(listener, next, &conf->uds_rpc->listeners, list)
    {
        rpcsvc_listener_destroy(listener);
    }

    (void)rpcsvc_unregister_notify(conf->uds_rpc, glusterd_rpcsvc_notify, this);

    sock_data = dict_get(this->options, "glusterd-sockfile");
    (void)snprintf(sockfile, sizeof(sockfile), "%s",
                   sock_data ? sock_data->data : DEFAULT_GLUSTERD_SOCKFILE);
    sys_unlink(sockfile);

    return;
}

void
glusterd_stop_listener(xlator_t *this)
{
    glusterd_conf_t *conf = NULL;
    rpcsvc_listener_t *listener = NULL;
    rpcsvc_listener_t *next = NULL;
    int i = 0;

    GF_VALIDATE_OR_GOTO("glusterd", this, out);
    conf = this->private;
    GF_VALIDATE_OR_GOTO(this->name, conf, out);

    gf_msg_debug(this->name, 0, "%s function called ", __func__);

    for (i = 0; i < gd_inet_programs_count; i++) {
        rpcsvc_program_unregister(conf->rpc, gd_inet_programs[i]);
    }

    list_for_each_entry_safe(listener, next, &conf->rpc->listeners, list)
    {
        rpcsvc_listener_destroy(listener);
    }

    (void)rpcsvc_unregister_notify(conf->rpc, glusterd_rpcsvc_notify, this);

out:

    return;
}

static int
glusterd_find_correct_var_run_dir(xlator_t *this, char *var_run_dir)
{
    int ret = -1;
    struct stat buf = {
        0,
    };

    GF_VALIDATE_OR_GOTO("glusterd", this, out);
    GF_VALIDATE_OR_GOTO(this->name, var_run_dir, out);

    /* /var/run is normally a symbolic link to /run dir, which
     * creates problems as the entry point in the mtab for the mount point
     * and glusterd maintained entry point will be different. Therefore
     * identify the correct run dir and use it
     */
    ret = sys_lstat(GLUSTERD_VAR_RUN_DIR, &buf);
    if (ret != 0) {
        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_FILE_OP_FAILED,
               "stat fails on %s, exiting. (errno = %d)", GLUSTERD_VAR_RUN_DIR,
               errno);
        goto out;
    }

    /* If /var/run is symlink then use /run dir */
    if (S_ISLNK(buf.st_mode)) {
        strcpy(var_run_dir, GLUSTERD_RUN_DIR);
    } else {
        strcpy(var_run_dir, GLUSTERD_VAR_RUN_DIR);
    }

    ret = 0;
out:
    return ret;
}

static int
glusterd_init_var_run_dirs(xlator_t *this, char *var_run_dir,
                           char *dir_to_be_created)
{
    int ret = -1;
    struct stat buf = {
        0,
    };
    char abs_path[PATH_MAX] = {
        0,
    };

    GF_VALIDATE_OR_GOTO("glusterd", this, out);
    GF_VALIDATE_OR_GOTO(this->name, var_run_dir, out);
    GF_VALIDATE_OR_GOTO(this->name, dir_to_be_created, out);

    snprintf(abs_path, sizeof(abs_path), "%s%s", var_run_dir,
             dir_to_be_created);

    ret = sys_stat(abs_path, &buf);
    if ((ret != 0) && (ENOENT != errno)) {
        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_FILE_OP_FAILED,
               "stat fails on %s, exiting. (errno = %d)", abs_path, errno);
        ret = -1;
        goto out;
    }

    if ((!ret) && (!S_ISDIR(buf.st_mode))) {
        gf_msg(this->name, GF_LOG_CRITICAL, ENOENT, GD_MSG_DIR_NOT_FOUND,
               "Provided snap path %s is not a directory,"
               "exiting",
               abs_path);
        ret = -1;
        goto out;
    }

    if ((-1 == ret) && (ENOENT == errno)) {
        /* Create missing dirs */
        ret = mkdir_p(abs_path, 0755, _gf_true);

        if (-1 == ret) {
            gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
                   "Unable to create directory %s"
                   " ,errno = %d",
                   abs_path, errno);
            goto out;
        }
    }

out:
    return ret;
}

static int
is_upgrade(dict_t *options, gf_boolean_t *upgrade)
{
    int ret = 0;
    char *type = NULL;

    ret = dict_get_str(options, "upgrade", &type);
    if (!ret) {
        ret = gf_string2boolean(type, upgrade);
        if (ret) {
            gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_STR_TO_BOOL_FAIL,
                   "upgrade option "
                   "%s is not a valid boolean type",
                   type);
            ret = -1;
            goto out;
        }
    }
    ret = 0;
out:
    return ret;
}

static int
is_downgrade(dict_t *options, gf_boolean_t *downgrade)
{
    int ret = 0;
    char *type = NULL;

    ret = dict_get_str(options, "downgrade", &type);
    if (!ret) {
        ret = gf_string2boolean(type, downgrade);
        if (ret) {
            gf_msg("glusterd", GF_LOG_ERROR, 0, GD_MSG_STR_TO_BOOL_FAIL,
                   "downgrade option "
                   "%s is not a valid boolean type",
                   type);
            ret = -1;
            goto out;
        }
    }
    ret = 0;
out:
    return ret;
}

void
glusterd_destroy_hostname_list(struct list_head *hostname_list_head)
{
    glusterd_hostname_t *hostname_obj = NULL;
    glusterd_hostname_t *tmp = NULL;

    list_for_each_entry_safe(hostname_obj, tmp, hostname_list_head,
                             hostname_list)
    {
        list_del_init(&hostname_obj->hostname_list);
        GF_FREE(hostname_obj->hostname);
        GF_FREE(hostname_obj);
    }
}

/*
 * init - called during glusterd initialization
 *
 * @this:
 *
 */
int
init(xlator_t *this)
{
    int32_t ret = -1;
    rpcsvc_t *rpc = NULL;
    rpcsvc_t *uds_rpc = NULL;
    glusterd_conf_t *conf = NULL;
    data_t *dir_data = NULL;
    struct stat buf = {
        0,
    };
    char storedir[PATH_MAX] = {
        0,
    };
    char workdir[PATH_MAX] = {
        0,
    };
    char rundir[PATH_MAX] = {
        0,
    };
    char logdir[VALID_GLUSTERD_PATHMAX] = {
        0,
    };
    char cmd_log_filename[PATH_MAX] = {
        0,
    };
    char *mountbroker_root = NULL;
    int i = 0;
    int total_transport = 0;
    gf_valgrind_tool vgtool;
    char *valgrind_str = NULL;
    char *transport_type = NULL;
    char var_run_dir[PATH_MAX] = {
        0,
    };
    int32_t workers = 0;
    gf_boolean_t upgrade = _gf_false;
    gf_boolean_t downgrade = _gf_false;
    char *localtime_logging = NULL;
    int32_t len = 0;
    int op_version = 0;

#if defined(RUN_WITH_MEMCHECK)
    vgtool = _gf_memcheck;
#elif defined(RUN_WITH_DRD)
    vgtool = _gf_drd;
#else
    vgtool = _gf_none;
#endif

#ifndef GF_DARWIN_HOST_OS
    {
        struct rlimit lim;
        lim.rlim_cur = 65536;
        lim.rlim_max = 65536;

        if (setrlimit(RLIMIT_NOFILE, &lim) == -1) {
            gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_SET_XATTR_FAIL,
                    "Failed to set 'ulimit -n 65536'", NULL);
        } else {
            gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_FILE_DESC_LIMIT_SET,
                   "Maximum allowed open file descriptors "
                   "set to 65536");
        }
    }
#endif

    dir_data = dict_get(this->options, "run-directory");

    if (!dir_data) {
        /* Use default working dir */
        len = snprintf(rundir, PATH_MAX, "%s", DEFAULT_VAR_RUN_DIRECTORY);
    } else {
        len = snprintf(rundir, PATH_MAX, "%s", dir_data->data);
    }
    if (len < 0 || len >= PATH_MAX)
        exit(2);

    dir_data = dict_get(this->options, "logging-directory");
    if (!dir_data) {
        // Check for deprecated 'cluster-test-mode' option
        dir_data = dict_get(this->options, "cluster-test-mode");
        if (dir_data) {
            len = snprintf(logdir, VALID_GLUSTERD_PATHMAX, "%s",
                           dir_data->data);
            gf_msg(
                this->name, GF_LOG_WARNING, 0, GD_MSG_CLUSTER_RC_ENABLE,
                "gluster log directory is set to %s. The option "
                "'cluster-test-mode' is deprecated and will be removed soon. "
                "Please use the new option 'logging-directory' instead.",
                dir_data->data);
        } else {
            /* Use default working dir */
            len = snprintf(logdir, VALID_GLUSTERD_PATHMAX, "%s",
                           DEFAULT_LOG_FILE_DIRECTORY);
        }
    } else {
        len = snprintf(logdir, VALID_GLUSTERD_PATHMAX, "%s", dir_data->data);
        gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_CLUSTER_RC_ENABLE,
               "gluster log directory is set to %s", dir_data->data);
    }
    if (len < 0 || len >= PATH_MAX)
        exit(2);

    ret = mkdir_p(logdir, 0777, _gf_true);
    if ((ret == -1) && (EEXIST != errno)) {
        gf_msg(THIS->name, GF_LOG_ERROR, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create log dir %s", logdir);
        exit(1);
    }

    dir_data = dict_get(this->options, "working-directory");

    if (!dir_data) {
        // Use default working dir
        len = snprintf(workdir, PATH_MAX, "%s", GLUSTERD_DEFAULT_WORKDIR);
    } else {
        len = snprintf(workdir, PATH_MAX, "%s", dir_data->data);
    }
    if (len < 0 || len >= PATH_MAX)
        exit(2);

    ret = sys_stat(workdir, &buf);
    if ((ret != 0) && (ENOENT != errno)) {
        gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_DIR_OP_FAILED,
               "stat fails on %s, exiting. (errno = %d)", workdir, errno);
        exit(1);
    }

    if ((!ret) && (!S_ISDIR(buf.st_mode))) {
        gf_msg(this->name, GF_LOG_CRITICAL, ENOENT, GD_MSG_DIR_NOT_FOUND,
               "Provided working area %s is not a directory,"
               "exiting",
               workdir);
        exit(1);
    }

    if ((-1 == ret) && (ENOENT == errno)) {
        ret = mkdir_p(workdir, 0755, _gf_true);

        if (-1 == ret) {
            gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
                   "Unable to create directory %s"
                   " ,errno = %d",
                   workdir, errno);
            exit(1);
        }
    }

    setenv("GLUSTERD_WORKDIR", workdir, 1);
    gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_CURR_WORK_DIR_INFO,
           "Using %s as working directory", workdir);

    setenv("DEFAULT_VAR_RUN_DIRECTORY", rundir, 1);
    gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_CURR_WORK_DIR_INFO,
           "Using %s as pid file working "
           "directory",
           rundir);

    ret = glusterd_find_correct_var_run_dir(this, var_run_dir);
    if (ret) {
        gf_msg(this->name, GF_LOG_CRITICAL, 0, GD_MSG_VAR_RUN_DIR_FIND_FAIL,
               "Unable to find "
               "the correct var run dir");
        exit(1);
    }

    ret = glusterd_init_var_run_dirs(this, var_run_dir,
                                     GLUSTERD_DEFAULT_SNAPS_BRICK_DIR);

    if (ret) {
        gf_msg(this->name, GF_LOG_CRITICAL, 0, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create "
               "snap backend folder");
        exit(1);
    }

    len = snprintf(snap_mount_dir, sizeof(snap_mount_dir), "%s%s", var_run_dir,
                   GLUSTERD_DEFAULT_SNAPS_BRICK_DIR);
    if ((len < 0) || (len >= sizeof(snap_mount_dir))) {
        gf_msg(this->name, GF_LOG_CRITICAL, 0, GD_MSG_DIR_OP_FAILED,
               "Snap mount dir too long");
        exit(1);
    }

    ret = mkdir_p(GLUSTER_SHARED_STORAGE_BRICK_DIR, 0755, _gf_true);
    if (ret) {
        gf_msg(this->name, GF_LOG_CRITICAL, 0, GD_MSG_DIR_OP_FAILED,
               "Unable to create "
               "shared storage brick");
        exit(1);
    }

    ret = glusterd_init_var_run_dirs(this, rundir, GLUSTERD_BITD_RUN_DIR);
    if (ret) {
        gf_msg(this->name, GF_LOG_CRITICAL, 0, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create "
               "bitd running directory");
        exit(1);
    }

    ret = glusterd_init_var_run_dirs(this, rundir, GLUSTERD_SCRUB_RUN_DIR);
    if (ret) {
        gf_msg(this->name, GF_LOG_CRITICAL, 0, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create "
               "scrub running directory");
        exit(1);
    }

#ifdef BUILD_GNFS
    ret = glusterd_init_var_run_dirs(this, rundir, GLUSTERD_NFS_RUN_DIR);
    if (ret) {
        gf_msg(this->name, GF_LOG_CRITICAL, 0, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create "
               "nfs running directory");
        exit(1);
    }
#endif

    ret = glusterd_init_var_run_dirs(this, rundir, GLUSTERD_QUOTAD_RUN_DIR);
    if (ret) {
        gf_msg(this->name, GF_LOG_CRITICAL, 0, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create "
               "quota running directory");
        exit(1);
    }

    snprintf(cmd_log_filename, PATH_MAX, "%s/cmd_history.log", logdir);
    ret = gf_cmd_log_init(cmd_log_filename);

    if (ret == -1) {
        gf_msg("this->name", GF_LOG_CRITICAL, errno, GD_MSG_FILE_OP_FAILED,
               "Unable to create cmd log file %s", cmd_log_filename);
        exit(1);
    }

    len = snprintf(storedir, sizeof(storedir), "%s/vols", workdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }

    ret = sys_mkdir(storedir, 0755);

    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create volume directory %s"
               " ,errno = %d",
               storedir, errno);
        exit(1);
    }

    /*keeping individual volume pid file information in /var/run/gluster* */
    len = snprintf(storedir, sizeof(storedir), "%s/vols", rundir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }

    ret = sys_mkdir(storedir, 0755);

    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create volume directory %s"
               " ,errno = %d",
               storedir, errno);
        exit(1);
    }

    len = snprintf(storedir, sizeof(storedir), "%s/snaps", workdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }

    ret = sys_mkdir(storedir, 0755);

    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create snaps directory %s"
               " ,errno = %d",
               storedir, errno);
        exit(1);
    }

    len = snprintf(storedir, sizeof(storedir), "%s/peers", workdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }

    ret = sys_mkdir(storedir, 0755);

    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create peers directory %s"
               " ,errno = %d",
               storedir, errno);
        exit(1);
    }

    len = snprintf(storedir, sizeof(storedir), "%s/bricks", logdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }

    ret = sys_mkdir(storedir, 0755);
    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create logs directory %s"
               " ,errno = %d",
               storedir, errno);
        exit(1);
    }

#ifdef BUILD_GNFS
    len = snprintf(storedir, sizeof(storedir), "%s/nfs", workdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }
    ret = sys_mkdir(storedir, 0755);
    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create nfs directory %s"
               " ,errno = %d",
               storedir, errno);
        exit(1);
    }
#endif
    len = snprintf(storedir, sizeof(storedir), "%s/bitd", workdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }
    ret = sys_mkdir(storedir, 0755);
    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create bitrot directory %s", storedir);
        exit(1);
    }

    len = snprintf(storedir, sizeof(storedir), "%s/scrub", workdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }
    ret = sys_mkdir(storedir, 0755);
    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create scrub directory %s", storedir);
        exit(1);
    }

    len = snprintf(storedir, sizeof(storedir), "%s/glustershd", workdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }
    ret = sys_mkdir(storedir, 0755);
    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create glustershd directory %s"
               " ,errno = %d",
               storedir, errno);
        exit(1);
    }

    len = snprintf(storedir, sizeof(storedir), "%s/quotad", workdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }
    ret = sys_mkdir(storedir, 0755);
    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create quotad directory %s"
               " ,errno = %d",
               storedir, errno);
        exit(1);
    }

    len = snprintf(storedir, sizeof(storedir), "%s/groups", workdir);
    if ((len < 0) || (len >= sizeof(storedir))) {
        exit(1);
    }
    ret = sys_mkdir(storedir, 0755);
    if ((-1 == ret) && (errno != EEXIST)) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
               "Unable to create glustershd directory %s"
               " ,errno = %d",
               storedir, errno);
        exit(1);
    }

    ret = glusterd_rpcsvc_options_build(this->options);
    if (ret)
        goto out;
    rpc = rpcsvc_init(this, this->ctx, this->options, 64);
    if (rpc == NULL) {
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_RPC_INIT_FAIL,
               "failed to init rpc");
        goto out;
    }

    ret = rpcsvc_register_notify(rpc, glusterd_rpcsvc_notify, this);
    if (ret) {
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_RPCSVC_REG_NOTIFY_RETURNED,
               "rpcsvc_register_notify returned %d", ret);
        goto out;
    }

    /* Enable encryption for the TCP listener is management encryption is
     * enabled
     */
    if (this->ctx->secure_mgmt) {
        ret = dict_set_str(this->options, "transport.socket.ssl-enabled", "on");
        if (ret != 0) {
            gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_SET_FAILED,
                   "failed to set ssl-enabled in dict");
            goto out;
        }
        /*
         * This is the only place where we want secure_srvr to reflect
         * the management-plane setting.
         */
        this->ctx->secure_srvr = MGMT_SSL_ALWAYS;
    }

    /*
     * only one (at most a pair - rdma and socket) listener for
     * glusterd1_mop_prog, gluster_pmap_prog and gluster_handshake_prog.
     */

    ret = dict_get_str(this->options, "transport-type", &transport_type);
    if (ret) {
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED,
               "Failed to get transport type");
        ret = -1;
        goto out;
    }

    total_transport = rpc_transport_count(transport_type);
    if (total_transport <= 0) {
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_RPC_TRANSPORT_COUNT_GET_FAIL,
               "failed to get total number of available tranpsorts");
        ret = -1;
        goto out;
    }

    ret = rpcsvc_create_listeners(rpc, this->options, this->name);
    if (ret < 1) {
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_RPC_LISTENER_CREATE_FAIL,
               "creation of listener failed");
        ret = -1;
        goto out;
    } else if (ret < total_transport) {
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_RPC_LISTENER_CREATE_FAIL,
               "creation of %d listeners failed, continuing with "
               "succeeded transport",
               (total_transport - ret));
    }

    for (i = 0; i < gd_inet_programs_count; i++) {
        ret = glusterd_program_register(this, rpc, gd_inet_programs[i]);
        if (ret) {
            i--;
            for (; i >= 0; i--)
                rpcsvc_program_unregister(rpc, gd_inet_programs[i]);

            goto out;
        }
    }

    /*
     * Start a unix domain socket listener just for cli commands This
     * should prevent ports from being wasted by being in TIMED_WAIT when
     * cli commands are done continuously
     */
    uds_rpc = glusterd_init_uds_listener(this);
    if (uds_rpc == NULL) {
        ret = -1;
        goto out;
    }

    conf = GF_CALLOC(1, sizeof(glusterd_conf_t), gf_gld_mt_glusterd_conf_t);
    GF_VALIDATE_OR_GOTO(this->name, conf, out);

    CDS_INIT_LIST_HEAD(&conf->peers);
    CDS_INIT_LIST_HEAD(&conf->volumes);
    CDS_INIT_LIST_HEAD(&conf->snapshots);
    CDS_INIT_LIST_HEAD(&conf->missed_snaps_list);
    CDS_INIT_LIST_HEAD(&conf->brick_procs);
    CDS_INIT_LIST_HEAD(&conf->shd_procs);
    CDS_INIT_LIST_HEAD(&conf->hostnames);
    INIT_LIST_HEAD(&conf->remote_hostnames);
    pthread_mutex_init(&conf->attach_lock, NULL);
    pthread_mutex_init(&conf->volume_lock, NULL);

    pthread_mutex_init(&conf->mutex, NULL);
    conf->rpc = rpc;
    conf->uds_rpc = uds_rpc;
    conf->gfs_mgmt = &gd_brick_prog;
    conf->restart_shd = _gf_false;
    this->private = conf;
    /* conf->workdir and conf->rundir are smaller than PATH_MAX; gcc's
     * snprintf checking will throw an error here if sprintf is used.
     * Dueling gcc-8 and coverity, now coverity isn't smart enough to
     * detect that these strncpy calls are safe. And for extra fun,
     * the annotations don't do anything. */
    if (strlen(workdir) >= sizeof(conf->workdir)) {
        ret = -1;
        goto out;
    }
    /* coverity[BUFFER_SIZE_WARNING] */
    (void)strncpy(conf->workdir, workdir, sizeof(conf->workdir));
    /* separate tests because combined tests confuses gcc */
    if (strlen(rundir) >= sizeof(conf->rundir)) {
        ret = -1;
        goto out;
    }
    /* coverity[BUFFER_SIZE_WARNING] */
    (void)strncpy(conf->rundir, rundir, sizeof(conf->rundir));

    /* coverity[BUFFER_SIZE_WARNING] */
    (void)strncpy(conf->logdir, logdir, sizeof(conf->logdir));

    synclock_init(&conf->big_lock, SYNC_LOCK_RECURSIVE);
    synccond_init(&conf->cond_restart_bricks);
    synccond_init(&conf->cond_restart_shd);
    synccond_init(&conf->cond_blockers);
    pthread_mutex_init(&conf->xprt_lock, NULL);
    INIT_LIST_HEAD(&conf->xprt_list);
    pthread_mutex_init(&conf->import_volumes, NULL);

    glusterd_friend_sm_init();
    glusterd_op_sm_init();
    glusterd_opinfo_init();
    ret = glusterd_sm_tr_log_init(
        &conf->op_sm_log, glusterd_op_sm_state_name_get,
        glusterd_op_sm_event_name_get, GLUSTERD_TR_LOG_SIZE);
    if (ret)
        goto out;

    conf->base_port = GF_IANA_PRIV_PORTS_START;
    if (dict_get_uint32(this->options, "base-port", &conf->base_port) == 0) {
        gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_DICT_SET_FAILED,
               "base-port override: %d", conf->base_port);
    }
    conf->max_port = GF_PORT_MAX;
    if (dict_get_uint32(this->options, "max-port", &conf->max_port) == 0) {
        gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_DICT_SET_FAILED,
               "max-port override: %d", conf->max_port);
    }

    conf->mgmt_v3_lock_timeout = GF_LOCK_TIMER;
    if (dict_get_time(this->options, "lock-timer",
                      &conf->mgmt_v3_lock_timeout) == 0) {
        gf_msg(this->name, GF_LOG_INFO, 0, GD_MSG_DICT_SET_FAILED,
               "lock-timer override: %ld", conf->mgmt_v3_lock_timeout);
    }

    /* Set option to run bricks on valgrind if enabled in glusterd.vol */
    this->ctx->cmd_args.vgtool = vgtool;
    ret = dict_get_str(this->options, "run-with-valgrind", &valgrind_str);
    if (ret < 0) {
        gf_msg_debug(this->name, 0, "cannot get run-with-valgrind value");
    }
    if (valgrind_str) {
        gf_boolean_t vg = _gf_false;

        if (!strcmp(valgrind_str, "memcheck"))
            this->ctx->cmd_args.vgtool = _gf_memcheck;
        else if (!strcmp(valgrind_str, "drd"))
            this->ctx->cmd_args.vgtool = _gf_drd;
        else if (!gf_string2boolean(valgrind_str, &vg))
            this->ctx->cmd_args.vgtool = (vg ? _gf_memcheck : _gf_none);
        else
            gf_msg(this->name, GF_LOG_WARNING, EINVAL, GD_MSG_INVALID_ENTRY,
                   "run-with-valgrind is neither boolean"
                   " nor one of 'memcheck' or 'drd'");
    }

    /* Store ping-timeout in conf */
    ret = dict_get_time(this->options, "ping-timeout", &conf->ping_timeout);
    /* Not failing here since ping-timeout can be optional as well */

    glusterd_mgmt_v3_lock_init();
    glusterd_mgmt_v3_lock_timer_init();
    glusterd_txn_opinfo_dict_init();

#ifdef BUILD_GNFS
    glusterd_nfssvc_build(&conf->nfs_svc);
#endif
    glusterd_quotadsvc_build(&conf->quotad_svc);
    glusterd_bitdsvc_build(&conf->bitd_svc);
    glusterd_scrubsvc_build(&conf->scrub_svc);

    /* Make install copies few of the hook-scripts by creating hooks
     * directory. Hence purposefully not doing the check for the presence of
     * hooks directory. Doing so avoids creation of complete hooks directory
     * tree.
     */
    ret = glusterd_hooks_create_hooks_directory(conf->workdir);
    if (-1 == ret) {
        gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_DIR_OP_FAILED,
               "Unable to create hooks directory ");
        exit(1);
    }

    CDS_INIT_LIST_HEAD(&conf->mount_specs);

    ret = dict_foreach(this->options, _install_mount_spec, NULL);
    if (ret)
        goto out;
    ret = dict_get_str(this->options, "mountbroker-root", &mountbroker_root);
    if (ret)
        ret = 0;
    else
        ret = check_prepare_mountbroker_root(mountbroker_root);
    if (ret)
        goto out;

    ret = is_upgrade(this->options, &upgrade);
    if (ret)
        goto out;

    ret = is_downgrade(this->options, &downgrade);
    if (ret)
        goto out;

    if (!upgrade && !downgrade) {
        ret = configure_syncdaemon(conf);
        if (ret)
            goto out;
    }

    /* Restoring op-version needs to be done before initializing the
     * services as glusterd_svc_init_common () invokes
     * glusterd_conn_build_socket_filepath () which uses MY_UUID macro.
     * MY_UUID generates a new uuid if its not been generated and writes it
     * in the info file, Since the op-version is not read yet
     * the default value i.e. 0 will be written for op-version and restore
     * will fail. This is why restoring op-version needs to happen before
     * service initialization
     * */
    ret = glusterd_restore_op_version(this);
    if (ret) {
        gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_OP_VERS_RESTORE_FAIL,
               "Failed to restore op_version");
        goto out;
    }

    ret = glusterd_restore();
    if (ret < 0)
        goto out;

    if (dict_get_str(conf->opts, GLUSTERD_LOCALTIME_LOGGING_KEY,
                     &localtime_logging) == 0) {
        int already_enabled = gf_log_get_localtime();

        if (strcmp(localtime_logging, "enable") == 0) {
            gf_log_set_localtime(1);
            if (!already_enabled)
                gf_msg(this->name, GF_LOG_INFO, 0,
                       GD_MSG_LOCALTIME_LOGGING_ENABLE,
                       "localtime logging enable");
        } else if (strcmp(localtime_logging, "disable") == 0) {
            gf_log_set_localtime(0);
            if (already_enabled)
                gf_msg(this->name, GF_LOG_INFO, 0,
                       GD_MSG_LOCALTIME_LOGGING_DISABLE,
                       "localtime logging disable");
        }
    }

    GF_ATOMIC_INIT(conf->blockers, 0);
    ret = glusterd_handle_upgrade_downgrade(this->options, conf, upgrade,
                                            downgrade);
    if (ret)
        goto out;

    ret = glusterd_retrieve_max_op_version(this, &op_version);
    /* first condition indicates file isn't present which means this code
     * change is hitting for the first time or someone has deleted it from the
     * backend.second condition is when max op_version differs, in both cases
     * volfiles should be regenerated
     */
    if (op_version == 0 || op_version != GD_OP_VERSION_MAX) {
        gf_log(this->name, GF_LOG_INFO,
               "Regenerating volfiles due to a max op-version mismatch or "
               "glusterd.upgrade file not being present, op_version retrieved:"
               "%d, max op_version: %d",
               op_version, GD_OP_VERSION_MAX);
        glusterd_recreate_volfiles(conf);
        ret = glusterd_store_max_op_version(this);
        if (ret)
            gf_log(this->name, GF_LOG_ERROR, "Failed to store max op-version");
    }

    /* If the peer count is less than 2 then this would be the best time to
     * spawn process/bricks that may need (re)starting since last time
     * (this) glusterd was up. */
    if (glusterd_get_peers_count() < 2)
        glusterd_launch_synctask(glusterd_spawn_daemons, NULL);

    ret = glusterd_hooks_spawn_worker(this);
    if (ret)
        goto out;

    GF_OPTION_INIT("event-threads", workers, int32, out);
    if (workers > 0 && workers != conf->workers) {
        conf->workers = workers;
        ret = gf_event_reconfigure_threads(this->ctx->event_pool, workers);
        if (ret)
            goto out;
    }

    ret = 0;
out:
    if (ret < 0) {
        if (this->private != NULL) {
            GF_FREE(this->private);
            this->private = NULL;
        }
    }

    return ret;
}

/*
 * fini - finish function for glusterd, called before
 *        unloading gluster.
 *
 * @this:
 *
 */
void
fini(xlator_t *this)
{
    if (!this || !this->private)
        goto out;

    glusterd_conf_t *priv = NULL;
    priv = this->private;

    glusterd_stop_uds_listener(this);                 /*stop unix socket rpc*/
    glusterd_stop_listener(this);                     /*stop tcp/ip socket rpc*/
    glusterd_destroy_hostname_list(&priv->hostnames); /*Destroy hostname list */
    glusterd_destroy_hostname_list(
        &priv->remote_hostnames); /*Destroy remote hostname list*/

#if 0
       /* Running threads might be using these resourses, we have to cancel/stop
        * running threads before deallocating the memory, but we don't have
        * control over the running threads to do pthread_cancel().
        * So memory freeing handover to kernel.
        */
        /*TODO: cancel/stop the running threads*/

        GF_FREE (conf->uds_rpc);
        GF_FREE (conf->rpc);
        FREE (conf->pmap);
        if (conf->handle)
                gf_store_handle_destroy (conf->handle);
        glusterd_sm_tr_log_delete (&conf->op_sm_log);
        glusterd_mgmt_v3_lock_fini ();
        glusterd_mgmt_v3_lock_timer_fini ();
        glusterd_txn_opinfo_dict_fini ();
        GF_FREE (conf);

        this->private = NULL;
#endif
out:
    return;
}

/*
 * notify - notify function for glusterd
 * @this:
 * @trans:
 * @event:
 *
 */
int
notify(xlator_t *this, int32_t event, void *data, ...)
{
    int ret = 0;

    switch (event) {
        case GF_EVENT_POLLIN:
            break;

        case GF_EVENT_POLLERR:
            break;

        case GF_EVENT_CLEANUP:
            break;

        default:
            default_notify(this, event, data);
            break;
    }

    return ret;
}

struct xlator_fops fops;

struct xlator_cbks cbks;

struct xlator_dumpops dumpops = {
    .priv = glusterd_dump_priv,
};

struct volume_options options[] = {
    {
        .key = {"working-directory"},
        .type = GF_OPTION_TYPE_PATH,
    },
    {
        .key = {"transport-type"},
        .type = GF_OPTION_TYPE_ANY,
    },
    {
        .key = {"transport.*"},
        .type = GF_OPTION_TYPE_ANY,
    },
    {
        .key = {"rpc-auth.*"},
        .type = GF_OPTION_TYPE_ANY,
    },
    {
        .key = {"rpc-auth-allow-insecure"},
        .type = GF_OPTION_TYPE_BOOL,
    },
    {
        .key = {"upgrade"},
        .type = GF_OPTION_TYPE_BOOL,
    },
    {
        .key = {"downgrade"},
        .type = GF_OPTION_TYPE_BOOL,
    },
    {
        .key = {"bind-insecure"},
        .type = GF_OPTION_TYPE_BOOL,
    },
    {
        .key = {"mountbroker-root"},
        .type = GF_OPTION_TYPE_PATH,
    },
    {
        .key = {"mountbroker.*"},
        .type = GF_OPTION_TYPE_ANY,
    },
    {
        .key = {"mountbroker-" GEOREP ".*"},
        .type = GF_OPTION_TYPE_ANY,
    },
    {
        .key = {GEOREP "-log-group"},
        .type = GF_OPTION_TYPE_ANY,
    },
    {
        .key = {"run-with-valgrind"},
        .type = GF_OPTION_TYPE_BOOL,
    },
    {.key = {"server-quorum-type"},
     .type = GF_OPTION_TYPE_STR,
     .value = {"none", "server"},
     .default_value = "none",
     .description = "It can be set to none or server. When set to server, "
                    "this option enables the specified volume to "
                    "participate in the server-side quorum. "
                    "This feature is on the server-side i.e. in glusterd. "
                    "Whenever the glusterd on a machine observes that "
                    "the quorum is not met, it brings down the bricks to "
                    "prevent data split-brains. When the network "
                    "connections are brought back up and the quorum is "
                    "restored the bricks in   "
                    "the volume are brought back up."},
    {.key = {"server-quorum-ratio"},
     .type = GF_OPTION_TYPE_PERCENT,
     .description = "Sets the quorum percentage for the trusted "
                    "storage pool."},
    {.key = {"glusterd-sockfile"},
     .type = GF_OPTION_TYPE_PATH,
     .description = "The socket file on which glusterd should listen for "
                    "cli requests. Default is " DEFAULT_GLUSTERD_SOCKFILE "."},
    {.key = {"base-port"},
     .type = GF_OPTION_TYPE_INT,
     .description = "Sets the base port for portmap query"},
    {.key = {"max-port"},
     .type = GF_OPTION_TYPE_INT,
     .max = GF_PORT_MAX,
     .description = "Sets the max port for portmap query"},
    {.key = {"mgmt-v3-lock-timeout"},
     .type = GF_OPTION_TYPE_INT,
     .max = 600,
     .description = "Sets the mgmt-v3-lock-timeout for transactions."
                    "Specifes the default timeout value after which "
                    "lock acquired while performing transaction will "
                    "be released."},
    {.key = {"snap-brick-path"},
     .type = GF_OPTION_TYPE_STR,
     .description =
         "directory where the bricks for the snapshots will be created"},
    {
        .key = {"ping-timeout"},
        .type = GF_OPTION_TYPE_TIME,
        .min = 0,
        .max = 300,
        .default_value = TOSTRING(RPC_DEFAULT_PING_TIMEOUT),
    },
    {.key = {"event-threads"},
     .type = GF_OPTION_TYPE_INT,
     .min = GLUSTERD_MIN_EVENT_THREADS,
     .max = GLUSTERD_MAX_EVENT_THREADS,
     .default_value = TOSTRING(STARTING_EVENT_THREADS),
     .description = "Specifies the number of event threads to execute "
                    "in parallel. Larger values would help process"
                    " responses faster, depending on available processing"
                    " power. Range 1-32 threads."},
    {.key = {NULL}},
};

xlator_api_t xlator_api = {
    .init = init,
    .fini = fini,
    .mem_acct_init = mem_acct_init,
    .op_version = {1}, /* Present from the initial version */
    .dumpops = &dumpops,
    .fops = &fops,
    .cbks = &cbks,
    .options = options,
    .identifier = "glusterd",
    .category = GF_MAINTAINED,
};
