/*
   Copyright (c) 2010-2012 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>

#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>

#include "cli.h"
#include "cli-cmd.h"
#include "cli-mem-types.h"
#include "cli1-xdr.h"
#include <glusterfs/run.h>
#include <glusterfs/syscall.h>
#include <glusterfs/common-utils.h>
#include <glusterfs/events.h>

extern rpc_clnt_prog_t cli_quotad_clnt;

static int
gf_asprintf_append(char **string_ptr, const char *format, ...);

int
cli_cmd_volume_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word,
                        const char **words, int wordcount);

int
cli_cmd_bitrot_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word,
                        const char **words, int wordcount);

int
cli_cmd_quota_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word,
                       const char **words, int wordcount);

int
cli_cmd_volume_info_cbk(struct cli_state *state, struct cli_cmd_word *word,
                        const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    cli_cmd_volume_get_ctx_t ctx = {
        0,
    };
    cli_local_t *local = NULL;
    int sent = 0;
    int parse_error = 0;

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_VOLUME];

    if ((wordcount == 2) || (wordcount == 3 && !strcmp(words[2], "all"))) {
        ctx.flags = GF_CLI_GET_NEXT_VOLUME;
        proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_NEXT_VOLUME];
    } else if (wordcount == 3) {
        ctx.flags = GF_CLI_GET_VOLUME;
        ctx.volname = (char *)words[2];
        if (strlen(ctx.volname) > GD_VOLUME_NAME_MAX) {
            cli_out("Invalid volume name");
            goto out;
        }
        proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_VOLUME];
    } else {
        cli_usage_out(word->pattern);
        parse_error = 1;
        return -1;
    }

    local = cli_local_get();

    if (!local)
        goto out;

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame)
        goto out;

    local->get_vol.flags = ctx.flags;
    if (ctx.volname)
        local->get_vol.volname = gf_strdup(ctx.volname);

    frame->local = local;

    if (proc->fn) {
        ret = proc->fn(frame, THIS, &ctx);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Getting Volume information failed!");
    }

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_sync_volume_cbk(struct cli_state *state, struct cli_cmd_word *word,
                        const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    int sent = 0;
    int parse_error = 0;
    dict_t *dict = NULL;
    cli_local_t *local = NULL;
    gf_answer_t answer = GF_ANSWER_NO;
    const char *question =
        "Sync volume may make data "
        "inaccessible while the sync "
        "is in progress. Do you want "
        "to continue?";

    if ((wordcount < 3) || (wordcount > 4)) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    dict = dict_new();
    if (!dict)
        goto out;

    if ((wordcount == 3) || !strcmp(words[3], "all")) {
        ret = dict_set_int32(dict, "flags", (int32_t)GF_CLI_SYNC_ALL);
        if (ret) {
            gf_log(THIS->name, GF_LOG_ERROR,
                   "failed to set"
                   "flag");
            goto out;
        }
    } else {
        ret = dict_set_str(dict, "volname", (char *)words[3]);
        if (ret) {
            gf_log(THIS->name, GF_LOG_ERROR,
                   "failed to set "
                   "volume");
            goto out;
        }
    }

    ret = dict_set_str(dict, "hostname", (char *)words[2]);
    if (ret) {
        gf_log(THIS->name, GF_LOG_ERROR, "failed to set hostname");
        goto out;
    }

    if (!(state->mode & GLUSTER_MODE_SCRIPT)) {
        answer = cli_cmd_get_confirmation(state, question);
        if (GF_ANSWER_NO == answer) {
            ret = 0;
            goto out;
        }
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_SYNC_VOLUME];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame");
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, dict);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, dict);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume sync failed");
    }

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_create_cbk(struct cli_state *state, struct cli_cmd_word *word,
                          const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;
    char *trans_type = NULL;
    char *bricks = NULL;

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_CREATE_VOLUME];

    ret = cli_cmd_volume_create_parse(state, words, wordcount, &options,
                                      &bricks);

    if (ret) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    ret = dict_get_str(options, "transport", &trans_type);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Unable to get transport type");
        goto out;
    }

    if (state->mode & GLUSTER_MODE_WIGNORE) {
        ret = dict_set_int32(options, "force", _gf_true);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Failed to set force "
                   "option");
            goto out;
        }
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume create failed");
    }

    if (ret == 0) {
        gf_event(EVENT_VOLUME_CREATE, "name=%s;bricks=%s", (char *)words[2],
                 bricks);
    }

    CLI_STACK_DESTROY(frame);
    return ret;
}

int
cli_cmd_volume_delete_cbk(struct cli_state *state, struct cli_cmd_word *word,
                          const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    char *volname = NULL;
    gf_answer_t answer = GF_ANSWER_NO;
    const char *question = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;
    dict_t *dict = NULL;

    question =
        "Deleting volume will erase all information about the volume. "
        "Do you want to continue?";
    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_DELETE_VOLUME];

    if (wordcount != 3) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    volname = (char *)words[2];

    dict = dict_new();
    if (!dict)
        goto out;

    ret = dict_set_str(dict, "volname", volname);
    if (ret) {
        gf_log(THIS->name, GF_LOG_WARNING, "dict set failed");
        goto out;
    }

    if (!strcmp(volname, GLUSTER_SHARED_STORAGE)) {
        question =
            "Deleting the shared storage volume"
            "(gluster_shared_storage), will affect features "
            "like snapshot scheduler, geo-replication "
            "and NFS-Ganesha. Do you still want to "
            "continue?";
    }

    answer = cli_cmd_get_confirmation(state, question);
    if (GF_ANSWER_NO == answer) {
        ret = 0;
        goto out;
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, dict);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, dict);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume delete failed");
    }

    CLI_STACK_DESTROY(frame);

    if (ret == 0 && GF_ANSWER_YES == answer) {
        gf_event(EVENT_VOLUME_DELETE, "name=%s", (char *)words[2]);
    }

    return ret;
}

int
cli_cmd_volume_start_cbk(struct cli_state *state, struct cli_cmd_word *word,
                         const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    int sent = 0;
    int parse_error = 0;
    dict_t *dict = NULL;
    int flags = 0;
    cli_local_t *local = NULL;

    if (wordcount < 3 || wordcount > 4) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    if (!words[2])
        goto out;

    if (wordcount == 4) {
        if (!strcmp("force", words[3])) {
            flags |= GF_CLI_FLAG_OP_FORCE;
        } else {
            ret = -1;
            cli_usage_out(word->pattern);
            parse_error = 1;
            goto out;
        }
    }

    dict = dict_new();
    if (!dict) {
        goto out;
    }

    ret = dict_set_str(dict, "volname", (char *)words[2]);
    if (ret) {
        gf_log(THIS->name, GF_LOG_ERROR, "dict set failed");
        goto out;
    }

    ret = dict_set_int32(dict, "flags", flags);
    if (ret) {
        gf_log(THIS->name, GF_LOG_ERROR, "dict set failed");
        goto out;
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_START_VOLUME];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, dict);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, dict);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume start failed");
    }

    CLI_STACK_DESTROY(frame);

    if (ret == 0) {
        gf_event(EVENT_VOLUME_START, "name=%s;force=%d", (char *)words[2],
                 (flags & GF_CLI_FLAG_OP_FORCE));
    }

    return ret;
}

gf_answer_t
cli_cmd_get_confirmation(struct cli_state *state, const char *question)
{
    char answer[5] = {
        '\0',
    };
    int flush = '\0';
    size_t len;

    if (state->mode & GLUSTER_MODE_SCRIPT)
        return GF_ANSWER_YES;

    printf("%s (y/n) ", question);

    if (fgets(answer, 4, stdin) == NULL) {
        cli_out("gluster cli read error");
        goto out;
    }

    len = strlen(answer);

    if (len && answer[len - 1] == '\n') {
        answer[--len] = '\0';
    } else {
        do {
            flush = getchar();
        } while (flush != '\n');
    }

    if (len > 3)
        goto out;

    if (!strcasecmp(answer, "y") || !strcasecmp(answer, "yes"))
        return GF_ANSWER_YES;

    else if (!strcasecmp(answer, "n") || !strcasecmp(answer, "no"))
        return GF_ANSWER_NO;

out:
    cli_out("Invalid input, please enter y/n");

    return GF_ANSWER_NO;
}

int
cli_cmd_volume_stop_cbk(struct cli_state *state, struct cli_cmd_word *word,
                        const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    int flags = 0;
    gf_answer_t answer = GF_ANSWER_NO;
    int sent = 0;
    int parse_error = 0;
    dict_t *dict = NULL;
    char *volname = NULL;
    cli_local_t *local = NULL;

    const char *question =
        "Stopping volume will make its data inaccessible. "
        "Do you want to continue?";

    if (wordcount < 3 || wordcount > 4) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    volname = (char *)words[2];

    dict = dict_new();
    ret = dict_set_str(dict, "volname", volname);
    if (ret) {
        gf_log(THIS->name, GF_LOG_ERROR, "dict set failed");
        goto out;
    }

    if (!strcmp(volname, GLUSTER_SHARED_STORAGE)) {
        question =
            "Stopping the shared storage volume"
            "(gluster_shared_storage), will affect features "
            "like snapshot scheduler, geo-replication "
            "and NFS-Ganesha. Do you still want to "
            "continue?";
    }

    if (wordcount == 4) {
        if (!strcmp("force", words[3])) {
            flags |= GF_CLI_FLAG_OP_FORCE;
        } else {
            ret = -1;
            cli_usage_out(word->pattern);
            parse_error = 1;
            goto out;
        }
    }

    ret = dict_set_int32(dict, "flags", flags);
    if (ret) {
        gf_log(THIS->name, GF_LOG_ERROR, "dict set failed");
        goto out;
    }

    answer = cli_cmd_get_confirmation(state, question);

    if (GF_ANSWER_NO == answer) {
        ret = 0;
        goto out;
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_STOP_VOLUME];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, dict);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, dict);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume stop on '%s' failed", volname);
    }

    CLI_STACK_DESTROY(frame);
    if (dict)
        dict_unref(dict);

    if (ret == 0 && GF_ANSWER_YES == answer) {
        gf_event(EVENT_VOLUME_STOP, "name=%s;force=%d", (char *)words[2],
                 (flags & GF_CLI_FLAG_OP_FORCE));
    }

    return ret;
}

int
cli_cmd_volume_rename_cbk(struct cli_state *state, struct cli_cmd_word *word,
                          const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *dict = NULL;
    int sent = 0;
    int parse_error = 0;

    if (wordcount != 4) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    dict = dict_new();
    if (!dict)
        goto out;

    ret = dict_set_str(dict, "old-volname", (char *)words[2]);

    if (ret)
        goto out;

    ret = dict_set_str(dict, "new-volname", (char *)words[3]);

    if (ret)
        goto out;

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_RENAME_VOLUME];

    if (proc->fn) {
        frame = create_frame(THIS, THIS->ctx->pool);
        if (!frame) {
            ret = -1;
            goto out;
        }
        ret = proc->fn(frame, THIS, dict);
    }

out:
    if (dict)
        dict_unref(dict);

    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume rename on '%s' failed", (char *)words[2]);
    }

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_defrag_cbk(struct cli_state *state, struct cli_cmd_word *word,
                          const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *dict = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;
#if (USE_EVENTS)
    eventtypes_t event = EVENT_LAST;
#endif

#ifdef GF_SOLARIS_HOST_OS
    cli_out("Command not supported on Solaris");
    goto out;
#endif

    ret = cli_cmd_volume_defrag_parse(words, wordcount, &dict);

    if (ret) {
        cli_usage_out(word->pattern);
        parse_error = 1;
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_DEFRAG_VOLUME];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, dict);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, dict);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume rebalance failed");
    } else {
#if (USE_EVENTS)
        if (!(strcmp(words[wordcount - 1], "start")) ||
            !(strcmp(words[wordcount - 1], "force"))) {
            event = EVENT_VOLUME_REBALANCE_START;
        } else if (!strcmp(words[wordcount - 1], "stop")) {
            event = EVENT_VOLUME_REBALANCE_STOP;
        }

        if (event != EVENT_LAST)
            gf_event(event, "volume=%s", (char *)words[2]);
#endif
    }

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_reset_cbk(struct cli_state *state, struct cli_cmd_word *word,
                         const char **words, int wordcount)
{
    int sent = 0;
    int parse_error = 0;
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    cli_local_t *local = NULL;
#if (USE_EVENTS)
    int ret1 = -1;
    char *tmp_opt = NULL;
#endif

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_RESET_VOLUME];

    ret = cli_cmd_volume_reset_parse(words, wordcount, &options);
    if (ret) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume reset failed");
    }

#if (USE_EVENTS)
    if (ret == 0) {
        ret1 = dict_get_str(options, "key", &tmp_opt);
        if (ret1)
            tmp_opt = "";

        gf_event(EVENT_VOLUME_RESET, "name=%s;option=%s", (char *)words[2],
                 tmp_opt);
    }
#endif

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_profile_cbk(struct cli_state *state, struct cli_cmd_word *word,
                           const char **words, int wordcount)
{
    int sent = 0;
    int parse_error = 0;

    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    cli_local_t *local = NULL;

    ret = cli_cmd_volume_profile_parse(words, wordcount, &options);

    if (ret) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_PROFILE_VOLUME];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame");
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume profile failed");
    }

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_set_cbk(struct cli_state *state, struct cli_cmd_word *word,
                       const char **words, int wordcount)
{
    int sent = 0;
    int parse_error = 0;

    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    cli_local_t *local = NULL;
    char *op_errstr = NULL;

#if (USE_EVENTS)
    int ret1 = -1;
    int i = 1;
    char dict_key[50] = {
        0,
    };
    char *tmp_opt = NULL;
    char *opts_str = NULL;
    int num_options = 0;
#endif

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_SET_VOLUME];

    ret = cli_cmd_volume_set_parse(state, words, wordcount, &options,
                                   &op_errstr);
    if (ret) {
        if (op_errstr) {
            cli_err("%s", op_errstr);
            GF_FREE(op_errstr);
        } else
            cli_usage_out(word->pattern);

        parse_error = 1;
        goto out;
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume set failed");
    }

#if (USE_EVENTS)
    if (ret == 0 && strcmp(words[2], "help") != 0) {
        ret1 = dict_get_int32(options, "count", &num_options);
        if (ret1) {
            num_options = 0;
            goto end;
        } else {
            num_options = num_options / 2;
        }

        char *free_list_key[num_options];
        char *free_list_val[num_options];
        for (i = 0; i < num_options; i++) {
            free_list_key[i] = NULL;
            free_list_val[i] = NULL;
        }
        /* Initialize opts_str */
        opts_str = "";

        /* Prepare String in format options=KEY1,VALUE1,KEY2,VALUE2 */
        for (i = 1; i <= num_options; i++) {
            sprintf(dict_key, "key%d", i);
            ret1 = dict_get_str(options, dict_key, &tmp_opt);
            if (ret1)
                tmp_opt = "";

            gf_asprintf(&opts_str, "%s,%s", opts_str, tmp_opt);
            free_list_key[i - 1] = opts_str;

            sprintf(dict_key, "value%d", i);
            ret1 = dict_get_str(options, dict_key, &tmp_opt);
            if (ret1)
                tmp_opt = "";

            gf_asprintf(&opts_str, "%s,%s", opts_str, tmp_opt);
            free_list_val[i - 1] = opts_str;
        }

        gf_event(EVENT_VOLUME_SET, "name=%s;options=%s", (char *)words[2],
                 opts_str);

        /* Allocated by gf_strdup and gf_asprintf */
        for (i = 0; i < num_options; i++) {
            GF_FREE(free_list_key[i]);
            GF_FREE(free_list_val[i]);
        }
    }
#endif

end:
    CLI_STACK_DESTROY(frame);

    return ret;
}

static int
cli_event_remove_brick_str(dict_t *options, char **event_str,
                           eventtypes_t *event)
{
    int ret = -1;
    char *bricklist = NULL;
    char *brick = NULL;
    char *volname = NULL;
    char key[256] = {
        0,
    };
    const char *eventstrformat = "volume=%s;bricks=%s";
    int32_t command = 0;
    int32_t i = 1;
    int32_t count = 0;
    int32_t eventstrlen = 1;
    int bricklen = 0;
    char *tmp_ptr = NULL;

    if (!options || !event_str || !event)
        goto out;

    ret = dict_get_str(options, "volname", &volname);
    if (ret || !volname) {
        gf_log("cli", GF_LOG_ERROR, "Failed to fetch volname");
        ret = -1;
        goto out;
    }
    /* Get the list of bricks for the event */
    ret = dict_get_int32(options, "command", &command);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to fetch command");
        ret = -1;
        goto out;
    }

    switch (command) {
        case GF_OP_CMD_START:
            *event = EVENT_VOLUME_REMOVE_BRICK_START;
            break;
        case GF_OP_CMD_COMMIT:
            *event = EVENT_VOLUME_REMOVE_BRICK_COMMIT;
            break;
        case GF_OP_CMD_COMMIT_FORCE:
            *event = EVENT_VOLUME_REMOVE_BRICK_FORCE;
            break;
        case GF_OP_CMD_STOP:
            *event = EVENT_VOLUME_REMOVE_BRICK_STOP;
            break;
        default:
            *event = EVENT_LAST;
            break;
    }

    ret = -1;

    if (*event == EVENT_LAST) {
        goto out;
    }

    /* I could just get this from words[] but this is cleaner in case the
     * format changes  */
    while (i) {
        snprintf(key, sizeof(key), "brick%d", i);
        ret = dict_get_str(options, key, &brick);
        if (ret) {
            break;
        }
        eventstrlen += strlen(brick) + 1;
        i++;
    }

    count = --i;

    eventstrlen += 1;

    bricklist = GF_CALLOC(eventstrlen, sizeof(char), gf_common_mt_char);
    if (!bricklist) {
        gf_log(THIS->name, GF_LOG_ERROR,
               "memory allocation failed for"
               "bricklist");
        ret = -1;
        goto out;
    }

    tmp_ptr = bricklist;

    i = 1;
    while (i <= count) {
        snprintf(key, sizeof(key), "brick%d", i);
        ret = dict_get_str(options, key, &brick);
        if (ret) {
            break;
        }
        snprintf(tmp_ptr, eventstrlen, "%s ", brick);
        bricklen = strlen(brick);
        eventstrlen -= (bricklen + 1);
        tmp_ptr += (bricklen + 1);
        i++;
    }

    if (!ret) {
        gf_asprintf(event_str, eventstrformat, volname, bricklist);
    } else {
        gf_asprintf(event_str, eventstrformat, volname, "<unavailable>");
    }

    ret = 0;
out:
    GF_FREE(bricklist);
    return ret;
}

int
cli_cmd_volume_add_brick_cbk(struct cli_state *state, struct cli_cmd_word *word,
                             const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_error = 0;
    gf_answer_t answer = GF_ANSWER_NO;
    cli_local_t *local = NULL;

#if (USE_EVENTS)
    char *event_str = NULL;
    char *bricks = NULL;
    const char *eventstrformat = "volume=%s;bricks=%s";
#endif

    const char *question =
        "Changing the 'stripe count' of the volume is "
        "not a supported feature. In some cases it may result in data "
        "loss on the volume. Also there may be issues with regular "
        "filesystem operations on the volume after the change. Do you "
        "really want to continue with 'stripe' count option ? ";

    ret = cli_cmd_volume_add_brick_parse(state, words, wordcount, &options, 0);
    if (ret) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    /* TODO: there are challenges in supporting changing of
       stripe-count, until it is properly supported give warning to user */
    if (dict_get(options, "stripe-count")) {
        answer = cli_cmd_get_confirmation(state, question);

        if (GF_ANSWER_NO == answer) {
            ret = 0;
            goto out;
        }
    }

#if (USE_EVENTS)
    /* Get the list of bricks for the event */

    ret = dict_get_str(options, "bricks", &bricks);

    if (!ret) {
        gf_asprintf(&event_str, eventstrformat, (char *)words[2],
                    &bricks[1] /*Skip leading space*/);
    } else {
        gf_asprintf(&event_str, eventstrformat, (char *)words[2],
                    "<unavailable>");
    }
#endif

    if (state->mode & GLUSTER_MODE_WIGNORE) {
        ret = dict_set_int32(options, "force", _gf_true);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Failed to set force "
                   "option");
            goto out;
        }
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_ADD_BRICK];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume add-brick failed");
    } else {
#if (USE_EVENTS)
        gf_event(EVENT_VOLUME_ADD_BRICK, "%s", event_str);
#endif
    }
#if (USE_EVENTS)
    GF_FREE(event_str);
#endif

    CLI_STACK_DESTROY(frame);
    return ret;
}

int
cli_get_soft_limit(dict_t *options, const char **words, dict_t *xdata)
{
    call_frame_t *frame = NULL;
    cli_local_t *local = NULL;
    rpc_clnt_procedure_t *proc = NULL;
    char *default_sl = NULL;
    char *default_sl_dup = NULL;
    int ret = -1;

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    // We need a ref on @options to prevent CLI_STACK_DESTROY
    // from destroying it prematurely.
    dict_ref(options);
    CLI_LOCAL_INIT(local, words, frame, options);
    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_QUOTA];
    ret = proc->fn(frame, THIS, options);

    ret = dict_get_str(options, "default-soft-limit", &default_sl);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to get default soft limit");
        goto out;
    }

    default_sl_dup = gf_strdup(default_sl);
    if (!default_sl_dup) {
        ret = -1;
        goto out;
    }

    ret = dict_set_dynstr(xdata, "default-soft-limit", default_sl_dup);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to set default soft limit");
        GF_FREE(default_sl_dup);
        goto out;
    }

out:
    CLI_STACK_DESTROY(frame);
    return ret;
}

/* Checks if at least one limit has been set on the volume
 *
 * Returns true if at least one limit is set. Returns false otherwise.
 */
gf_boolean_t
_limits_set_on_volume(char *volname, int type)
{
    gf_boolean_t limits_set = _gf_false;
    int ret = -1;
    char quota_conf_file[PATH_MAX] = {
        0,
    };
    int fd = -1;
    char buf[16] = {
        0,
    };
    float version = 0.0f;
    char gfid_type_stored = 0;
    char gfid_type = 0;

    /* TODO: fix hardcoding; Need to perform an RPC call to glusterd
     * to fetch working directory
     */
    snprintf(quota_conf_file, sizeof quota_conf_file, "%s/vols/%s/quota.conf",
             GLUSTERD_DEFAULT_WORKDIR, volname);
    fd = open(quota_conf_file, O_RDONLY);
    if (fd == -1)
        goto out;

    ret = quota_conf_read_version(fd, &version);
    if (ret)
        goto out;

    if (type == GF_QUOTA_OPTION_TYPE_LIST)
        gfid_type = GF_QUOTA_CONF_TYPE_USAGE;
    else
        gfid_type = GF_QUOTA_CONF_TYPE_OBJECTS;

    /* Try to read at least one gfid  of type 'gfid_type' */
    while (1) {
        ret = quota_conf_read_gfid(fd, buf, &gfid_type_stored, version);
        if (ret <= 0)
            break;

        if (gfid_type_stored == gfid_type) {
            limits_set = _gf_true;
            break;
        }
    }
out:
    if (fd != -1)
        sys_close(fd);

    return limits_set;
}

int
cli_cmd_quota_handle_list_all(const char **words, dict_t *options)
{
    int all_failed = 1;
    int count = 0;
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    cli_local_t *local = NULL;
    call_frame_t *frame = NULL;
    dict_t *xdata = NULL;
    char gfid_str[UUID_CANONICAL_FORM_LEN + 1];
    char *volname = NULL;
    char *volname_dup = NULL;
    unsigned char buf[16] = {0};
    int fd = -1;
    char quota_conf_file[PATH_MAX] = {0};
    gf_boolean_t xml_err_flag = _gf_false;
    char err_str[NAME_MAX] = {
        0,
    };
    int32_t type = 0;
    char gfid_type = 0;
    float version = 0.0f;
    int32_t max_count = 0;

    xdata = dict_new();
    if (!xdata) {
        ret = -1;
        goto out;
    }

    ret = dict_get_str(options, "volname", &volname);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to get volume name");
        goto out;
    }

    ret = dict_get_int32(options, "type", &type);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to get quota option type");
        goto out;
    }

    ret = dict_set_int32(xdata, "type", type);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to set type in xdata");
        goto out;
    }

    ret = cli_get_soft_limit(options, words, xdata);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR,
               "Failed to fetch default "
               "soft-limit");
        goto out;
    }

    /* Check if at least one limit is set on volume. No need to check for
     * quota enabled as cli_get_soft_limit() handles that
     */
    if (!_limits_set_on_volume(volname, type)) {
        snprintf(err_str, sizeof(err_str),
                 "No%s quota configured on"
                 " volume %s",
                 (type == GF_QUOTA_OPTION_TYPE_LIST) ? "" : " inode", volname);
        if (global_state->mode & GLUSTER_MODE_XML) {
            xml_err_flag = _gf_true;
        } else {
            cli_out("quota: %s", err_str);
        }
        ret = 0;
        goto out;
    }

    volname_dup = gf_strdup(volname);
    if (!volname_dup) {
        ret = -1;
        goto out;
    }

    ret = dict_set_dynstr(xdata, "volume-uuid", volname_dup);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to set volume-uuid");
        GF_FREE(volname_dup);
        goto out;
    }

    // TODO: fix hardcoding; Need to perform an RPC call to glusterd
    // to fetch working directory
    snprintf(quota_conf_file, sizeof quota_conf_file, "%s/vols/%s/quota.conf",
             GLUSTERD_DEFAULT_WORKDIR, volname);
    fd = open(quota_conf_file, O_RDONLY);
    if (fd == -1) {
        // This may because no limits were yet set on the volume
        gf_log("cli", GF_LOG_TRACE,
               "Unable to open "
               "quota.conf");
        ret = 0;
        goto out;
    }

    ret = quota_conf_read_version(fd, &version);
    if (ret)
        goto out;

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, xdata);
    proc = &cli_quotad_clnt.proctable[GF_AGGREGATOR_GETLIMIT];

    for (count = 0;; count++) {
        ret = quota_conf_read_gfid(fd, buf, &gfid_type, version);
        if (ret == 0) {
            break;
        } else if (ret < 0) {
            gf_log(THIS->name, GF_LOG_CRITICAL,
                   "Quota "
                   "configuration store may be corrupt.");
            goto out;
        }

        if ((type == GF_QUOTA_OPTION_TYPE_LIST &&
             gfid_type == GF_QUOTA_CONF_TYPE_OBJECTS) ||
            (type == GF_QUOTA_OPTION_TYPE_LIST_OBJECTS &&
             gfid_type == GF_QUOTA_CONF_TYPE_USAGE))
            continue;

        max_count++;
    }
    ret = dict_set_int32(xdata, "max_count", max_count);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to set max_count");
        goto out;
    }

    ret = sys_lseek(fd, 0L, SEEK_SET);
    if (ret < 0) {
        gf_log(THIS->name, GF_LOG_ERROR,
               "failed to move offset to "
               "the beginning: %s",
               strerror(errno));
        goto out;
    }
    ret = quota_conf_read_version(fd, &version);
    if (ret)
        goto out;

    for (count = 0;; count++) {
        ret = quota_conf_read_gfid(fd, buf, &gfid_type, version);
        if (ret == 0) {
            break;
        } else if (ret < 0) {
            gf_log(THIS->name, GF_LOG_CRITICAL,
                   "Quota "
                   "configuration store may be corrupt.");
            goto out;
        }

        if ((type == GF_QUOTA_OPTION_TYPE_LIST &&
             gfid_type == GF_QUOTA_CONF_TYPE_OBJECTS) ||
            (type == GF_QUOTA_OPTION_TYPE_LIST_OBJECTS &&
             gfid_type == GF_QUOTA_CONF_TYPE_USAGE))
            continue;

        uuid_utoa_r(buf, gfid_str);
        ret = dict_set_str(xdata, "gfid", gfid_str);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR, "Failed to set gfid");
            goto out;
        }

        ret = proc->fn(frame, THIS, xdata);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Failed to get quota "
                   "limits for %s",
                   uuid_utoa((unsigned char *)buf));
        }

        dict_del(xdata, "gfid");
        all_failed = all_failed && ret;
    }

    if (global_state->mode & GLUSTER_MODE_XML) {
        ret = cli_xml_output_vol_quota_limit_list_end(local);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Error in printing "
                   "xml output");
            goto out;
        }
    }

    if (count > 0) {
        ret = all_failed ? -1 : 0;
    } else {
        ret = 0;
    }

out:
    if (xml_err_flag) {
        ret = cli_xml_output_str("volQuota", NULL, -1, 0, err_str);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Error outputting in "
                   "xml format");
        }
    }
    if (xdata)
        dict_unref(xdata);

    if (fd != -1) {
        sys_close(fd);
    }

    if (ret) {
        gf_log("cli", GF_LOG_ERROR,
               "Could not fetch and display quota"
               " limits");
    }
    CLI_STACK_DESTROY(frame);
    return ret;
}

int
cli_cmd_bitrot_cbk(struct cli_state *state, struct cli_cmd_word *word,
                   const char **words, int wordcount)
{
    int ret = -1;
    int parse_err = 0;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    cli_local_t *local = NULL;
    rpc_clnt_procedure_t *proc = NULL;
    int sent = 0;
#if (USE_EVENTS)
    int cmd_type = -1;
    int ret1 = -1;
    int event_type = -1;
    char *tmp = NULL;
    char *events_str = NULL;
    char *volname = NULL;
#endif

    ret = cli_cmd_bitrot_parse(words, wordcount, &options);
    if (ret < 0) {
        cli_usage_out(word->pattern);
        parse_err = 1;
        goto out;
    }

    if (ret == 1) {
        /* this is 'volume bitrot help' */
        cli_cmd_bitrot_help_cbk(state, word, words, wordcount);
        ret = 0;
        goto out2;
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_BITROT];

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_err == 0))
            cli_err(
                "Bit rot command failed. Please check the cli "
                "logs for more details");
    }

#if (USE_EVENTS)
    if (ret == 0) {
        ret1 = dict_get_int32(options, "type", &cmd_type);
        if (ret1)
            cmd_type = -1;
        else {
            ret1 = dict_get_str(options, "volname", &volname);
            if (ret1)
                volname = "";
        }

        switch (cmd_type) {
            case GF_BITROT_OPTION_TYPE_ENABLE:
                event_type = EVENT_BITROT_ENABLE;
                break;
            case GF_BITROT_OPTION_TYPE_DISABLE:
                event_type = EVENT_BITROT_DISABLE;
                break;
            case GF_BITROT_CMD_SCRUB_ONDEMAND:
                event_type = EVENT_BITROT_SCRUB_ONDEMAND;
                break;
            case GF_BITROT_OPTION_TYPE_SCRUB_THROTTLE:
                event_type = EVENT_BITROT_SCRUB_THROTTLE;
                ret1 = dict_get_str(options, "scrub-throttle-value", &tmp);
                if (ret1)
                    tmp = "";
                gf_asprintf(&events_str, "name=%s;value=%s", volname, tmp);
                break;
            case GF_BITROT_OPTION_TYPE_SCRUB_FREQ:
                event_type = EVENT_BITROT_SCRUB_FREQ;
                ret1 = dict_get_str(options, "scrub-frequency-value", &tmp);
                if (ret1)
                    tmp = "";
                gf_asprintf(&events_str, "name=%s;value=%s", volname, tmp);
                break;
            case GF_BITROT_OPTION_TYPE_SCRUB:
                event_type = EVENT_BITROT_SCRUB_OPTION;
                ret1 = dict_get_str(options, "scrub-value", &tmp);
                if (ret1)
                    tmp = "";
                gf_asprintf(&events_str, "name=%s;value=%s", volname, tmp);
                break;
            default:
                break;
        }

        if (event_type > -1)
            gf_event(event_type, "%s", events_str);

        if (events_str)
            GF_FREE(events_str);
    }
#endif

    CLI_STACK_DESTROY(frame);
out2:
    return ret;
}

int
cli_cmd_quota_cbk(struct cli_state *state, struct cli_cmd_word *word,
                  const char **words, int wordcount)
{
    int ret = 0;
    int parse_err = 0;
    int32_t type = 0;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    gf_answer_t answer = GF_ANSWER_NO;
    cli_local_t *local = NULL;
    int sent = 0;
    char *volname = NULL;
    const char *question =
        "Disabling quota will delete all the quota "
        "configuration. Do you want to continue?";

    // parse **words into options dictionary
    if (strcmp(words[1], "inode-quota") == 0) {
        ret = cli_cmd_inode_quota_parse(words, wordcount, &options);
        if (ret < 0) {
            cli_usage_out(word->pattern);
            parse_err = 1;
            goto out;
        }
    } else {
        ret = cli_cmd_quota_parse(words, wordcount, &options);

        if (ret == 1) {
            cli_cmd_quota_help_cbk(state, word, words, wordcount);
            ret = 0;
            goto out;
        }
        if (ret < 0) {
            cli_usage_out(word->pattern);
            parse_err = 1;
            goto out;
        }
    }

    ret = dict_get_int32(options, "type", &type);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to get opcode");
        goto out;
    }

    // handle quota-disable and quota-list-all different from others
    switch (type) {
        case GF_QUOTA_OPTION_TYPE_DISABLE:
            answer = cli_cmd_get_confirmation(state, question);
            if (answer == GF_ANSWER_NO)
                goto out;
            break;
        case GF_QUOTA_OPTION_TYPE_LIST:
        case GF_QUOTA_OPTION_TYPE_LIST_OBJECTS:
            if (wordcount != 4)
                break;
            ret = cli_cmd_quota_handle_list_all(words, options);
            goto out;
        default:
            break;
    }

    ret = dict_get_str(options, "volname", &volname);
    if (ret) {
        gf_log("cli", GF_LOG_ERROR, "Failed to get volume name");
        goto out;
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);
    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_QUOTA];

    if (proc->fn)
        ret = proc->fn(frame, THIS, options);

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if (sent == 0 && parse_err == 0)
            cli_out(
                "Quota command failed. Please check the cli "
                "logs for more details");
    }
    if (options)
        dict_unref(options);

    /* Events for Quota */
    if (ret == 0) {
        switch (type) {
            case GF_QUOTA_OPTION_TYPE_ENABLE:
                gf_event(EVENT_QUOTA_ENABLE, "volume=%s", volname);
                break;
            case GF_QUOTA_OPTION_TYPE_DISABLE:
                gf_event(EVENT_QUOTA_DISABLE, "volume=%s", volname);
                break;
            case GF_QUOTA_OPTION_TYPE_LIMIT_USAGE:
                gf_event(EVENT_QUOTA_SET_USAGE_LIMIT,
                         "volume=%s;"
                         "path=%s;limit=%s",
                         volname, words[4], words[5]);
                break;
            case GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS:
                gf_event(EVENT_QUOTA_SET_OBJECTS_LIMIT,
                         "volume=%s;"
                         "path=%s;limit=%s",
                         volname, words[4], words[5]);
                break;
            case GF_QUOTA_OPTION_TYPE_REMOVE:
                gf_event(EVENT_QUOTA_REMOVE_USAGE_LIMIT,
                         "volume=%s;"
                         "path=%s",
                         volname, words[4]);
                break;
            case GF_QUOTA_OPTION_TYPE_REMOVE_OBJECTS:
                gf_event(EVENT_QUOTA_REMOVE_OBJECTS_LIMIT,
                         "volume=%s;"
                         "path=%s",
                         volname, words[4]);
                break;
            case GF_QUOTA_OPTION_TYPE_ALERT_TIME:
                gf_event(EVENT_QUOTA_ALERT_TIME, "volume=%s;time=%s", volname,
                         words[4]);
                break;
            case GF_QUOTA_OPTION_TYPE_SOFT_TIMEOUT:
                gf_event(EVENT_QUOTA_SOFT_TIMEOUT,
                         "volume=%s;"
                         "soft-timeout=%s",
                         volname, words[4]);
                break;
            case GF_QUOTA_OPTION_TYPE_HARD_TIMEOUT:
                gf_event(EVENT_QUOTA_HARD_TIMEOUT,
                         "volume=%s;"
                         "hard-timeout=%s",
                         volname, words[4]);
                break;
            case GF_QUOTA_OPTION_TYPE_DEFAULT_SOFT_LIMIT:
                gf_event(EVENT_QUOTA_DEFAULT_SOFT_LIMIT,
                         "volume=%s;"
                         "default-soft-limit=%s",
                         volname, words[4]);
                break;
        }
    }

    CLI_STACK_DESTROY(frame);
    return ret;
}

int
cli_cmd_volume_remove_brick_cbk(struct cli_state *state,
                                struct cli_cmd_word *word, const char **words,
                                int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    gf_answer_t answer = GF_ANSWER_NO;
    int brick_count = 0;
    int sent = 0;
    int parse_error = 0;
    int need_question = 0;
    cli_local_t *local = NULL;
    char *volname = NULL;
#if (USE_EVENTS)
    eventtypes_t event = EVENT_LAST;
    char *event_str = NULL;
    int event_ret = -1;
#endif
    int32_t command = GF_OP_CMD_NONE;
    char *question = NULL;

    ret = cli_cmd_volume_remove_brick_parse(state, words, wordcount, &options,
                                            &need_question, &brick_count,
                                            &command);
    if (ret) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    if (command == GF_OP_CMD_COMMIT_FORCE) {
        question =
            "Remove-brick force will not migrate files from the "
            "removed bricks, so they will no longer be available"
            " on the volume.\nDo you want to continue?";
    } else if (command == GF_OP_CMD_START) {
        question =
            "It is recommended that remove-brick be run with"
            " cluster.force-migration option disabled to prevent"
            " possible data corruption. Doing so will ensure that"
            " files that receive writes during migration will not"
            " be migrated and will need to be manually copied"
            " after the remove-brick commit operation. Please"
            " check the value of the option and update accordingly."
            " \nDo you want to continue with your current"
            " cluster.force-migration settings?";
    }

    if (!brick_count) {
        cli_err("No bricks specified");
        cli_usage_out(word->pattern);
        parse_error = 1;
        ret = -1;
        goto out;
    }
    ret = dict_get_str(options, "volname", &volname);
    if (ret || !volname) {
        gf_log("cli", GF_LOG_ERROR, "Failed to fetch volname");
        ret = -1;
        goto out;
    }

#if (USE_EVENTS)
    event_ret = cli_event_remove_brick_str(options, &event_str, &event);
#endif

    if (!strcmp(volname, GLUSTER_SHARED_STORAGE)) {
        question =
            "Removing brick from the shared storage volume"
            "(gluster_shared_storage), will affect features "
            "like snapshot scheduler, geo-replication "
            "and NFS-Ganesha. Do you still want to "
            "continue?";
        need_question = _gf_true;
    }

    if (!(state->mode & GLUSTER_MODE_SCRIPT) && need_question) {
        answer = cli_cmd_get_confirmation(state, question);
        if (GF_ANSWER_NO == answer) {
            ret = 0;
            goto out;
        }
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_REMOVE_BRICK];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume remove-brick failed");
    }
#if (USE_EVENTS)
    if (!ret && !event_ret)
        gf_event(event, "%s", event_str);
    if (event_str)
        GF_FREE(event_str);

#endif

    CLI_STACK_DESTROY(frame);
    if (options)
        dict_unref(options);

    return ret;
}

int
cli_cmd_volume_reset_brick_cbk(struct cli_state *state,
                               struct cli_cmd_word *word, const char **words,
                               int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;

#ifdef GF_SOLARIS_HOST_OS
    cli_out("Command not supported on Solaris");
    goto out;
#endif
    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_RESET_BRICK];

    ret = cli_cmd_volume_reset_brick_parse(words, wordcount, &options);

    if (ret) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    if (state->mode & GLUSTER_MODE_WIGNORE_PARTITION) {
        ret = dict_set_int32(options, "ignore-partition", _gf_true);
        if (ret) {
            gf_log("cli", GF_LOG_ERROR,
                   "Failed to set ignore-"
                   "partition option");
            goto out;
        }
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume reset-brick failed");
    } else {
        if (wordcount > 5) {
            gf_event(EVENT_BRICK_RESET_COMMIT,
                     "Volume=%s;source-brick=%s;"
                     "destination-brick=%s",
                     (char *)words[2], (char *)words[3], (char *)words[4]);
        } else {
            gf_event(EVENT_BRICK_RESET_START, "Volume=%s;source-brick=%s",
                     (char *)words[2], (char *)words[3]);
        }
    }
    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_replace_brick_cbk(struct cli_state *state,
                                 struct cli_cmd_word *word, const char **words,
                                 int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;

#ifdef GF_SOLARIS_HOST_OS
    cli_out("Command not supported on Solaris");
    goto out;
#endif
    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_REPLACE_BRICK];

    ret = cli_cmd_volume_replace_brick_parse(words, wordcount, &options);

    if (ret) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume replace-brick failed");
    } else {
        gf_event(EVENT_BRICK_REPLACE,
                 "Volume=%s;source-brick=%s;destination-brick=%s",
                 (char *)words[2], (char *)words[3], (char *)words[4]);
    }
    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_set_transport_cbk(struct cli_state *state,
                                 struct cli_cmd_word *word, const char **words,
                                 int wordcount)
{
    cli_cmd_broadcast_response(0);
    return 0;
}

int
cli_cmd_volume_top_cbk(struct cli_state *state, struct cli_cmd_word *word,
                       const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;

    ret = cli_cmd_volume_top_parse(words, wordcount, &options);

    if (ret) {
        parse_error = 1;
        cli_usage_out(word->pattern);
        goto out;
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_TOP_VOLUME];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame");
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume top failed");
    }

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_log_rotate_cbk(struct cli_state *state, struct cli_cmd_word *word,
                       const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;

    if (!((wordcount == 4) || (wordcount == 5))) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    if (!(strcmp("rotate", words[3]) == 0)) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_LOG_ROTATE];

    ret = cli_cmd_log_rotate_parse(words, wordcount, &options);
    if (ret)
        goto out;

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame");
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume log rotate failed");
    }
    CLI_STACK_DESTROY(frame);

    return ret;
}

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

    ret = setenv("_GLUSTERD_CALLED_", "1", 1);
    if (-1 == ret) {
        gf_log("", GF_LOG_WARNING,
               "setenv syscall failed, hence could"
               "not assert if geo-replication is installed");
        goto out;
    }

    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) {
        gf_log("", GF_LOG_INFO, "geo-replication not installed");
        goto out;
    }

    ptr = fgets(buff, sizeof(buff), runner_chio(&runner, STDOUT_FILENO));
    if (ptr) {
        if (!strstr(buff, "gsyncd")) {
            ret = -1;
            goto out;
        }
    } else {
        ret = -1;
        goto out;
    }

    ret = runner_end(&runner);

    if (ret)
        gf_log("", GF_LOG_ERROR, "geo-replication not installed");

out:
    gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret);
    return ret ? -1 : 0;
}

void
cli_cmd_check_gsync_exists_cbk(struct cli_cmd *this)
{
    int ret = 0;

    ret = cli_check_gsync_present();
    if (ret)
        this->disable = _gf_true;
}
#endif

int
cli_cmd_volume_gsync_set_cbk(struct cli_state *state, struct cli_cmd_word *word,
                             const char **words, int wordcount)
{
    int ret = 0;
    int parse_err = 0;
    dict_t *options = NULL;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    cli_local_t *local = NULL;
    char *errstr = NULL;
#if (USE_EVENTS)
    int ret1 = -1;
    int cmd_type = -1;
    int tmpi = 0;
    char *tmp = NULL;
    char *events_str = NULL;
    int event_type = -1;
#endif

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GSYNC_SET];

    ret = cli_cmd_gsync_set_parse(state, words, wordcount, &options, &errstr);
    if (ret) {
        if (errstr) {
            cli_err("%s", errstr);
            GF_FREE(errstr);
        } else {
            cli_usage_out(word->pattern);
        }
        parse_err = 1;
        goto out;
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (frame == NULL) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn)
        ret = proc->fn(frame, THIS, options);

out:
    if (ret && parse_err == 0)
        cli_out(GEOREP " command failed");

#if (USE_EVENTS)
    if (ret == 0) {
        events_str = gf_strdup("");

        /* Type of Geo-rep Action - Create, Start etc */
        ret1 = dict_get_int32(options, "type", &cmd_type);
        if (ret1)
            cmd_type = -1;

        /* Only capture Events for modification commands */
        switch (cmd_type) {
            case GF_GSYNC_OPTION_TYPE_CREATE:
                event_type = EVENT_GEOREP_CREATE;
                break;
            case GF_GSYNC_OPTION_TYPE_START:
                event_type = EVENT_GEOREP_START;
                break;
            case GF_GSYNC_OPTION_TYPE_STOP:
                event_type = EVENT_GEOREP_STOP;
                break;
            case GF_GSYNC_OPTION_TYPE_PAUSE:
                event_type = EVENT_GEOREP_PAUSE;
                break;
            case GF_GSYNC_OPTION_TYPE_RESUME:
                event_type = EVENT_GEOREP_RESUME;
                break;
            case GF_GSYNC_OPTION_TYPE_DELETE:
                event_type = EVENT_GEOREP_DELETE;
                break;
            case GF_GSYNC_OPTION_TYPE_CONFIG:
                ret1 = dict_get_str(options, "subop", &tmp);
                if (ret1)
                    tmp = "";

                /* For Config Set additionally capture key and value */
                /* For Config Reset capture key */
                if (strcmp(tmp, "set") == 0) {
                    event_type = EVENT_GEOREP_CONFIG_SET;

                    ret1 = dict_get_str(options, "op_name", &tmp);
                    if (ret1)
                        tmp = "";

                    gf_asprintf_append(&events_str, "%soption=%s;", events_str,
                                       tmp);

                    ret1 = dict_get_str(options, "op_value", &tmp);
                    if (ret1)
                        tmp = "";

                    gf_asprintf_append(&events_str, "%svalue=%s;", events_str,
                                       tmp);
                } else if (strcmp(tmp, "del") == 0) {
                    event_type = EVENT_GEOREP_CONFIG_RESET;

                    ret1 = dict_get_str(options, "op_name", &tmp);
                    if (ret1)
                        tmp = "";

                    gf_asprintf_append(&events_str, "%soption=%s;", events_str,
                                       tmp);
                }
                break;
            default:
                break;
        }

        if (event_type > -1) {
            /* Capture all optional arguments used */
            ret1 = dict_get_int32(options, "force", &tmpi);
            if (ret1 == 0) {
                gf_asprintf_append(&events_str, "%sforce=%d;", events_str,
                                   tmpi);
            }
            ret1 = dict_get_int32(options, "push_pem", &tmpi);
            if (ret1 == 0) {
                gf_asprintf_append(&events_str, "%spush_pem=%d;", events_str,
                                   tmpi);
            }
            ret1 = dict_get_int32(options, "no_verify", &tmpi);
            if (ret1 == 0) {
                gf_asprintf_append(&events_str, "%sno_verify=%d;", events_str,
                                   tmpi);
            }

            ret1 = dict_get_int32(options, "ssh_port", &tmpi);
            if (ret1 == 0) {
                gf_asprintf_append(&events_str, "%sssh_port=%d;", events_str,
                                   tmpi);
            }

            ret1 = dict_get_int32(options, "reset-sync-time", &tmpi);
            if (ret1 == 0) {
                gf_asprintf_append(&events_str, "%sreset_sync_time=%d;",
                                   events_str, tmpi);
            }
            /* Capture Master and Slave Info */
            ret1 = dict_get_str(options, "master", &tmp);
            if (ret1)
                tmp = "";
            gf_asprintf_append(&events_str, "%smaster=%s;", events_str, tmp);

            ret1 = dict_get_str(options, "slave", &tmp);
            if (ret1)
                tmp = "";
            gf_asprintf_append(&events_str, "%sslave=%s", events_str, tmp);

            gf_event(event_type, "%s", events_str);
        }

        /* Allocated by gf_strdup and gf_asprintf */
        if (events_str)
            GF_FREE(events_str);
    }
#endif

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_status_cbk(struct cli_state *state, struct cli_cmd_word *word,
                          const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *dict = NULL;
    uint32_t cmd = 0;
    cli_local_t *local = NULL;

    ret = cli_cmd_volume_status_parse(words, wordcount, &dict);

    if (ret) {
        cli_usage_out(word->pattern);
        goto out;
    }

    ret = dict_get_uint32(dict, "cmd", &cmd);
    if (ret)
        goto out;

    if (!(cmd & GF_CLI_STATUS_ALL)) {
        /* for one volume or brick */
        proc = &cli_rpc_prog->proctable[GLUSTER_CLI_STATUS_VOLUME];
    } else {
        /* volume status all or all detail */
        proc = &cli_rpc_prog->proctable[GLUSTER_CLI_STATUS_ALL];
    }

    if (!proc->fn) {
        ret = -1;
        goto out;
    }

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        gf_log(THIS->name, GF_LOG_ERROR, "failed to create frame");
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, dict);

    ret = proc->fn(frame, THIS, dict);

out:
    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_get_detail_status(dict_t *dict, int i, cli_volume_status_t *status)
{
    uint64_t free = 0;
    uint64_t total = 0;
    char key[1024] = {0};
    int ret = 0;

    snprintf(key, sizeof(key), "brick%d.free", i);
    ret = dict_get_uint64(dict, key, &free);

    status->free = gf_uint64_2human_readable(free);
    if (!status->free)
        goto out;

    snprintf(key, sizeof(key), "brick%d.total", i);
    ret = dict_get_uint64(dict, key, &total);

    status->total = gf_uint64_2human_readable(total);
    if (!status->total)
        goto out;

    snprintf(key, sizeof(key), "brick%d.device", i);
    ret = dict_get_str(dict, key, &(status->device));
    if (ret)
        status->device = NULL;

    snprintf(key, sizeof(key), "brick%d.block_size", i);
    ret = dict_get_uint64(dict, key, &(status->block_size));
    if (ret) {
        ret = 0;
        status->block_size = 0;
    }

    snprintf(key, sizeof(key), "brick%d.mnt_options", i);
    ret = dict_get_str(dict, key, &(status->mount_options));
    if (ret)
        status->mount_options = NULL;

    snprintf(key, sizeof(key), "brick%d.fs_name", i);
    ret = dict_get_str(dict, key, &(status->fs_name));
    if (ret) {
        ret = 0;
        status->fs_name = NULL;
    }

    snprintf(key, sizeof(key), "brick%d.inode_size", i);
    ret = dict_get_str(dict, key, &(status->inode_size));
    if (ret)
        status->inode_size = NULL;

    snprintf(key, sizeof(key), "brick%d.total_inodes", i);
    ret = dict_get_uint64(dict, key, &(status->total_inodes));
    if (ret)
        status->total_inodes = 0;

    snprintf(key, sizeof(key), "brick%d.free_inodes", i);
    ret = dict_get_uint64(dict, key, &(status->free_inodes));
    if (ret) {
        ret = 0;
        status->free_inodes = 0;
    }

out:
    return ret;
}

void
cli_print_detailed_status(cli_volume_status_t *status)
{
    cli_out("%-20s : %-20s", "Brick", status->brick);

    if (status->online) {
        cli_out("%-20s : %-20d", "TCP Port", status->port);
        cli_out("%-20s : %-20d", "RDMA Port", status->rdma_port);
    } else {
        cli_out("%-20s : %-20s", "TCP Port", "N/A");
        cli_out("%-20s : %-20s", "RDMA Port", "N/A");
    }

    cli_out("%-20s : %-20c", "Online", (status->online) ? 'Y' : 'N');
    cli_out("%-20s : %-20s", "Pid", status->pid_str);

    if (status->fs_name)
        cli_out("%-20s : %-20s", "File System", status->fs_name);
    else
        cli_out("%-20s : %-20s", "File System", "N/A");

    if (status->device)
        cli_out("%-20s : %-20s", "Device", status->device);
    else
        cli_out("%-20s : %-20s", "Device", "N/A");

    if (status->mount_options) {
        cli_out("%-20s : %-20s", "Mount Options", status->mount_options);
    } else {
        cli_out("%-20s : %-20s", "Mount Options", "N/A");
    }

    if (status->inode_size) {
        cli_out("%-20s : %-20s", "Inode Size", status->inode_size);
    } else {
        cli_out("%-20s : %-20s", "Inode Size", "N/A");
    }
    if (status->free)
        cli_out("%-20s : %-20s", "Disk Space Free", status->free);
    else
        cli_out("%-20s : %-20s", "Disk Space Free", "N/A");

    if (status->total)
        cli_out("%-20s : %-20s", "Total Disk Space", status->total);
    else
        cli_out("%-20s : %-20s", "Total Disk Space", "N/A");

    if (status->total_inodes) {
        cli_out("%-20s : %-20" GF_PRI_INODE, "Inode Count",
                status->total_inodes);
    } else {
        cli_out("%-20s : %-20s", "Inode Count", "N/A");
    }

    if (status->free_inodes) {
        cli_out("%-20s : %-20" GF_PRI_INODE, "Free Inodes",
                status->free_inodes);
    } else {
        cli_out("%-20s : %-20s", "Free Inodes", "N/A");
    }
}

int
cli_print_brick_status(cli_volume_status_t *status)
{
    int fieldlen = CLI_VOL_STATUS_BRICK_LEN;
    int bricklen = 0;
    char *p = NULL;
    int num_spaces = 0;

    p = status->brick;
    bricklen = strlen(p);
    while (bricklen > 0) {
        if (bricklen > fieldlen) {
            cli_out("%.*s", fieldlen, p);
            p += fieldlen;
            bricklen -= fieldlen;
        } else {
            num_spaces = (fieldlen - bricklen) + 1;
            printf("%s", p);
            while (num_spaces-- != 0)
                printf(" ");
            if (status->port || status->rdma_port) {
                if (status->online)
                    cli_out("%-10d%-11d%-8c%-5s", status->port,
                            status->rdma_port, status->online ? 'Y' : 'N',
                            status->pid_str);
                else
                    cli_out("%-10s%-11s%-8c%-5s", "N/A", "N/A",
                            status->online ? 'Y' : 'N', status->pid_str);
            } else
                cli_out("%-10s%-11s%-8c%-5s", "N/A", "N/A",
                        status->online ? 'Y' : 'N', status->pid_str);
            bricklen = 0;
        }
    }

    return 0;
}

#define NEEDS_GLFS_HEAL(op)                                                    \
    ((op == GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE) ||                         \
     (op == GF_SHD_OP_SBRAIN_HEAL_FROM_LATEST_MTIME) ||                        \
     (op == GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK) ||                               \
     (op == GF_SHD_OP_INDEX_SUMMARY) || (op == GF_SHD_OP_SPLIT_BRAIN_FILES) || \
     (op == GF_SHD_OP_GRANULAR_ENTRY_HEAL_ENABLE) ||                           \
     (op == GF_SHD_OP_HEAL_SUMMARY))

int
cli_launch_glfs_heal(int heal_op, dict_t *options)
{
    char buff[PATH_MAX] = {0};
    runner_t runner = {0};
    char *filename = NULL;
    char *hostname = NULL;
    char *path = NULL;
    char *volname = NULL;
    char *out = NULL;
    int ret = 0;

    runinit(&runner);
    ret = dict_get_str(options, "volname", &volname);
    runner_add_args(&runner, GLFSHEAL_PREFIX "/glfsheal", volname, NULL);
    runner_redir(&runner, STDOUT_FILENO, RUN_PIPE);

    switch (heal_op) {
        case GF_SHD_OP_INDEX_SUMMARY:
            if (global_state->mode & GLUSTER_MODE_XML) {
                runner_add_args(&runner, "--xml", NULL);
            }
            break;
        case GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE:
            ret = dict_get_str(options, "file", &filename);
            runner_add_args(&runner, "bigger-file", filename, NULL);
            break;
        case GF_SHD_OP_SBRAIN_HEAL_FROM_LATEST_MTIME:
            ret = dict_get_str(options, "file", &filename);
            runner_add_args(&runner, "latest-mtime", filename, NULL);
            break;
        case GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK:
            ret = dict_get_str(options, "heal-source-hostname", &hostname);
            ret = dict_get_str(options, "heal-source-brickpath", &path);
            runner_add_args(&runner, "source-brick", NULL);
            runner_argprintf(&runner, "%s:%s", hostname, path);
            if (dict_get_str(options, "file", &filename) == 0)
                runner_argprintf(&runner, "%s", filename);
            break;
        case GF_SHD_OP_SPLIT_BRAIN_FILES:
            runner_add_args(&runner, "split-brain-info", NULL);
            if (global_state->mode & GLUSTER_MODE_XML) {
                runner_add_args(&runner, "--xml", NULL);
            }
            break;
        case GF_SHD_OP_GRANULAR_ENTRY_HEAL_ENABLE:
        case GF_SHD_OP_GRANULAR_ENTRY_HEAL_DISABLE:
            runner_add_args(&runner, "granular-entry-heal-op", NULL);
            break;
        case GF_SHD_OP_HEAL_SUMMARY:
            runner_add_args(&runner, "info-summary", NULL);
            if (global_state->mode & GLUSTER_MODE_XML) {
                runner_add_args(&runner, "--xml", NULL);
            }
            break;
        default:
            ret = -1;
            goto out;
    }
    if (global_state->mode & GLUSTER_MODE_GLFSHEAL_NOLOG)
        runner_add_args(&runner, "--nolog", NULL);
    ret = runner_start(&runner);
    if (ret == -1)
        goto out;
    while ((
        out = fgets(buff, sizeof(buff), runner_chio(&runner, STDOUT_FILENO)))) {
        printf("%s", out);
    }
    ret = runner_end(&runner);

out:
    return ret;
}

int
cli_cmd_volume_heal_cbk(struct cli_state *state, struct cli_cmd_word *word,
                        const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    int sent = 0;
    int parse_error = 0;
    dict_t *options = NULL;
    xlator_t *this = NULL;
    cli_local_t *local = NULL;
    int heal_op = 0;

    this = THIS;

    if (wordcount < 3) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    ret = cli_cmd_volume_heal_options_parse(words, wordcount, &options);
    if (ret) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }
    ret = dict_get_int32(options, "heal-op", &heal_op);
    if (ret < 0)
        goto out;
    if (NEEDS_GLFS_HEAL(heal_op)) {
        ret = cli_launch_glfs_heal(heal_op, options);
        if (ret < 0)
            goto out;
        if (heal_op != GF_SHD_OP_GRANULAR_ENTRY_HEAL_ENABLE)
            goto out;
    }

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_HEAL_VOLUME];

    frame = create_frame(this, this->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }
out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0) &&
            !(global_state->mode & GLUSTER_MODE_XML)) {
            cli_out("Volume heal failed.");
        }
    }

    if (options)
        dict_unref(options);

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_statedump_cbk(struct cli_state *state, struct cli_cmd_word *word,
                             const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;

    if (wordcount < 3) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    if (wordcount >= 3) {
        ret = cli_cmd_volume_statedump_options_parse(words, wordcount,
                                                     &options);
        if (ret) {
            parse_error = 1;
            gf_log("cli", GF_LOG_ERROR,
                   "Error parsing "
                   "statedump options");
            cli_out("Error parsing options");
            cli_usage_out(word->pattern);
        }
    }

    ret = dict_set_str(options, "volname", (char *)words[2]);
    if (ret)
        goto out;

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_STATEDUMP_VOLUME];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume statedump failed");
    }

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_list_cbk(struct cli_state *state, struct cli_cmd_word *word,
                        const char **words, int wordcount)
{
    int ret = -1;
    call_frame_t *frame = NULL;
    rpc_clnt_procedure_t *proc = NULL;
    int sent = 0;

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_LIST_VOLUME];
    if (proc->fn) {
        frame = create_frame(THIS, THIS->ctx->pool);
        if (!frame)
            goto out;
        ret = proc->fn(frame, THIS, NULL);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if (sent == 0)
            cli_out("Volume list failed");
    }

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_clearlocks_cbk(struct cli_state *state,
                              struct cli_cmd_word *word, const char **words,
                              int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;

    if (wordcount < 7 || wordcount > 8) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    ret = cli_cmd_volume_clrlks_opts_parse(words, wordcount, &options);
    if (ret) {
        parse_error = 1;
        gf_log("cli", GF_LOG_ERROR,
               "Error parsing "
               "clear-locks options");
        cli_out("Error parsing options");
        cli_usage_out(word->pattern);
    }

    ret = dict_set_str(options, "volname", (char *)words[2]);
    if (ret)
        goto out;

    ret = dict_set_str(options, "path", (char *)words[3]);
    if (ret)
        goto out;

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_CLRLOCKS_VOLUME];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn) {
        ret = proc->fn(frame, THIS, options);
    }

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_out("Volume clear-locks failed");
    }

    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_barrier_cbk(struct cli_state *state, struct cli_cmd_word *word,
                           const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_error = 0;
    cli_local_t *local = NULL;

    if (wordcount != 4) {
        cli_usage_out(word->pattern);
        parse_error = 1;
        goto out;
    }

    options = dict_new();
    if (!options) {
        ret = -1;
        goto out;
    }
    ret = dict_set_str(options, "volname", (char *)words[2]);
    if (ret)
        goto out;

    ret = dict_set_str(options, "barrier", (char *)words[3]);
    if (ret)
        goto out;

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_BARRIER_VOLUME];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn)
        ret = proc->fn(frame, THIS, options);

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_error == 0))
            cli_err("Volume barrier failed");
    }
    CLI_STACK_DESTROY(frame);

    return ret;
}

int
cli_cmd_volume_getopt_cbk(struct cli_state *state, struct cli_cmd_word *word,
                          const char **words, int wordcount)
{
    int ret = -1;
    rpc_clnt_procedure_t *proc = NULL;
    call_frame_t *frame = NULL;
    dict_t *options = NULL;
    int sent = 0;
    int parse_err = 0;
    cli_local_t *local = NULL;

    if (wordcount != 4) {
        cli_usage_out(word->pattern);
        parse_err = 1;
        goto out;
    }

    options = dict_new();
    if (!options)
        goto out;

    ret = dict_set_str(options, "volname", (char *)words[2]);
    if (ret)
        goto out;

    ret = dict_set_str(options, "key", (char *)words[3]);
    if (ret)
        goto out;

    proc = &cli_rpc_prog->proctable[GLUSTER_CLI_GET_VOL_OPT];

    frame = create_frame(THIS, THIS->ctx->pool);
    if (!frame) {
        ret = -1;
        goto out;
    }

    CLI_LOCAL_INIT(local, words, frame, options);

    if (proc->fn)
        ret = proc->fn(frame, THIS, options);

out:
    if (ret) {
        cli_cmd_sent_status_get(&sent);
        if ((sent == 0) && (parse_err == 0))
            cli_err("Volume get option failed");
    }
    CLI_STACK_DESTROY(frame);
    return ret;
}

/* This is a bit of a hack to display the help. The current bitrot cmd
 * format does not work well when registering the cmds.
 * Ideally the should have been of the form
 * gluster volume bitrot <subcommand> <volumename> ...
 */

struct cli_cmd bitrot_cmds[] = {

    {"volume bitrot help", cli_cmd_bitrot_help_cbk,
     "display help for volume bitrot commands"},

    {"volume bitrot <VOLNAME> {enable|disable}", NULL, /*cli_cmd_bitrot_cbk,*/
     "Enable/disable bitrot for volume <VOLNAME>"},

    {"volume bitrot <VOLNAME> signing-time <time-in-secs>",
     NULL, /*cli_cmd_bitrot_cbk,*/
     "Waiting time for an object after last fd is closed to start signing "
     "process"},

    {"volume bitrot <VOLNAME> signer-threads <count>",
     NULL, /*cli_cmd_bitrot_cbk,*/
     "Number of signing process threads. Usually set to number of available "
     "cores"},

    {"volume bitrot <VOLNAME> scrub-throttle {lazy|normal|aggressive}",
     NULL, /*cli_cmd_bitrot_cbk,*/
     "Set the speed of the scrubber for volume <VOLNAME>"},

    {"volume bitrot <VOLNAME> scrub-frequency {hourly|daily|weekly|biweekly"
     "|monthly}",
     NULL, /*cli_cmd_bitrot_cbk,*/
     "Set the frequency of the scrubber for volume <VOLNAME>"},

    {"volume bitrot <VOLNAME> scrub {pause|resume|status|ondemand}",
     NULL, /*cli_cmd_bitrot_cbk,*/
     "Pause/resume the scrubber for <VOLNAME>. Status displays the status of "
     "the scrubber. ondemand starts the scrubber immediately."},

    {"volume bitrot <VOLNAME> {enable|disable}\n"
     "volume bitrot <VOLNAME> signing-time <time-in-secs>\n"
     "volume bitrot <VOLNAME> signer-threads <count>\n"
     "volume bitrot <volname> scrub-throttle {lazy|normal|aggressive}\n"
     "volume bitrot <volname> scrub-frequency {hourly|daily|weekly|biweekly"
     "|monthly}\n"
     "volume bitrot <volname> scrub {pause|resume|status|ondemand}",
     cli_cmd_bitrot_cbk, NULL},

    {NULL, NULL, NULL}};

struct cli_cmd quota_cmds[] = {

    /* Quota commands */
    {"volume quota help", cli_cmd_quota_help_cbk,
     "display help for volume quota commands"},

    {"volume quota <VOLNAME> {enable|disable|list [<path> ...]| "
     "list-objects [<path> ...] | remove <path>| remove-objects <path> | "
     "default-soft-limit <percent>}",
     cli_cmd_quota_cbk, "Enable/disable and configure quota for <VOLNAME>"},

    {"volume quota <VOLNAME> {limit-usage <path> <size> [<percent>]}",
     cli_cmd_quota_cbk, "Set maximum size for <path> for <VOLNAME>"},

    {"volume quota <VOLNAME> {limit-objects <path> <number> [<percent>]}",
     cli_cmd_quota_cbk,
     "Set the maximum number of entries allowed in <path> for <VOLNAME>"},

    {"volume quota <VOLNAME> {alert-time|soft-timeout|hard-timeout} {<time>}",
     cli_cmd_quota_cbk, "Set quota timeout for <VOLNAME>"},

    {"volume inode-quota <VOLNAME> enable", cli_cmd_quota_cbk,
     "Enable/disable inode-quota for <VOLNAME>"},

    {"volume quota <VOLNAME> {enable|disable|list [<path> ...]| "
     "list-objects [<path> ...] | remove <path>| remove-objects <path> | "
     "default-soft-limit <percent>}\n"
     "volume quota <VOLNAME> {limit-usage <path> <size> [<percent>]}\n"
     "volume quota <VOLNAME> {limit-objects <path> <number> [<percent>]}\n"
     "volume quota <VOLNAME> {alert-time|soft-timeout|hard-timeout} {<time>}",
     cli_cmd_quota_cbk, NULL},

    {NULL, NULL, NULL}};

struct cli_cmd volume_cmds[] = {
    {"volume help", cli_cmd_volume_help_cbk,
     "display help for volume commands"},

    {"volume info [all|<VOLNAME>]", cli_cmd_volume_info_cbk,
     "list information of all volumes"},

    {"volume create <NEW-VOLNAME> [stripe <COUNT>] "
     "[[replica <COUNT> [arbiter <COUNT>]]|[replica 2 thin-arbiter 1]] "
     "[disperse [<COUNT>]] [disperse-data <COUNT>] [redundancy <COUNT>] "
     "[transport <tcp|rdma|tcp,rdma>] <NEW-BRICK> <TA-BRICK>"
     "... [force]",

     cli_cmd_volume_create_cbk,
     "create a new volume of specified type with mentioned bricks"},

    {"volume delete <VOLNAME>", cli_cmd_volume_delete_cbk,
     "delete volume specified by <VOLNAME>"},

    {"volume start <VOLNAME> [force]", cli_cmd_volume_start_cbk,
     "start volume specified by <VOLNAME>"},

    {"volume stop <VOLNAME> [force]", cli_cmd_volume_stop_cbk,
     "stop volume specified by <VOLNAME>"},

    /*{ "volume rename <VOLNAME> <NEW-VOLNAME>",
      cli_cmd_volume_rename_cbk,
      "rename volume <VOLNAME> to <NEW-VOLNAME>"},*/

    {"volume add-brick <VOLNAME> [<stripe|replica> <COUNT> "
     "[arbiter <COUNT>]] <NEW-BRICK> ... [force]",
     cli_cmd_volume_add_brick_cbk, "add brick to volume <VOLNAME>"},

    {"volume remove-brick <VOLNAME> [replica <COUNT>] <BRICK> ..."
     " <start|stop|status|commit|force>",
     cli_cmd_volume_remove_brick_cbk, "remove brick from volume <VOLNAME>"},

    {"volume rebalance <VOLNAME> {{fix-layout start} | {start "
     "[force]|stop|status}}",
     cli_cmd_volume_defrag_cbk, "rebalance operations"},

    {"volume replace-brick <VOLNAME> <SOURCE-BRICK> <NEW-BRICK> "
     "{commit force}",
     cli_cmd_volume_replace_brick_cbk, "replace-brick operations"},

    /*{ "volume set-transport <VOLNAME> <TRANSPORT-TYPE> [<TRANSPORT-TYPE>]
      ...", cli_cmd_volume_set_transport_cbk, "set transport type for volume
      <VOLNAME>"},*/

    {"volume set <VOLNAME> <KEY> <VALUE>", cli_cmd_volume_set_cbk,
     "set options for volume <VOLNAME>"},

    {"volume set <VOLNAME> group <GROUP>", cli_cmd_volume_set_cbk,
     "This option can be used for setting multiple pre-defined volume options "
     "where group_name is a file under /var/lib/glusterd/groups containing one "
     "key value pair per line"},

    {"volume log <VOLNAME> rotate [BRICK]", cli_cmd_log_rotate_cbk,
     "rotate the log file for corresponding volume/brick"},

    {"volume sync <HOSTNAME> [all|<VOLNAME>]", cli_cmd_sync_volume_cbk,
     "sync the volume information from a peer"},

    {"volume reset <VOLNAME> [option] [force]", cli_cmd_volume_reset_cbk,
     "reset all the reconfigured options"},

#if (SYNCDAEMON_COMPILE)
    {"volume " GEOREP " [<MASTER-VOLNAME>] [<SLAVE-IP>]::[<SLAVE-VOLNAME>] {"
     "\\\n create [[ssh-port n] [[no-verify] \\\n | [push-pem]]] [force] \\\n"
     " | start [force] \\\n | stop [force] \\\n | pause [force] \\\n | resume "
     "[force] \\\n"
     " | config [[[\\!]<option>] [<value>]] \\\n | status "
     "[detail] \\\n | delete [reset-sync-time]} ",
     cli_cmd_volume_gsync_set_cbk, "Geo-sync operations",
     cli_cmd_check_gsync_exists_cbk},
#endif

    {"volume profile <VOLNAME> {start|info [peek|incremental "
     "[peek]|cumulative|clear]|stop} [nfs]",
     cli_cmd_volume_profile_cbk, "volume profile operations"},

    {"volume top <VOLNAME> {open|read|write|opendir|readdir|clear} [nfs|brick "
     "<brick>] [list-cnt <value>] | "
     "{read-perf|write-perf} [bs <size> count <count>] "
     "[brick <brick>] [list-cnt <value>]",
     cli_cmd_volume_top_cbk, "volume top operations"},

    {"volume status [all | <VOLNAME> [nfs|shd|<BRICK>|quotad]]"
     " [detail|clients|mem|inode|fd|callpool|tasks|client-list]",
     cli_cmd_volume_status_cbk,
     "display status of all or specified volume(s)/brick"},

    {"volume heal <VOLNAME> [enable | disable | full |"
     "statistics [heal-count [replica <HOSTNAME:BRICKNAME>]] |"
     "info [summary | split-brain] |"
     "split-brain {bigger-file <FILE> | latest-mtime <FILE> |"
     "source-brick <HOSTNAME:BRICKNAME> [<FILE>]} |"
     "granular-entry-heal {enable | disable}]",
     cli_cmd_volume_heal_cbk,
     "self-heal commands on volume specified by <VOLNAME>"},

    {"volume statedump <VOLNAME> [[nfs|quotad] [all|mem|iobuf|callpool|"
     "priv|fd|inode|history]... | [client <hostname:process-id>]]",
     cli_cmd_volume_statedump_cbk, "perform statedump on bricks"},

    {"volume list", cli_cmd_volume_list_cbk, "list all volumes in cluster"},

    {"volume clear-locks <VOLNAME> <path> kind {blocked|granted|all}"
     "{inode [range]|entry [basename]|posix [range]}",
     cli_cmd_volume_clearlocks_cbk, "Clear locks held on path"},
    {"volume barrier <VOLNAME> {enable|disable}", cli_cmd_volume_barrier_cbk,
     "Barrier/unbarrier file operations on a volume"},
    {"volume get <VOLNAME|all> <key|all>", cli_cmd_volume_getopt_cbk,
     "Get the value of the all options or given option for volume <VOLNAME>"
     " or all option. gluster volume get all all is to get all global "
     "options"},

    {"volume reset-brick <VOLNAME> <SOURCE-BRICK> {{start} |"
     " {<NEW-BRICK> commit}}",
     cli_cmd_volume_reset_brick_cbk, "reset-brick operations"},

    {NULL, NULL, NULL}};

int
cli_cmd_quota_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word,
                       const char **words, int wordcount)
{
    struct cli_cmd *cmd = NULL;
    struct cli_cmd *quota_cmd = NULL;
    int count = 0;

    cmd = GF_MALLOC(sizeof(quota_cmds), cli_mt_cli_cmd);
    memcpy(cmd, quota_cmds, sizeof(quota_cmds));
    count = (sizeof(quota_cmds) / sizeof(struct cli_cmd));
    cli_cmd_sort(cmd, count);

    cli_out("\ngluster quota commands");
    cli_out("=======================\n");

    for (quota_cmd = cmd; quota_cmd->pattern; quota_cmd++)
        if ((_gf_false == quota_cmd->disable) && (quota_cmd->desc))
            cli_out("%s - %s", quota_cmd->pattern, quota_cmd->desc);

    cli_out("\n");
    GF_FREE(cmd);

    return 0;
}

int
cli_cmd_bitrot_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word,
                        const char **words, int wordcount)
{
    struct cli_cmd *cmd = NULL;
    struct cli_cmd *bitrot_cmd = NULL;
    int count = 0;

    cmd = GF_MALLOC(sizeof(bitrot_cmds), cli_mt_cli_cmd);
    memcpy(cmd, bitrot_cmds, sizeof(bitrot_cmds));
    count = (sizeof(bitrot_cmds) / sizeof(struct cli_cmd));
    cli_cmd_sort(cmd, count);

    cli_out("\ngluster bitrot commands");
    cli_out("========================\n");

    for (bitrot_cmd = cmd; bitrot_cmd->pattern; bitrot_cmd++)
        if ((_gf_false == bitrot_cmd->disable) && (bitrot_cmd->desc))
            cli_out("%s - %s", bitrot_cmd->pattern, bitrot_cmd->desc);

    cli_out("\n");
    GF_FREE(cmd);

    return 0;
}

int
cli_cmd_volume_help_cbk(struct cli_state *state, struct cli_cmd_word *in_word,
                        const char **words, int wordcount)
{
    struct cli_cmd *cmd = NULL;
    struct cli_cmd *vol_cmd = NULL;
    int count = 0;

    cmd = GF_MALLOC(sizeof(volume_cmds), cli_mt_cli_cmd);
    memcpy(cmd, volume_cmds, sizeof(volume_cmds));
    count = (sizeof(volume_cmds) / sizeof(struct cli_cmd));
    cli_cmd_sort(cmd, count);

    cli_out("\ngluster volume commands");
    cli_out("========================\n");

    for (vol_cmd = cmd; vol_cmd->pattern; vol_cmd++)
        if (_gf_false == vol_cmd->disable)
            cli_out("%s - %s", vol_cmd->pattern, vol_cmd->desc);

    cli_out("\n");
    GF_FREE(cmd);
    return 0;
}

int
cli_cmd_volume_register(struct cli_state *state)
{
    int ret = 0;
    struct cli_cmd *cmd = NULL;

    for (cmd = volume_cmds; cmd->pattern; cmd++) {
        ret = cli_cmd_register(&state->tree, cmd);
        if (ret)
            goto out;
    }

    for (cmd = bitrot_cmds; cmd->pattern; cmd++) {
        ret = cli_cmd_register(&state->tree, cmd);
        if (ret)
            goto out;
    }

    for (cmd = quota_cmds; cmd->pattern; cmd++) {
        ret = cli_cmd_register(&state->tree, cmd);
        if (ret)
            goto out;
    }

out:
    return ret;
}

static int
gf_asprintf_append(char **string_ptr, const char *format, ...)
{
    va_list arg;
    int rv = 0;
    char *tmp = *string_ptr;

    va_start(arg, format);
    rv = gf_vasprintf(string_ptr, format, arg);
    va_end(arg);

    if (tmp)
        GF_FREE(tmp);

    return rv;
}
