"Fossies" - the Fresh Open Source Software Archive

Member "glusterfs-8.2/cli/src/cli-cmd-parser.c" (16 Sep 2020, 167311 Bytes) of package /linux/misc/glusterfs-8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cli-cmd-parser.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2    Copyright (c) 2010-2013 Red Hat, Inc. <http://www.redhat.com>
    3    This file is part of GlusterFS.
    4 
    5    This file is licensed to you under your choice of the GNU Lesser
    6    General Public License, version 3 or any later version (LGPLv3 or
    7    later), or the GNU General Public License, version 2 (GPLv2), in all
    8    cases as published by the Free Software Foundation.
    9 */
   10 #include <stdio.h>
   11 #include <string.h>
   12 #include <stdlib.h>
   13 #include <stdint.h>
   14 #include <pthread.h>
   15 #include <fnmatch.h>
   16 #include <time.h>
   17 
   18 #include "cli.h"
   19 #include "cli-cmd.h"
   20 #include "cli-mem-types.h"
   21 #include <glusterfs/dict.h>
   22 #include <glusterfs/list.h>
   23 
   24 #include "protocol-common.h"
   25 #include "cli1-xdr.h"
   26 
   27 #define MAX_SNAP_DESCRIPTION_LEN 1024
   28 
   29 static struct snap_config_opt_vals_ snap_confopt_vals[] = {
   30     {.op_name = "snap-max-hard-limit",
   31      .question = "Changing snapshot-max-hard-limit "
   32                  "will limit the creation of new snapshots "
   33                  "if they exceed the new limit.\n"
   34                  "Do you want to continue?"},
   35     {.op_name = "snap-max-soft-limit",
   36      .question = "If Auto-delete is enabled, snap-max-soft-limit will"
   37                  " trigger deletion of oldest snapshot, on the "
   38                  "creation of new snapshot, when the "
   39                  "snap-max-soft-limit is reached.\n"
   40                  "Do you want to change the snap-max-soft-limit?"},
   41     {.op_name = "both",
   42      .question = "Changing snapshot-max-hard-limit "
   43                  "will limit the creation of new snapshots "
   44                  "if they exceed the new snapshot-max-hard-limit.\n"
   45                  "If Auto-delete is enabled, snap-max-soft-limit will"
   46                  " trigger deletion of oldest snapshot, on the "
   47                  "creation of new snapshot, when the "
   48                  "snap-max-soft-limit is reached.\n"
   49                  "Do you want to continue?"},
   50     {
   51         .op_name = NULL,
   52     }};
   53 
   54 enum cli_snap_config_set_types {
   55     GF_SNAP_CONFIG_SET_HARD = 0,
   56     GF_SNAP_CONFIG_SET_SOFT = 1,
   57     GF_SNAP_CONFIG_SET_BOTH = 2,
   58 };
   59 typedef enum cli_snap_config_set_types cli_snap_config_set_types;
   60 
   61 typedef struct _cli_brick {
   62     struct list_head list;
   63     const char *name;
   64     int32_t len;
   65 } cli_brick_t;
   66 
   67 int
   68 cli_cmd_validate_volume(char *volname);
   69 
   70 static const char *
   71 id_sel(void *wcon)
   72 {
   73     return (const char *)wcon;
   74 }
   75 
   76 static char *
   77 str_getunamb(const char *tok, char **opwords)
   78 {
   79     return (char *)cli_getunamb(tok, (void **)opwords, id_sel);
   80 }
   81 
   82 int32_t
   83 cli_cmd_ta_brick_parse(const char **words, int wordcount, char **ta_brick)
   84 {
   85     char *host_name = NULL;
   86     char *tmp_host = NULL;
   87     char *delimiter = NULL;
   88     cli_brick_t *brick = NULL;
   89     int ret = 0;
   90 
   91     GF_ASSERT(words);
   92     GF_ASSERT(wordcount);
   93 
   94     if (validate_brick_name((char *)words[wordcount - 1])) {
   95         cli_err(
   96             "Wrong brick type: %s, use <HOSTNAME>:"
   97             "<export-dir-abs-path>",
   98             words[wordcount - 1]);
   99         ret = -1;
  100         goto out;
  101     } else {
  102         delimiter = strrchr(words[wordcount - 1], ':');
  103         ret = gf_canonicalize_path(delimiter + 1);
  104         if (ret)
  105             goto out;
  106     }
  107 
  108     tmp_host = gf_strdup((char *)words[wordcount - 1]);
  109     if (!tmp_host) {
  110         gf_log("cli", GF_LOG_ERROR, "Out of memory");
  111         ret = -1;
  112         goto out;
  113     }
  114     get_host_name(tmp_host, &host_name);
  115     if (!host_name) {
  116         ret = -1;
  117         gf_log("cli", GF_LOG_ERROR,
  118                "Unable to retrieve "
  119                "hostname");
  120         goto out;
  121     }
  122 
  123     if (!(strcmp(host_name, "localhost") && strcmp(host_name, "127.0.0.1") &&
  124           strncmp(host_name, "0.", 2))) {
  125         cli_err(
  126             "Please provide a valid hostname/ip other "
  127             "than localhost, 127.0.0.1 or loopback "
  128             "address (0.0.0.0 to 0.255.255.255).");
  129         ret = -1;
  130         goto out;
  131     }
  132     if (!valid_internet_address(host_name, _gf_false, _gf_false)) {
  133         cli_err(
  134             "internet address '%s' does not conform to "
  135             "standards",
  136             host_name);
  137     }
  138 
  139     brick = GF_MALLOC(sizeof(cli_brick_t), gf_common_list_node);
  140     if (brick == NULL) {
  141         ret = -1;
  142         gf_log("cli", GF_LOG_ERROR, "Out of memory");
  143         goto out;
  144     }
  145 
  146     brick->name = words[wordcount - 1];
  147     brick->len = strlen(words[wordcount - 1]);
  148     *ta_brick = GF_MALLOC(brick->len + 3, gf_common_mt_char);
  149     if (*ta_brick == NULL) {
  150         ret = -1;
  151         gf_log("cli", GF_LOG_ERROR, "Out of memory");
  152         goto out;
  153     }
  154 
  155     strcat(*ta_brick, " ");
  156     strcat(*ta_brick, brick->name);
  157     strcat(*ta_brick, " ");
  158 out:
  159     if (tmp_host) {
  160         GF_FREE(tmp_host);
  161         tmp_host = NULL;
  162     }
  163     if (brick) {
  164         GF_FREE(brick);
  165         brick = NULL;
  166     }
  167 
  168     return ret;
  169 }
  170 
  171 int32_t
  172 cli_cmd_bricks_parse(const char **words, int wordcount, int brick_index,
  173                      char **bricks, int *brick_count)
  174 {
  175     int ret = 0;
  176     char *delimiter = NULL;
  177     char *host_name = NULL;
  178     char *tmp_host = NULL;
  179     char *bricks_str = NULL;
  180     int len = 0;
  181     int brick_list_len = 1; /* For initial space */
  182     struct list_head brick_list = {
  183         0,
  184     };
  185     cli_brick_t *brick = NULL;
  186 
  187     GF_ASSERT(words);
  188     GF_ASSERT(wordcount);
  189     GF_ASSERT(bricks);
  190     GF_ASSERT(brick_index > 0);
  191     GF_ASSERT(brick_index < wordcount);
  192 
  193     INIT_LIST_HEAD(&brick_list);
  194 
  195     while (brick_index < wordcount) {
  196         if (validate_brick_name((char *)words[brick_index])) {
  197             cli_err(
  198                 "Wrong brick type: %s, use <HOSTNAME>:"
  199                 "<export-dir-abs-path>",
  200                 words[brick_index]);
  201             ret = -1;
  202             goto out;
  203         } else {
  204             delimiter = strrchr(words[brick_index], ':');
  205             ret = gf_canonicalize_path(delimiter + 1);
  206             if (ret)
  207                 goto out;
  208         }
  209 
  210         tmp_host = gf_strdup((char *)words[brick_index]);
  211         if (!tmp_host) {
  212             gf_log("cli", GF_LOG_ERROR, "Out of memory");
  213             ret = -1;
  214             goto out;
  215         }
  216         get_host_name(tmp_host, &host_name);
  217         if (!host_name) {
  218             ret = -1;
  219             gf_log("cli", GF_LOG_ERROR,
  220                    "Unable to allocate "
  221                    "memory");
  222             GF_FREE(tmp_host);
  223             goto out;
  224         }
  225 
  226         if (!(strcmp(host_name, "localhost") &&
  227               strcmp(host_name, "127.0.0.1") && strncmp(host_name, "0.", 2))) {
  228             cli_err(
  229                 "Please provide a valid hostname/ip other "
  230                 "than localhost, 127.0.0.1 or loopback "
  231                 "address (0.0.0.0 to 0.255.255.255).");
  232             ret = -1;
  233             GF_FREE(tmp_host);
  234             goto out;
  235         }
  236         if (!valid_internet_address(host_name, _gf_false, _gf_false)) {
  237             cli_err(
  238                 "internet address '%s' does not conform to "
  239                 "standards",
  240                 host_name);
  241         }
  242         GF_FREE(tmp_host);
  243         list_for_each_entry(brick, &brick_list, list)
  244         {
  245             if (strcmp(brick->name, words[brick_index]) == 0) {
  246                 ret = -1;
  247                 cli_err("Found duplicate exports %s", words[brick_index]);
  248                 goto out;
  249             }
  250         }
  251 
  252         brick = GF_MALLOC(sizeof(cli_brick_t), gf_common_list_node);
  253         if (brick == NULL) {
  254             ret = -1;
  255             gf_log("cli", GF_LOG_ERROR, "Out of memory");
  256             goto out;
  257         }
  258         len = strlen(words[brick_index]);
  259         brick->name = words[brick_index];
  260         brick->len = len;
  261         list_add_tail(&brick->list, &brick_list);
  262 
  263         brick_list_len += len + 1; /* Brick name + space */
  264         ++(*brick_count);
  265         ++brick_index;
  266     }
  267 
  268     /* If brick count is not valid exit here */
  269     if (!*brick_count) {
  270         cli_err("No bricks specified");
  271         ret = -1;
  272         goto out;
  273     }
  274 
  275     brick_list_len++; /* For terminating null char */
  276 
  277     bricks_str = GF_MALLOC(brick_list_len, gf_common_mt_char);
  278     if (bricks_str == NULL) {
  279         ret = -1;
  280         gf_log("cli", GF_LOG_ERROR, "Out of memory");
  281         goto out;
  282     }
  283     *bricks = bricks_str;
  284     *bricks_str = ' ';
  285     bricks_str++;
  286     while (!list_empty(&brick_list)) {
  287         brick = list_first_entry(&brick_list, cli_brick_t, list);
  288         list_del_init(&brick->list);
  289         memcpy(bricks_str, brick->name, brick->len);
  290         bricks_str[brick->len] = ' ';
  291         bricks_str += brick->len + 1;
  292         GF_FREE(brick);
  293     }
  294     *bricks_str = 0;
  295 
  296 out:
  297     while (!list_empty(&brick_list)) {
  298         brick = list_first_entry(&brick_list, cli_brick_t, list);
  299         list_del_init(&brick->list);
  300         GF_FREE(brick);
  301     }
  302 
  303     return ret;
  304 }
  305 
  306 int32_t
  307 cli_cmd_create_disperse_check(struct cli_state *state, int *disperse,
  308                               int *redundancy, int *data, int count)
  309 {
  310     int i = 0;
  311     int tmp = 0;
  312     gf_answer_t answer = GF_ANSWER_NO;
  313     char question[128];
  314 
  315     const char *question1 =
  316         "There isn't an optimal redundancy value "
  317         "for this configuration. Do you want to "
  318         "create the volume with redundancy 1 ?";
  319 
  320     const char *question2 =
  321         "The optimal redundancy for this "
  322         "configuration is %d. Do you want to create "
  323         "the volume with this value ?";
  324 
  325     const char *question3 =
  326         "This configuration is not optimal on most "
  327         "workloads. Do you want to use it ?";
  328 
  329     const char *question4 =
  330         "Redundancy for this configuration is %d. "
  331         "Do you want to create "
  332         "the volume with this value ?";
  333 
  334     if (*data > 0) {
  335         if (*disperse > 0 && *redundancy > 0) {
  336             if (*disperse != (*data + *redundancy)) {
  337                 cli_err(
  338                     "Disperse count(%d) should be equal "
  339                     "to sum of disperse-data count(%d) and "
  340                     "redundancy count(%d)",
  341                     *disperse, *data, *redundancy);
  342                 return -1;
  343             }
  344         } else if (*redundancy > 0) {
  345             *disperse = *data + *redundancy;
  346         } else if (*disperse > 0) {
  347             *redundancy = *disperse - *data;
  348         } else {
  349             if ((count - *data) >= *data) {
  350                 cli_err(
  351                     "Please provide redundancy count "
  352                     "along with disperse-data count");
  353                 return -1;
  354             } else {
  355                 sprintf(question, question4, count - *data);
  356                 answer = cli_cmd_get_confirmation(state, question);
  357                 if (answer == GF_ANSWER_NO)
  358                     return -1;
  359                 *redundancy = count - *data;
  360                 *disperse = count;
  361             }
  362         }
  363     }
  364 
  365     if (*disperse <= 0) {
  366         if (count < 3) {
  367             cli_err(
  368                 "number of bricks must be greater "
  369                 "than 2");
  370 
  371             return -1;
  372         }
  373         *disperse = count;
  374     }
  375 
  376     if (*redundancy == -1) {
  377         tmp = *disperse - 1;
  378         for (i = tmp / 2; (i > 0) && ((tmp & -tmp) != tmp); i--, tmp--)
  379             ;
  380 
  381         if (i == 0) {
  382             answer = cli_cmd_get_confirmation(state, question1);
  383             if (answer == GF_ANSWER_NO)
  384                 return -1;
  385 
  386             *redundancy = 1;
  387         } else {
  388             *redundancy = *disperse - tmp;
  389             if (*redundancy > 1) {
  390                 sprintf(question, question2, *redundancy);
  391                 answer = cli_cmd_get_confirmation(state, question);
  392                 if (answer == GF_ANSWER_NO)
  393                     return -1;
  394             }
  395         }
  396 
  397         tmp = 0;
  398     } else {
  399         tmp = *disperse - *redundancy;
  400     }
  401 
  402     if ((*redundancy < 1) || (*redundancy > (*disperse - 1) / 2)) {
  403         cli_err(
  404             "redundancy must be greater than or equal to 1 and "
  405             "less than %d for a disperse %d volume",
  406             (*disperse + 1) / 2, *disperse);
  407 
  408         return -1;
  409     }
  410 
  411     if ((tmp & -tmp) != tmp) {
  412         answer = cli_cmd_get_confirmation(state, question3);
  413         if (answer == GF_ANSWER_NO)
  414             return -1;
  415     }
  416 
  417     return 0;
  418 }
  419 
  420 static int32_t
  421 cli_validate_disperse_volume(char *word, gf1_cluster_type type,
  422                              const char **words, int32_t wordcount,
  423                              int32_t index, int32_t *disperse_count,
  424                              int32_t *redundancy_count, int32_t *data_count)
  425 {
  426     int ret = -1;
  427 
  428     switch (type) {
  429         case GF_CLUSTER_TYPE_NONE:
  430         case GF_CLUSTER_TYPE_DISPERSE:
  431             if (strcmp(word, "disperse") == 0) {
  432                 if (*disperse_count >= 0) {
  433                     cli_err("disperse option given twice");
  434                     goto out;
  435                 }
  436                 if (wordcount < (index + 2)) {
  437                     goto out;
  438                 }
  439                 ret = gf_string2int(words[index + 1], disperse_count);
  440                 if (ret == -1 && errno == EINVAL) {
  441                     *disperse_count = 0;
  442                     ret = 1;
  443                 } else if (ret == -1) {
  444                     goto out;
  445                 } else {
  446                     if (*disperse_count < 3) {
  447                         cli_err(
  448                             "disperse count must "
  449                             "be greater than 2");
  450                         goto out;
  451                     }
  452                     ret = 2;
  453                 }
  454             } else if (strcmp(word, "disperse-data") == 0) {
  455                 if (*data_count >= 0) {
  456                     cli_err("disperse-data option given twice");
  457                     goto out;
  458                 }
  459                 if (wordcount < (index + 2)) {
  460                     goto out;
  461                 }
  462                 ret = gf_string2int(words[index + 1], data_count);
  463                 if (ret == -1 || *data_count < 2) {
  464                     cli_err("disperse-data must be greater than 1");
  465                     goto out;
  466                 }
  467                 ret = 2;
  468             } else if (strcmp(word, "redundancy") == 0) {
  469                 if (*redundancy_count >= 0) {
  470                     cli_err("redundancy option given twice");
  471                     goto out;
  472                 }
  473                 if (wordcount < (index + 2)) {
  474                     goto out;
  475                 }
  476                 ret = gf_string2int(words[index + 1], redundancy_count);
  477                 if (ret == -1 || *redundancy_count < 1) {
  478                     cli_err("redundancy must be greater than 0");
  479                     goto out;
  480                 }
  481                 ret = 2;
  482             }
  483             break;
  484         case GF_CLUSTER_TYPE_REPLICATE:
  485             cli_err(
  486                 "replicated-dispersed volume is not "
  487                 "supported");
  488             goto out;
  489         default:
  490             cli_err("Invalid type given");
  491             break;
  492     }
  493 out:
  494     return ret;
  495 }
  496 
  497 int32_t
  498 cli_validate_volname(const char *volname)
  499 {
  500     int32_t ret = -1;
  501     int32_t i = -1;
  502     int volname_len;
  503     static const char *const invalid_volnames[] = {"volume",
  504                                                    "type",
  505                                                    "subvolumes",
  506                                                    "option",
  507                                                    "end-volume",
  508                                                    "all",
  509                                                    "volume_not_in_ring",
  510                                                    "description",
  511                                                    "force",
  512                                                    "snap-max-hard-limit",
  513                                                    "snap-max-soft-limit",
  514                                                    "auto-delete",
  515                                                    "activate-on-create",
  516                                                    NULL};
  517 
  518     if (volname[0] == '-')
  519         goto out;
  520 
  521     for (i = 0; invalid_volnames[i]; i++) {
  522         if (!strcmp(volname, invalid_volnames[i])) {
  523             cli_err("\"%s\" cannot be the name of a volume.", volname);
  524             goto out;
  525         }
  526     }
  527 
  528     if (strchr(volname, '/'))
  529         goto out;
  530 
  531     volname_len = strlen(volname);
  532     if (volname_len > GD_VOLUME_NAME_MAX) {
  533         cli_err("Volume name exceeds %d characters.", GD_VOLUME_NAME_MAX);
  534         goto out;
  535     }
  536 
  537     for (i = 0; i < volname_len; i++) {
  538         if (!isalnum(volname[i]) && (volname[i] != '_') &&
  539             (volname[i] != '-')) {
  540             cli_err(
  541                 "Volume name should not contain \"%c\""
  542                 " character.\nVolume names can only"
  543                 "contain alphanumeric, '-' and '_' "
  544                 "characters.",
  545                 volname[i]);
  546             goto out;
  547         }
  548     }
  549 
  550     ret = 0;
  551 out:
  552     return ret;
  553 }
  554 
  555 int32_t
  556 cli_cmd_volume_create_parse(struct cli_state *state, const char **words,
  557                             int wordcount, dict_t **options, char **brick_list)
  558 {
  559     dict_t *dict = NULL;
  560     char *volname = NULL;
  561     int ret = -1;
  562     gf1_cluster_type type = GF_CLUSTER_TYPE_NONE;
  563     int sub_count = 1;
  564     int brick_index = 0;
  565     char *trans_type = NULL;
  566     int32_t index = 0;
  567     char *bricks = NULL;
  568     char *ta_brick = NULL;
  569     int32_t brick_count = 0;
  570     static char *opwords[] = {"replica",  "stripe",       "transport",
  571                               "disperse", "redundancy",   "disperse-data",
  572                               "arbiter",  "thin-arbiter", NULL};
  573 
  574     char *w = NULL;
  575     int op_count = 0;
  576     int32_t replica_count = 1;
  577     int32_t arbiter_count = 0;
  578     int32_t thin_arbiter_count = 0;
  579     int32_t stripe_count = 1;
  580     int32_t disperse_count = -1;
  581     int32_t redundancy_count = -1;
  582     int32_t disperse_data_count = -1;
  583     gf_boolean_t is_force = _gf_false;
  584     int wc = wordcount;
  585     gf_answer_t answer = GF_ANSWER_NO;
  586     const char *question = NULL;
  587 
  588     GF_ASSERT(words);
  589     GF_ASSERT(options);
  590 
  591     dict = dict_new();
  592 
  593     if (!dict)
  594         goto out;
  595 
  596     if (wordcount < 3)
  597         goto out;
  598 
  599     volname = (char *)words[2];
  600 
  601     GF_ASSERT(volname);
  602 
  603     /* Validate the volume name here itself */
  604     if (cli_validate_volname(volname) < 0)
  605         goto out;
  606 
  607     if (wordcount < 4) {
  608         ret = -1;
  609         goto out;
  610     }
  611 
  612     type = GF_CLUSTER_TYPE_NONE;
  613     index = 3;
  614 
  615     while (op_count < 3) {
  616         ret = -1;
  617         w = str_getunamb(words[index], opwords);
  618         if (!w) {
  619             break;
  620         } else if ((strcmp(w, "replica")) == 0) {
  621             switch (type) {
  622                 case GF_CLUSTER_TYPE_STRIPE_REPLICATE:
  623                 case GF_CLUSTER_TYPE_REPLICATE:
  624                     cli_err("replica option given twice");
  625                     goto out;
  626                 case GF_CLUSTER_TYPE_NONE:
  627                     type = GF_CLUSTER_TYPE_REPLICATE;
  628                     break;
  629                 case GF_CLUSTER_TYPE_STRIPE:
  630                     cli_err("stripe option not supported");
  631                     goto out;
  632                 case GF_CLUSTER_TYPE_DISPERSE:
  633                     cli_err(
  634                         "replicated-dispersed volume is not "
  635                         "supported");
  636                     goto out;
  637                 default:
  638                     cli_err("Invalid type given");
  639                     goto out;
  640             }
  641 
  642             if (wordcount < (index + 2)) {
  643                 ret = -1;
  644                 goto out;
  645             }
  646 
  647             replica_count = strtol(words[index + 1], NULL, 0);
  648             if (replica_count < 2) {
  649                 cli_err(
  650                     "replica count should be greater"
  651                     " than 1");
  652                 ret = -1;
  653                 goto out;
  654             }
  655 
  656             index += 2;
  657             if (words[index]) {
  658                 if (!strcmp(words[index], "arbiter")) {
  659                     ret = gf_string2int(words[index + 1], &arbiter_count);
  660                     if ((ret == -1) || (arbiter_count != 1)) {
  661                         cli_err(
  662                             "For arbiter "
  663                             "configuration, "
  664                             "replica count must be"
  665                             " 2 and arbiter count "
  666                             "must be 1. The 3rd "
  667                             "brick of the replica "
  668                             "will be the arbiter");
  669                         ret = -1;
  670                         goto out;
  671                     }
  672                     ret = dict_set_int32(dict, "arbiter-count", arbiter_count);
  673                     if (ret)
  674                         goto out;
  675                     index += 2;
  676                 } else if (!strcmp(words[index], "thin-arbiter")) {
  677                     ret = gf_string2int(words[index + 1], &thin_arbiter_count);
  678                     if ((ret == -1) || (thin_arbiter_count != 1) ||
  679                         (replica_count != 2)) {
  680                         cli_err(
  681                             "For thin-arbiter "
  682                             "configuration, "
  683                             "replica count must be"
  684                             " 2 and thin-arbiter count "
  685                             "must be 1. The 3rd "
  686                             "brick of the replica "
  687                             "will be the thin-arbiter brick");
  688                         ret = -1;
  689                         goto out;
  690                     }
  691                     ret = dict_set_int32(dict, "thin-arbiter-count",
  692                                          thin_arbiter_count);
  693                     if (ret)
  694                         goto out;
  695                     index += 2;
  696                 }
  697             }
  698 
  699             /* Do this to keep glusterd happy with sending
  700                "replica 3 arbiter 1" options to server */
  701             if ((arbiter_count == 1) && (replica_count == 2))
  702                 replica_count += arbiter_count;
  703 
  704             if (replica_count == 2 && thin_arbiter_count == 0) {
  705                 if (strcmp(words[wordcount - 1], "force")) {
  706                     question =
  707                         "Replica 2 volumes are prone"
  708                         " to split-brain. Use "
  709                         "Arbiter or Replica 3 to "
  710                         "avoid this. See: "
  711                         "http://docs.gluster.org/en/latest/"
  712                         "Administrator%20Guide/"
  713                         "Split%20brain%20and%20ways%20to%20deal%20with%20it/."
  714                         "\nDo you still want to "
  715                         "continue?\n";
  716                     answer = cli_cmd_get_confirmation(state, question);
  717                     if (GF_ANSWER_NO == answer) {
  718                         gf_log("cli", GF_LOG_ERROR,
  719                                "Volume create "
  720                                "cancelled, exiting");
  721                         ret = -1;
  722                         goto out;
  723                     }
  724                 }
  725             }
  726             ret = dict_set_int32(dict, "replica-count", replica_count);
  727             if (ret)
  728                 goto out;
  729 
  730         } else if ((strcmp(w, "stripe")) == 0) {
  731             cli_err("stripe option not supported");
  732             goto out;
  733         } else if ((strcmp(w, "transport")) == 0) {
  734             if (trans_type) {
  735                 cli_err(
  736                     "'transport' option given more"
  737                     " than one time");
  738                 goto out;
  739             }
  740             if ((strcasecmp(words[index + 1], "tcp") == 0)) {
  741                 trans_type = gf_strdup("tcp");
  742             } else if ((strcasecmp(words[index + 1], "rdma") == 0)) {
  743                 trans_type = gf_strdup("rdma");
  744             } else if ((strcasecmp(words[index + 1], "tcp,rdma") == 0) ||
  745                        (strcasecmp(words[index + 1], "rdma,tcp") == 0)) {
  746                 trans_type = gf_strdup("tcp,rdma");
  747             } else {
  748                 gf_log("", GF_LOG_ERROR,
  749                        "incorrect transport"
  750                        " protocol specified");
  751                 ret = -1;
  752                 goto out;
  753             }
  754             index += 2;
  755 
  756         } else if ((strcmp(w, "disperse") == 0) ||
  757                    (strcmp(w, "redundancy") == 0) ||
  758                    (strcmp(w, "disperse-data") == 0)) {
  759             ret = cli_validate_disperse_volume(
  760                 w, type, words, wordcount, index, &disperse_count,
  761                 &redundancy_count, &disperse_data_count);
  762             if (ret < 0)
  763                 goto out;
  764             index += ret;
  765             type = GF_CLUSTER_TYPE_DISPERSE;
  766         } else if ((strcmp(w, "arbiter") == 0)) {
  767             cli_err(
  768                 "arbiter option must be preceded by replica "
  769                 "option.");
  770             ret = -1;
  771             goto out;
  772         } else if ((strcmp(w, "thin-arbiter") == 0)) {
  773             cli_err(
  774                 "thin-arbiter option must be preceded by replica "
  775                 "option.");
  776             ret = -1;
  777             goto out;
  778         } else {
  779             GF_ASSERT(!"opword mismatch");
  780             ret = -1;
  781             goto out;
  782         }
  783         op_count++;
  784     }
  785 
  786     if (!trans_type)
  787         trans_type = gf_strdup("tcp");
  788 
  789     if (index >= wordcount) {
  790         ret = -1;
  791         goto out;
  792     }
  793 
  794     brick_index = index;
  795 
  796     if (strcmp(words[wordcount - 1], "force") == 0) {
  797         is_force = _gf_true;
  798         wc = wordcount - 1;
  799     }
  800 
  801     // Exclude the thin-arbiter-brick i.e. last brick in the bricks list
  802     if (thin_arbiter_count == 1) {
  803         ret = cli_cmd_bricks_parse(words, wc - 1, brick_index, &bricks,
  804                                    &brick_count);
  805         if (ret)
  806             goto out;
  807 
  808         ret = cli_cmd_ta_brick_parse(words, wc, &ta_brick);
  809 
  810     } else {
  811         ret = cli_cmd_bricks_parse(words, wc, brick_index, &bricks,
  812                                    &brick_count);
  813     }
  814 
  815     if (ret)
  816         goto out;
  817 
  818     if (type == GF_CLUSTER_TYPE_DISPERSE) {
  819         ret = cli_cmd_create_disperse_check(state, &disperse_count,
  820                                             &redundancy_count,
  821                                             &disperse_data_count, brick_count);
  822         if (!ret)
  823             ret = dict_set_int32(dict, "disperse-count", disperse_count);
  824         if (!ret)
  825             ret = dict_set_int32(dict, "redundancy-count", redundancy_count);
  826         if (ret)
  827             goto out;
  828 
  829         sub_count = disperse_count;
  830     } else
  831         sub_count = stripe_count * replica_count;
  832 
  833     if (brick_count % sub_count) {
  834         if (type == GF_CLUSTER_TYPE_STRIPE)
  835             cli_err(
  836                 "number of bricks is not a multiple of "
  837                 "stripe count");
  838         else if (type == GF_CLUSTER_TYPE_REPLICATE)
  839             cli_err(
  840                 "number of bricks is not a multiple of "
  841                 "replica count");
  842         else if (type == GF_CLUSTER_TYPE_DISPERSE)
  843             cli_err(
  844                 "number of bricks is not a multiple of "
  845                 "disperse count");
  846         else
  847             cli_err(
  848                 "number of bricks given doesn't match "
  849                 "required count");
  850 
  851         ret = -1;
  852         goto out;
  853     }
  854 
  855     /* Everything is parsed fine. start setting info in dict */
  856     ret = dict_set_str(dict, "volname", volname);
  857     if (ret)
  858         goto out;
  859 
  860     ret = dict_set_int32(dict, "type", type);
  861     if (ret)
  862         goto out;
  863 
  864     ret = dict_set_dynstr(dict, "transport", trans_type);
  865     if (ret)
  866         goto out;
  867     trans_type = NULL;
  868 
  869     ret = dict_set_dynstr(dict, "bricks", bricks);
  870     if (ret)
  871         goto out;
  872 
  873     if (thin_arbiter_count == 1) {
  874         ret = dict_set_dynstr(dict, "ta-brick", ta_brick);
  875         if (ret)
  876             goto out;
  877     }
  878 
  879     ret = dict_set_int32(dict, "count", brick_count);
  880     if (ret)
  881         goto out;
  882 
  883     ret = dict_set_int32(dict, "force", is_force);
  884     if (ret)
  885         goto out;
  886 
  887     *options = dict;
  888     *brick_list = bricks;
  889 out:
  890     if (ret) {
  891         GF_FREE(bricks);
  892         GF_FREE(ta_brick);
  893         gf_log("cli", GF_LOG_ERROR, "Unable to parse create volume CLI");
  894         if (dict)
  895             dict_unref(dict);
  896     }
  897 
  898     GF_FREE(trans_type);
  899 
  900     return ret;
  901 }
  902 
  903 int32_t
  904 cli_cmd_volume_reset_parse(const char **words, int wordcount, dict_t **options)
  905 {
  906     dict_t *dict = NULL;
  907     char *volname = NULL;
  908     int ret = -1;
  909 
  910     GF_ASSERT(words);
  911     GF_ASSERT(options);
  912 
  913     dict = dict_new();
  914 
  915     if (!dict)
  916         goto out;
  917 
  918     if (wordcount < 3)
  919         goto out;
  920 
  921     if (wordcount > 5)
  922         goto out;
  923 
  924     volname = (char *)words[2];
  925 
  926     if (!volname) {
  927         ret = -1;
  928         goto out;
  929     }
  930 
  931     ret = dict_set_str(dict, "volname", volname);
  932     if (ret)
  933         goto out;
  934 
  935     if (wordcount == 3) {
  936         ret = dict_set_str(dict, "key", "all");
  937         if (ret)
  938             goto out;
  939     }
  940 
  941     if (wordcount >= 4) {
  942         if (!strcmp("force", (char *)words[3])) {
  943             ret = dict_set_int32(dict, "force", 1);
  944             if (ret)
  945                 goto out;
  946             ret = dict_set_str(dict, "key", "all");
  947             if (ret)
  948                 goto out;
  949         } else {
  950             ret = dict_set_str(dict, "key", (char *)words[3]);
  951             if (ret)
  952                 goto out;
  953         }
  954     }
  955 
  956     if (wordcount == 5) {
  957         if (strcmp("force", (char *)words[4])) {
  958             ret = -1;
  959             goto out;
  960         } else {
  961             ret = dict_set_int32(dict, "force", 1);
  962             if (ret)
  963                 goto out;
  964         }
  965     }
  966 
  967     *options = dict;
  968 
  969 out:
  970     if (ret && dict) {
  971         dict_unref(dict);
  972     }
  973 
  974     return ret;
  975 }
  976 
  977 int32_t
  978 cli_cmd_get_state_parse(struct cli_state *state, const char **words,
  979                         int wordcount, dict_t **options, char **op_errstr)
  980 {
  981     dict_t *dict = NULL;
  982     int ret = -1;
  983     char *odir = NULL;
  984     char *filename = NULL;
  985     char *daemon_name = NULL;
  986     int count = 0;
  987     uint32_t cmd = 0;
  988 
  989     GF_VALIDATE_OR_GOTO("cli", options, out);
  990     GF_VALIDATE_OR_GOTO("cli", words, out);
  991 
  992     dict = dict_new();
  993     if (!dict)
  994         goto out;
  995 
  996     if (wordcount < 1 || wordcount > 7) {
  997         *op_errstr = gf_strdup(
  998             "Problem parsing arguments."
  999             " Check usage.");
 1000         goto out;
 1001     }
 1002 
 1003     if (wordcount >= 1) {
 1004         gf_asprintf(&daemon_name, "%s", "glusterd");
 1005 
 1006         for (count = 1; count < wordcount; count++) {
 1007             if (strcmp(words[count], "odir") == 0 ||
 1008                 strcmp(words[count], "file") == 0) {
 1009                 if (strcmp(words[count], "odir") == 0) {
 1010                     if (++count < wordcount) {
 1011                         odir = (char *)words[count];
 1012                         continue;
 1013                     } else {
 1014                         ret = -1;
 1015                         goto out;
 1016                     }
 1017                 } else if (strcmp(words[count], "file") == 0) {
 1018                     if (++count < wordcount) {
 1019                         filename = (char *)words[count];
 1020                         continue;
 1021                     } else {
 1022                         ret = -1;
 1023                         goto out;
 1024                     }
 1025                 }
 1026             } else {
 1027                 if (count > 1) {
 1028                     if (count == wordcount - 1) {
 1029                         if (strcmp(words[count], "detail") == 0) {
 1030                             cmd = GF_CLI_GET_STATE_DETAIL;
 1031                             continue;
 1032                         } else if (strcmp(words[count], "volumeoptions") == 0) {
 1033                             cmd = GF_CLI_GET_STATE_VOLOPTS;
 1034                             continue;
 1035                         }
 1036                     } else {
 1037                         *op_errstr = gf_strdup(
 1038                             "Problem"
 1039                             " parsing arguments. "
 1040                             "Check usage.");
 1041                         ret = -1;
 1042                         goto out;
 1043                     }
 1044                 }
 1045                 if (strcmp(words[count], "glusterd") == 0) {
 1046                     continue;
 1047                 } else {
 1048                     if (count == wordcount - 1) {
 1049                         if (strcmp(words[count], "detail") == 0) {
 1050                             cmd = GF_CLI_GET_STATE_DETAIL;
 1051                             continue;
 1052                         } else if (strcmp(words[count], "volumeoptions") == 0) {
 1053                             cmd = GF_CLI_GET_STATE_VOLOPTS;
 1054                             continue;
 1055                         }
 1056                     }
 1057 
 1058                     *op_errstr = gf_strdup(
 1059                         "glusterd is "
 1060                         "the only supported daemon.");
 1061                     ret = -1;
 1062                     goto out;
 1063                 }
 1064             }
 1065         }
 1066 
 1067         ret = dict_set_dynstr(dict, "daemon", daemon_name);
 1068         if (ret) {
 1069             *op_errstr = gf_strdup(
 1070                 "Command failed. Please check "
 1071                 " log file for more details.");
 1072             gf_log(THIS->name, GF_LOG_ERROR,
 1073                    "Setting daemon name to dictionary failed");
 1074             goto out;
 1075         }
 1076         daemon_name = NULL;
 1077 
 1078         if (odir) {
 1079             ret = dict_set_str(dict, "odir", odir);
 1080             if (ret) {
 1081                 *op_errstr = gf_strdup(
 1082                     "Command failed. Please"
 1083                     " check log file for"
 1084                     " more details.");
 1085                 gf_log(THIS->name, GF_LOG_ERROR,
 1086                        "Setting output directory to"
 1087                        "dictionary failed");
 1088                 goto out;
 1089             }
 1090         }
 1091 
 1092         if (filename) {
 1093             ret = dict_set_str(dict, "filename", filename);
 1094             if (ret) {
 1095                 *op_errstr = gf_strdup(
 1096                     "Command failed. Please"
 1097                     " check log file for"
 1098                     " more  details.");
 1099                 gf_log(THIS->name, GF_LOG_ERROR,
 1100                        "Setting filename to dictionary failed");
 1101                 goto out;
 1102             }
 1103         }
 1104 
 1105         if (cmd) {
 1106             ret = dict_set_uint32(dict, "getstate-cmd", cmd);
 1107             if (ret) {
 1108                 *op_errstr = gf_strdup(
 1109                     "Command failed. Please"
 1110                     " check log file for"
 1111                     " more details.");
 1112                 gf_log(THIS->name, GF_LOG_ERROR,
 1113                        "Setting "
 1114                        "get-state command type to dictionary "
 1115                        "failed");
 1116                 goto out;
 1117             }
 1118         }
 1119     }
 1120 
 1121 out:
 1122     if (dict)
 1123         *options = dict;
 1124 
 1125     if (ret && dict)
 1126         dict_unref(dict);
 1127 
 1128     GF_FREE(daemon_name);
 1129 
 1130     return ret;
 1131 }
 1132 
 1133 int32_t
 1134 cli_cmd_inode_quota_parse(const char **words, int wordcount, dict_t **options)
 1135 {
 1136     dict_t *dict = NULL;
 1137     char *volname = NULL;
 1138     int ret = -1;
 1139 
 1140     GF_ASSERT(words);
 1141     GF_ASSERT(options);
 1142 
 1143     dict = dict_new();
 1144     if (!dict) {
 1145         gf_log("cli", GF_LOG_ERROR, "dict_new failed");
 1146         goto out;
 1147     }
 1148 
 1149     if (wordcount != 4)
 1150         goto out;
 1151 
 1152     volname = (char *)words[2];
 1153     if (!volname) {
 1154         ret = -1;
 1155         goto out;
 1156     }
 1157 
 1158     /* Validate the volume name here itself */
 1159     if (cli_validate_volname(volname) < 0)
 1160         goto out;
 1161 
 1162     ret = dict_set_str(dict, "volname", volname);
 1163     if (ret < 0)
 1164         goto out;
 1165 
 1166     if (strcmp(words[3], "enable") != 0) {
 1167         cli_out("Invalid quota option : %s", words[3]);
 1168         ret = -1;
 1169         goto out;
 1170     }
 1171 
 1172     ret = dict_set_int32(dict, "type", GF_QUOTA_OPTION_TYPE_ENABLE_OBJECTS);
 1173     if (ret < 0)
 1174         goto out;
 1175 
 1176     *options = dict;
 1177 out:
 1178     if (ret < 0) {
 1179         if (dict)
 1180             dict_unref(dict);
 1181     }
 1182 
 1183     return ret;
 1184 }
 1185 
 1186 int32_t
 1187 cli_cmd_quota_parse(const char **words, int wordcount, dict_t **options)
 1188 {
 1189     dict_t *dict = NULL;
 1190     char *volname = NULL;
 1191     int ret = -1;
 1192     int i = -1;
 1193     char key[20] = {
 1194         0,
 1195     };
 1196     int64_t value = 0;
 1197     gf_quota_type type = GF_QUOTA_OPTION_TYPE_NONE;
 1198     static char *opwords[] = {"enable",
 1199                               "disable",
 1200                               "limit-usage",
 1201                               "remove",
 1202                               "list",
 1203                               "alert-time",
 1204                               "soft-timeout",
 1205                               "hard-timeout",
 1206                               "default-soft-limit",
 1207                               "limit-objects",
 1208                               "list-objects",
 1209                               "remove-objects",
 1210                               NULL};
 1211     char *w = NULL;
 1212     uint32_t time = 0;
 1213     double percent = 0;
 1214     char *end_ptr = NULL;
 1215     int64_t limit = 0;
 1216 
 1217     GF_ASSERT(words);
 1218     GF_ASSERT(options);
 1219 
 1220     dict = dict_new();
 1221     if (!dict) {
 1222         gf_log("cli", GF_LOG_ERROR, "dict_new failed");
 1223         goto out;
 1224     }
 1225 
 1226     if (wordcount < 4) {
 1227         if ((wordcount == 3) && !(strcmp(words[2], "help"))) {
 1228             ret = 1;
 1229         }
 1230         goto out;
 1231     }
 1232 
 1233     volname = (char *)words[2];
 1234     if (!volname) {
 1235         ret = -1;
 1236         goto out;
 1237     }
 1238 
 1239     /* Validate the volume name here itself */
 1240     if (cli_validate_volname(volname) < 0)
 1241         goto out;
 1242 
 1243     ret = dict_set_str(dict, "volname", volname);
 1244     if (ret < 0)
 1245         goto out;
 1246 
 1247     w = str_getunamb(words[3], opwords);
 1248     if (!w) {
 1249         cli_out("Invalid quota option : %s", words[3]);
 1250         ret = -1;
 1251         goto out;
 1252     }
 1253 
 1254     if (strcmp(w, "enable") == 0) {
 1255         if (wordcount == 4) {
 1256             type = GF_QUOTA_OPTION_TYPE_ENABLE;
 1257             ret = 0;
 1258             goto set_type;
 1259         } else {
 1260             ret = -1;
 1261             goto out;
 1262         }
 1263     }
 1264 
 1265     if (strcmp(w, "disable") == 0) {
 1266         if (wordcount == 4) {
 1267             type = GF_QUOTA_OPTION_TYPE_DISABLE;
 1268             ret = 0;
 1269             goto set_type;
 1270         } else {
 1271             ret = -1;
 1272             goto out;
 1273         }
 1274     }
 1275 
 1276     if (strcmp(w, "limit-usage") == 0) {
 1277         type = GF_QUOTA_OPTION_TYPE_LIMIT_USAGE;
 1278     } else if (strcmp(w, "limit-objects") == 0) {
 1279         type = GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS;
 1280     }
 1281 
 1282     if (type == GF_QUOTA_OPTION_TYPE_LIMIT_USAGE ||
 1283         type == GF_QUOTA_OPTION_TYPE_LIMIT_OBJECTS) {
 1284         if (wordcount < 6 || wordcount > 7) {
 1285             ret = -1;
 1286             goto out;
 1287         }
 1288 
 1289         if (words[4][0] != '/') {
 1290             cli_err("Please enter absolute path");
 1291             ret = -1;
 1292             goto out;
 1293         }
 1294         ret = dict_set_str(dict, "path", (char *)words[4]);
 1295         if (ret)
 1296             goto out;
 1297 
 1298         if (!words[5]) {
 1299             cli_err("Please enter the limit value to be set");
 1300             ret = -1;
 1301             goto out;
 1302         }
 1303 
 1304         if (type == GF_QUOTA_OPTION_TYPE_LIMIT_USAGE) {
 1305             ret = gf_string2bytesize_int64(words[5], &value);
 1306             if (ret != 0 || value <= 0) {
 1307                 if (errno == ERANGE || value <= 0) {
 1308                     ret = -1;
 1309                     cli_err(
 1310                         "Please enter an integer "
 1311                         "value in the range of "
 1312                         "(1 - %" PRId64 ")",
 1313                         INT64_MAX);
 1314                 } else
 1315                     cli_err(
 1316                         "Please enter a correct "
 1317                         "value");
 1318                 goto out;
 1319             }
 1320         } else {
 1321             errno = 0;
 1322             limit = strtol(words[5], &end_ptr, 10);
 1323             if (errno == ERANGE || errno == EINVAL || limit <= 0 ||
 1324                 strcmp(end_ptr, "") != 0) {
 1325                 ret = -1;
 1326                 cli_err(
 1327                     "Please enter an integer value in "
 1328                     "the range 1 - %" PRId64,
 1329                     INT64_MAX);
 1330                 goto out;
 1331             }
 1332         }
 1333 
 1334         ret = dict_set_str(dict, "hard-limit", (char *)words[5]);
 1335         if (ret < 0)
 1336             goto out;
 1337 
 1338         if (wordcount == 7) {
 1339             ret = gf_string2percent(words[6], &percent);
 1340             if (ret != 0 || percent > 100) {
 1341                 ret = -1;
 1342                 cli_err(
 1343                     "Please enter a correct value "
 1344                     "in the range of 0 to 100");
 1345                 goto out;
 1346             }
 1347 
 1348             ret = dict_set_str(dict, "soft-limit", (char *)words[6]);
 1349             if (ret < 0)
 1350                 goto out;
 1351         }
 1352 
 1353         goto set_type;
 1354     }
 1355 
 1356     if (strcmp(w, "remove") == 0) {
 1357         if (wordcount != 5) {
 1358             ret = -1;
 1359             goto out;
 1360         }
 1361 
 1362         type = GF_QUOTA_OPTION_TYPE_REMOVE;
 1363 
 1364         if (words[4][0] != '/') {
 1365             cli_err("Please enter absolute path");
 1366             ret = -1;
 1367             goto out;
 1368         }
 1369 
 1370         ret = dict_set_str(dict, "path", (char *)words[4]);
 1371         if (ret < 0)
 1372             goto out;
 1373         goto set_type;
 1374     }
 1375 
 1376     if (strcmp(w, "remove-objects") == 0) {
 1377         if (wordcount != 5) {
 1378             ret = -1;
 1379             goto out;
 1380         }
 1381 
 1382         type = GF_QUOTA_OPTION_TYPE_REMOVE_OBJECTS;
 1383 
 1384         if (words[4][0] != '/') {
 1385             cli_err("Please enter absolute path");
 1386             ret = -1;
 1387             goto out;
 1388         }
 1389 
 1390         ret = dict_set_str(dict, "path", (char *)words[4]);
 1391         if (ret < 0)
 1392             goto out;
 1393         goto set_type;
 1394     }
 1395 
 1396     if (strcmp(w, "list") == 0) {
 1397         type = GF_QUOTA_OPTION_TYPE_LIST;
 1398 
 1399         if (words[4] && words[4][0] != '/') {
 1400             cli_err("Please enter absolute path");
 1401             ret = -1;
 1402             goto out;
 1403         }
 1404 
 1405         i = 4;
 1406         while (i < wordcount) {
 1407             snprintf(key, 20, "path%d", i - 4);
 1408 
 1409             ret = dict_set_str(dict, key, (char *)words[i++]);
 1410             if (ret < 0)
 1411                 goto out;
 1412         }
 1413 
 1414         ret = dict_set_int32(dict, "count", i - 4);
 1415         if (ret < 0)
 1416             goto out;
 1417 
 1418         goto set_type;
 1419     }
 1420 
 1421     if (strcmp(w, "list-objects") == 0) {
 1422         type = GF_QUOTA_OPTION_TYPE_LIST_OBJECTS;
 1423 
 1424         i = 4;
 1425         while (i < wordcount) {
 1426             snprintf(key, 20, "path%d", i - 4);
 1427 
 1428             ret = dict_set_str(dict, key, (char *)words[i++]);
 1429             if (ret < 0) {
 1430                 gf_log("cli", GF_LOG_ERROR,
 1431                        "Failed to set "
 1432                        "quota patch in request dictionary");
 1433                 goto out;
 1434             }
 1435         }
 1436 
 1437         ret = dict_set_int32(dict, "count", i - 4);
 1438         if (ret < 0) {
 1439             gf_log("cli", GF_LOG_ERROR,
 1440                    "Failed to set quota "
 1441                    "limit count in request dictionary");
 1442             goto out;
 1443         }
 1444 
 1445         goto set_type;
 1446     }
 1447 
 1448     if (strcmp(w, "alert-time") == 0) {
 1449         if (wordcount != 5) {
 1450             ret = -1;
 1451             goto out;
 1452         }
 1453         type = GF_QUOTA_OPTION_TYPE_ALERT_TIME;
 1454 
 1455         ret = gf_string2time(words[4], &time);
 1456         if (ret) {
 1457             cli_err(
 1458                 "Invalid argument %s. Please enter a valid "
 1459                 "string",
 1460                 words[4]);
 1461             goto out;
 1462         }
 1463 
 1464         ret = dict_set_str(dict, "value", (char *)words[4]);
 1465         if (ret < 0)
 1466             goto out;
 1467         goto set_type;
 1468     }
 1469 
 1470     if (strcmp(w, "soft-timeout") == 0) {
 1471         if (wordcount != 5) {
 1472             ret = -1;
 1473             goto out;
 1474         }
 1475         type = GF_QUOTA_OPTION_TYPE_SOFT_TIMEOUT;
 1476 
 1477         ret = gf_string2time(words[4], &time);
 1478         if (ret) {
 1479             cli_err(
 1480                 "Invalid argument %s. Please enter a valid "
 1481                 "string",
 1482                 words[4]);
 1483             goto out;
 1484         }
 1485 
 1486         ret = dict_set_str(dict, "value", (char *)words[4]);
 1487         if (ret < 0)
 1488             goto out;
 1489         goto set_type;
 1490     }
 1491 
 1492     if (strcmp(w, "hard-timeout") == 0) {
 1493         if (wordcount != 5) {
 1494             ret = -1;
 1495             goto out;
 1496         }
 1497         type = GF_QUOTA_OPTION_TYPE_HARD_TIMEOUT;
 1498 
 1499         ret = gf_string2time(words[4], &time);
 1500         if (ret) {
 1501             cli_err(
 1502                 "Invalid argument %s. Please enter a valid "
 1503                 "string",
 1504                 words[4]);
 1505             goto out;
 1506         }
 1507 
 1508         ret = dict_set_str(dict, "value", (char *)words[4]);
 1509         if (ret < 0)
 1510             goto out;
 1511         goto set_type;
 1512     }
 1513     if (strcmp(w, "default-soft-limit") == 0) {
 1514         if (wordcount != 5) {
 1515             ret = -1;
 1516             goto out;
 1517         }
 1518         type = GF_QUOTA_OPTION_TYPE_DEFAULT_SOFT_LIMIT;
 1519 
 1520         ret = dict_set_str(dict, "value", (char *)words[4]);
 1521         if (ret < 0)
 1522             goto out;
 1523         goto set_type;
 1524     } else {
 1525         GF_ASSERT(!"opword mismatch");
 1526     }
 1527 
 1528 set_type:
 1529     ret = dict_set_int32(dict, "type", type);
 1530     if (ret < 0)
 1531         goto out;
 1532 
 1533     *options = dict;
 1534 out:
 1535     if (ret < 0) {
 1536         if (dict)
 1537             dict_unref(dict);
 1538     }
 1539 
 1540     return ret;
 1541 }
 1542 
 1543 static gf_boolean_t
 1544 cli_is_key_spl(char *key)
 1545 {
 1546     return (strcmp(key, "group") == 0);
 1547 }
 1548 
 1549 static int32_t
 1550 cli_add_key_group_value(dict_t *dict, const char *name, const char *value,
 1551                         int32_t id, char **op_errstr)
 1552 {
 1553     char *key = NULL;
 1554     char *data = NULL;
 1555     int32_t ret = -1;
 1556 
 1557     ret = gf_asprintf(&key, "%s%d", name, id);
 1558     if (ret < 0) {
 1559         goto out;
 1560     }
 1561     data = gf_strdup(value);
 1562     if (data == NULL) {
 1563         gf_log(THIS->name, GF_LOG_ERROR, "Failed to allocate memory for data");
 1564         ret = -1;
 1565         goto out;
 1566     }
 1567 
 1568     ret = dict_set_dynstr(dict, key, data);
 1569     if (ret == 0) {
 1570         data = NULL;
 1571     }
 1572 
 1573 out:
 1574     GF_FREE(key);
 1575     GF_FREE(data);
 1576 
 1577     if ((ret != 0) && (op_errstr != NULL)) {
 1578         *op_errstr = gf_strdup("Failed to allocate memory");
 1579     }
 1580 
 1581     return ret;
 1582 }
 1583 
 1584 static int
 1585 cli_add_key_group(dict_t *dict, char *key, char *value, char **op_errstr)
 1586 {
 1587     int ret = -1;
 1588     int opt_count = 0;
 1589     char *saveptr = NULL;
 1590     char *tok_key = NULL;
 1591     char *tok_val = NULL;
 1592     char *tagpath = NULL;
 1593     char line[PATH_MAX + 256] = {
 1594         0,
 1595     };
 1596     FILE *fp = NULL;
 1597 
 1598     ret = gf_asprintf(&tagpath, "%s/groups/%s", GLUSTERD_DEFAULT_WORKDIR,
 1599                       value);
 1600     if (ret == -1) {
 1601         tagpath = NULL;
 1602         goto out;
 1603     }
 1604 
 1605     fp = fopen(tagpath, "r");
 1606     if (!fp) {
 1607         ret = -1;
 1608         if (op_errstr) {
 1609             gf_asprintf(op_errstr,
 1610                         "Unable to open file '%s'. "
 1611                         "Error: %s",
 1612                         tagpath, strerror(errno));
 1613         }
 1614         goto out;
 1615     }
 1616 
 1617     opt_count = 0;
 1618     while (fgets(line, sizeof(line), fp) != NULL) {
 1619         if (strlen(line) >= sizeof(line) - 1) {
 1620             ret = -1;
 1621             if (op_errstr != NULL) {
 1622                 *op_errstr = gf_strdup("Line too long");
 1623             }
 1624             goto out;
 1625         }
 1626 
 1627         /* Treat line that start with "#" as comments */
 1628         if ('#' == line[0])
 1629             continue;
 1630 
 1631         opt_count++;
 1632         tok_key = strtok_r(line, "=", &saveptr);
 1633         tok_val = strtok_r(NULL, "\r\n", &saveptr);
 1634         if (!tok_key || !tok_val) {
 1635             ret = -1;
 1636             if (op_errstr) {
 1637                 gf_asprintf(op_errstr,
 1638                             "'%s' file format "
 1639                             "not valid.",
 1640                             tagpath);
 1641             }
 1642             goto out;
 1643         }
 1644 
 1645         ret = cli_add_key_group_value(dict, "key", tok_key, opt_count,
 1646                                       op_errstr);
 1647         if (ret != 0) {
 1648             goto out;
 1649         }
 1650         ret = cli_add_key_group_value(dict, "value", tok_val, opt_count,
 1651                                       op_errstr);
 1652         if (ret != 0) {
 1653             goto out;
 1654         }
 1655     }
 1656 
 1657     if (!opt_count) {
 1658         ret = -1;
 1659         if (op_errstr) {
 1660             gf_asprintf(op_errstr, "'%s' file format not valid.", tagpath);
 1661         }
 1662         goto out;
 1663     }
 1664     ret = dict_set_int32(dict, "count", opt_count);
 1665 out:
 1666 
 1667     GF_FREE(tagpath);
 1668 
 1669     if (fp)
 1670         fclose(fp);
 1671 
 1672     return ret;
 1673 }
 1674 
 1675 int32_t
 1676 cli_cmd_volume_set_parse(struct cli_state *state, const char **words,
 1677                          int wordcount, dict_t **options, char **op_errstr)
 1678 {
 1679     dict_t *dict = NULL;
 1680     char *volname = NULL;
 1681     int ret = -1;
 1682     int count = 0;
 1683     char *key = NULL;
 1684     char *value = NULL;
 1685     int i = 0;
 1686     char str[50] = {
 1687         0,
 1688     };
 1689     const char *question = NULL;
 1690     gf_answer_t answer = GF_ANSWER_NO;
 1691 
 1692     GF_ASSERT(words);
 1693     GF_ASSERT(options);
 1694 
 1695     dict = dict_new();
 1696 
 1697     if (!dict)
 1698         goto out;
 1699 
 1700     if (wordcount < 3)
 1701         goto out;
 1702 
 1703     volname = (char *)words[2];
 1704 
 1705     GF_ASSERT(volname);
 1706 
 1707     ret = dict_set_str(dict, "volname", volname);
 1708 
 1709     if (ret)
 1710         goto out;
 1711 
 1712     if (!strcmp(volname, "all")) {
 1713         ret = dict_set_str(dict, "globalname", "All");
 1714         if (ret) {
 1715             gf_log(THIS->name, GF_LOG_ERROR, "dict set on global key failed.");
 1716             goto out;
 1717         }
 1718 
 1719         ret = dict_set_int32(dict, "hold_global_locks", _gf_true);
 1720         if (ret) {
 1721             gf_log(THIS->name, GF_LOG_ERROR, "dict set on global key failed.");
 1722             goto out;
 1723         }
 1724     }
 1725 
 1726     if ((!strcmp(volname, "help") || !strcmp(volname, "help-xml")) &&
 1727         wordcount == 3) {
 1728         ret = dict_set_str(dict, volname, volname);
 1729         if (ret)
 1730             goto out;
 1731 
 1732     } else if (wordcount < 5) {
 1733         ret = -1;
 1734         goto out;
 1735 
 1736     } else if (wordcount == 5 && cli_is_key_spl((char *)words[3])) {
 1737         key = (char *)words[3];
 1738         value = (char *)words[4];
 1739         if (!key || !value) {
 1740             ret = -1;
 1741             goto out;
 1742         }
 1743 
 1744         ret = gf_strip_whitespace(value, strlen(value));
 1745         if (ret == -1)
 1746             goto out;
 1747 
 1748         if (strlen(value) == 0) {
 1749             ret = -1;
 1750             goto out;
 1751         }
 1752 
 1753         ret = cli_add_key_group(dict, key, value, op_errstr);
 1754         if (ret == 0)
 1755             *options = dict;
 1756         goto out;
 1757     }
 1758 
 1759     for (i = 3; i < wordcount; i += 2) {
 1760         key = (char *)words[i];
 1761         value = (char *)words[i + 1];
 1762 
 1763         if (!key || !value) {
 1764             ret = -1;
 1765             goto out;
 1766         }
 1767 
 1768         count++;
 1769 
 1770         if (fnmatch("user.*", key, FNM_NOESCAPE) != 0) {
 1771             ret = gf_strip_whitespace(value, strlen(value));
 1772             if (ret == -1)
 1773                 goto out;
 1774         }
 1775 
 1776         if (strlen(value) == 0) {
 1777             ret = -1;
 1778             goto out;
 1779         }
 1780 
 1781         if (cli_is_key_spl(key)) {
 1782             ret = -1;
 1783             goto out;
 1784         }
 1785 
 1786         sprintf(str, "key%d", count);
 1787         ret = dict_set_str(dict, str, key);
 1788         if (ret)
 1789             goto out;
 1790 
 1791         sprintf(str, "value%d", count);
 1792         ret = dict_set_str(dict, str, value);
 1793 
 1794         if (ret)
 1795             goto out;
 1796 
 1797         if ((!strcmp(key, "cluster.enable-shared-storage")) &&
 1798             (!strcmp(value, "disable"))) {
 1799             question =
 1800                 "Disabling cluster.enable-shared-storage "
 1801                 "will delete the shared storage volume"
 1802                 "(gluster_shared_storage), which is used "
 1803                 "by snapshot scheduler, geo-replication "
 1804                 "and NFS-Ganesha. Do you still want to "
 1805                 "continue?";
 1806             answer = cli_cmd_get_confirmation(state, question);
 1807             if (GF_ANSWER_NO == answer) {
 1808                 gf_log("cli", GF_LOG_ERROR,
 1809                        "Operation "
 1810                        "cancelled, exiting");
 1811                 *op_errstr = gf_strdup("Aborted by user.");
 1812                 ret = -1;
 1813                 goto out;
 1814             }
 1815         }
 1816         if ((!strcmp(key, "nfs.disable")) && (!strcmp(value, "off"))) {
 1817             question =
 1818                 "Gluster NFS is being deprecated in favor "
 1819                 "of NFS-Ganesha Enter \"yes\" to continue "
 1820                 "using Gluster NFS";
 1821             answer = cli_cmd_get_confirmation(state, question);
 1822             if (GF_ANSWER_NO == answer) {
 1823                 gf_log("cli", GF_LOG_ERROR,
 1824                        "Operation "
 1825                        "cancelled, exiting");
 1826                 *op_errstr = gf_strdup("Aborted by user.");
 1827                 ret = -1;
 1828                 goto out;
 1829             }
 1830         }
 1831     }
 1832 
 1833     ret = dict_set_int32(dict, "count", wordcount - 3);
 1834 
 1835     if (ret)
 1836         goto out;
 1837 
 1838     *options = dict;
 1839 
 1840 out:
 1841     if (ret && dict)
 1842         dict_unref(dict);
 1843 
 1844     return ret;
 1845 }
 1846 
 1847 int32_t
 1848 cli_cmd_volume_add_brick_parse(struct cli_state *state, const char **words,
 1849                                int wordcount, dict_t **options, int *ret_type)
 1850 {
 1851     dict_t *dict = NULL;
 1852     char *volname = NULL;
 1853     int ret = -1;
 1854     int brick_count = 0, brick_index = 0;
 1855     char *bricks = NULL;
 1856     static char *opwords_cl[] = {"replica", "stripe", NULL};
 1857     gf1_cluster_type type = GF_CLUSTER_TYPE_NONE;
 1858     int count = 1;
 1859     int arbiter_count = 0;
 1860     char *w = NULL;
 1861     int index;
 1862     gf_boolean_t is_force = _gf_false;
 1863     int wc = wordcount;
 1864     gf_answer_t answer = GF_ANSWER_NO;
 1865     const char *question = NULL;
 1866 
 1867     GF_ASSERT(words);
 1868     GF_ASSERT(options);
 1869 
 1870     dict = dict_new();
 1871 
 1872     if (!dict)
 1873         goto out;
 1874 
 1875     if (wordcount < 3)
 1876         goto out;
 1877 
 1878     volname = (char *)words[2];
 1879 
 1880     GF_ASSERT(volname);
 1881 
 1882     ret = dict_set_str(dict, "volname", volname);
 1883 
 1884     if (ret)
 1885         goto out;
 1886 
 1887     if (wordcount < 4) {
 1888         ret = -1;
 1889         goto out;
 1890     }
 1891     if (wordcount < 6) {
 1892         /* seems no options are given, go directly to the parse_brick */
 1893         brick_index = 3;
 1894         type = GF_CLUSTER_TYPE_NONE;
 1895         goto parse_bricks;
 1896     }
 1897 
 1898     w = str_getunamb(words[3], opwords_cl);
 1899     if (!w) {
 1900         type = GF_CLUSTER_TYPE_NONE;
 1901         index = 3;
 1902     } else if ((strcmp(w, "replica")) == 0) {
 1903         type = GF_CLUSTER_TYPE_REPLICATE;
 1904         count = strtol(words[4], NULL, 0);
 1905         if (!count || (count < 2)) {
 1906             cli_err("replica count should be greater than 1");
 1907             ret = -1;
 1908             goto out;
 1909         }
 1910         ret = dict_set_int32(dict, "replica-count", count);
 1911         if (ret)
 1912             goto out;
 1913         index = 5;
 1914         if (words[index] && !strcmp(words[index], "arbiter")) {
 1915             arbiter_count = strtol(words[6], NULL, 0);
 1916             if (arbiter_count != 1 || count != 3) {
 1917                 cli_err(
 1918                     "For arbiter configuration, replica "
 1919                     "count must be 3 and arbiter count "
 1920                     "must be 1. The 3rd brick of the "
 1921                     "replica will be the arbiter");
 1922                 ret = -1;
 1923                 goto out;
 1924             }
 1925             ret = dict_set_int32(dict, "arbiter-count", arbiter_count);
 1926             if (ret)
 1927                 goto out;
 1928             index = 7;
 1929         }
 1930 
 1931         if (count == 2) {
 1932             if (strcmp(words[wordcount - 1], "force")) {
 1933                 question =
 1934                     "Replica 2 volumes are prone to "
 1935                     "split-brain. Use Arbiter or "
 1936                     "Replica 3 to avoid this. See: "
 1937                     "http://docs.gluster.org/en/latest/Administrator%20Guide/"
 1938                     "Split%20brain%20and%20ways%20to%20deal%20with%20it/."
 1939                     "\nDo you still want to continue?\n";
 1940                 answer = cli_cmd_get_confirmation(state, question);
 1941                 if (GF_ANSWER_NO == answer) {
 1942                     gf_log("cli", GF_LOG_ERROR,
 1943                            "Add brick"
 1944                            " cancelled, exiting");
 1945                     ret = -1;
 1946                     goto out;
 1947                 }
 1948             }
 1949         }
 1950     } else if ((strcmp(w, "stripe")) == 0) {
 1951         cli_err("stripe option not supported");
 1952         goto out;
 1953     } else {
 1954         GF_ASSERT(!"opword mismatch");
 1955         ret = -1;
 1956         goto out;
 1957     }
 1958 
 1959     brick_index = index;
 1960 
 1961 parse_bricks:
 1962 
 1963     if (strcmp(words[wordcount - 1], "force") == 0) {
 1964         is_force = _gf_true;
 1965         wc = wordcount - 1;
 1966     }
 1967 
 1968     ret = cli_cmd_bricks_parse(words, wc, brick_index, &bricks, &brick_count);
 1969     if (ret)
 1970         goto out;
 1971 
 1972     ret = dict_set_dynstr(dict, "bricks", bricks);
 1973     if (ret)
 1974         goto out;
 1975 
 1976     ret = dict_set_int32(dict, "count", brick_count);
 1977 
 1978     if (ret)
 1979         goto out;
 1980 
 1981     ret = dict_set_int32(dict, "force", is_force);
 1982     if (ret)
 1983         goto out;
 1984 
 1985     *options = dict;
 1986 
 1987 out:
 1988     if (ret_type)
 1989         *ret_type = type;
 1990 
 1991     if (ret) {
 1992         gf_log("cli", GF_LOG_ERROR, "Unable to parse add-brick CLI");
 1993         if (dict)
 1994             dict_unref(dict);
 1995     }
 1996 
 1997     return ret;
 1998 }
 1999 
 2000 int32_t
 2001 cli_cmd_volume_remove_brick_parse(struct cli_state *state, const char **words,
 2002                                   int wordcount, dict_t **options,
 2003                                   int *question, int *brick_count,
 2004                                   int32_t *comm)
 2005 {
 2006     dict_t *dict = NULL;
 2007     char *volname = NULL;
 2008     char *delimiter = NULL;
 2009     int ret = -1;
 2010     char key[50];
 2011     int brick_index = 0;
 2012     int32_t tmp_index = 0;
 2013     int32_t j = 0;
 2014     char *tmp_brick = NULL;
 2015     char *tmp_brick1 = NULL;
 2016     static char *type_opword[] = {"replica", NULL};
 2017     static char *opwords[] = {"start",  "commit", "stop",
 2018                               "status", "force",  NULL};
 2019     char *w = NULL;
 2020     int32_t command = GF_OP_CMD_NONE;
 2021     long count = 0;
 2022     gf_answer_t answer = GF_ANSWER_NO;
 2023     const char *ques = NULL;
 2024 
 2025     GF_ASSERT(words);
 2026     GF_ASSERT(options);
 2027 
 2028     if (wordcount < 5)
 2029         goto out;
 2030 
 2031     dict = dict_new();
 2032     if (!dict)
 2033         goto out;
 2034 
 2035     volname = (char *)words[2];
 2036 
 2037     GF_ASSERT(volname);
 2038 
 2039     ret = dict_set_str(dict, "volname", volname);
 2040     if (ret)
 2041         goto out;
 2042 
 2043     brick_index = 3;
 2044     w = str_getunamb(words[3], type_opword);
 2045     if (w && !strcmp("replica", w)) {
 2046         if (wordcount < 6) {
 2047             ret = -1;
 2048             goto out;
 2049         }
 2050         count = strtol(words[4], NULL, 0);
 2051         if (count < 1) {
 2052             cli_err(
 2053                 "replica count should be greater than 0 in "
 2054                 "case of remove-brick");
 2055             ret = -1;
 2056             goto out;
 2057         }
 2058 
 2059         if (count == 2) {
 2060             if (strcmp(words[wordcount - 1], "force")) {
 2061                 ques =
 2062                     "Replica 2 volumes are prone to "
 2063                     "split-brain. Use Arbiter or Replica 3 "
 2064                     "to avoid this. See: "
 2065                     "http://docs.gluster.org/en/latest/Administrator%20Guide/"
 2066                     "Split%20brain%20and%20ways%20to%20deal%20with%20it/."
 2067                     "\nDo you still want to continue?\n";
 2068                 answer = cli_cmd_get_confirmation(state, ques);
 2069                 if (GF_ANSWER_NO == answer) {
 2070                     gf_log("cli", GF_LOG_ERROR,
 2071                            "Remove "
 2072                            "brick cancelled, exiting");
 2073                     ret = -1;
 2074                     goto out;
 2075                 }
 2076             }
 2077         }
 2078 
 2079         ret = dict_set_int32(dict, "replica-count", count);
 2080         if (ret)
 2081             goto out;
 2082         brick_index = 5;
 2083     } else if (w) {
 2084         GF_ASSERT(!"opword mismatch");
 2085     }
 2086 
 2087     w = str_getunamb(words[wordcount - 1], opwords);
 2088     if (!w) {
 2089         ret = -1;
 2090         goto out;
 2091     } else {
 2092         /* handled this option */
 2093         wordcount--;
 2094         if (!strcmp("start", w)) {
 2095             command = GF_OP_CMD_START;
 2096             if (question)
 2097                 *question = 1;
 2098         } else if (!strcmp("commit", w)) {
 2099             command = GF_OP_CMD_COMMIT;
 2100         } else if (!strcmp("stop", w)) {
 2101             command = GF_OP_CMD_STOP;
 2102         } else if (!strcmp("status", w)) {
 2103             command = GF_OP_CMD_STATUS;
 2104         } else if (!strcmp("force", w)) {
 2105             command = GF_OP_CMD_COMMIT_FORCE;
 2106             if (question)
 2107                 *question = 1;
 2108         } else {
 2109             GF_ASSERT(!"opword mismatch");
 2110             ret = -1;
 2111             goto out;
 2112         }
 2113     }
 2114 
 2115     ret = dict_set_int32(dict, "command", command);
 2116     if (ret)
 2117         gf_log("cli", GF_LOG_INFO, "failed to set 'command' %d", command);
 2118 
 2119     tmp_index = brick_index;
 2120     tmp_brick = GF_MALLOC(2048 * sizeof(*tmp_brick), gf_common_mt_char);
 2121 
 2122     if (!tmp_brick) {
 2123         gf_log("", GF_LOG_ERROR,
 2124                "cli_cmd_volume_remove_brick_parse: "
 2125                "Unable to get memory");
 2126         ret = -1;
 2127         goto out;
 2128     }
 2129 
 2130     tmp_brick1 = GF_MALLOC(2048 * sizeof(*tmp_brick1), gf_common_mt_char);
 2131 
 2132     if (!tmp_brick1) {
 2133         gf_log("", GF_LOG_ERROR,
 2134                "cli_cmd_volume_remove_brick_parse: "
 2135                "Unable to get memory");
 2136         ret = -1;
 2137         goto out;
 2138     }
 2139 
 2140     while (brick_index < wordcount) {
 2141         if (validate_brick_name((char *)words[brick_index])) {
 2142             cli_err(
 2143                 "wrong brick type: %s, use <HOSTNAME>:"
 2144                 "<export-dir-abs-path>",
 2145                 words[brick_index]);
 2146             ret = -1;
 2147             goto out;
 2148         } else {
 2149             delimiter = strrchr(words[brick_index], ':');
 2150             ret = gf_canonicalize_path(delimiter + 1);
 2151             if (ret)
 2152                 goto out;
 2153         }
 2154 
 2155         j = tmp_index;
 2156         strcpy(tmp_brick, words[brick_index]);
 2157         while (j < brick_index) {
 2158             strcpy(tmp_brick1, words[j]);
 2159             if (!(strcmp(tmp_brick, tmp_brick1))) {
 2160                 gf_log("", GF_LOG_ERROR,
 2161                        "Duplicate bricks"
 2162                        " found %s",
 2163                        words[brick_index]);
 2164                 cli_err("Duplicate bricks found %s", words[brick_index]);
 2165                 ret = -1;
 2166                 goto out;
 2167             }
 2168             j++;
 2169         }
 2170         snprintf(key, 50, "brick%d", ++(*brick_count));
 2171         ret = dict_set_str(dict, key, (char *)words[brick_index++]);
 2172 
 2173         if (ret)
 2174             goto out;
 2175     }
 2176 
 2177     if (command != GF_OP_CMD_STATUS && command != GF_OP_CMD_STOP) {
 2178         ret = dict_set_int32(dict, "count", *brick_count);
 2179         if (ret)
 2180             goto out;
 2181     }
 2182 
 2183     *options = dict;
 2184 
 2185 out:
 2186     if (ret) {
 2187         gf_log("cli", GF_LOG_ERROR, "Unable to parse remove-brick CLI");
 2188         if (dict)
 2189             dict_unref(dict);
 2190     }
 2191 
 2192     GF_FREE(tmp_brick);
 2193     GF_FREE(tmp_brick1);
 2194 
 2195     *comm = command;
 2196 
 2197     return ret;
 2198 }
 2199 
 2200 int32_t
 2201 cli_cmd_brick_op_validate_bricks(const char **words, dict_t *dict, int src,
 2202                                  int dst)
 2203 {
 2204     int ret = -1;
 2205     char *delimiter = NULL;
 2206 
 2207     if (validate_brick_name((char *)words[src])) {
 2208         cli_err(
 2209             "wrong brick type: %s, use "
 2210             "<HOSTNAME>:<export-dir-abs-path>",
 2211             words[3]);
 2212         ret = -1;
 2213         goto out;
 2214     } else {
 2215         delimiter = strrchr((char *)words[src], '/');
 2216         ret = gf_canonicalize_path(delimiter);
 2217         if (ret)
 2218             goto out;
 2219     }
 2220 
 2221     ret = dict_set_str(dict, "src-brick", (char *)words[src]);
 2222     if (ret)
 2223         goto out;
 2224 
 2225     if (dst == -1) {
 2226         ret = 0;
 2227         goto out;
 2228     }
 2229 
 2230     if (validate_brick_name((char *)words[dst])) {
 2231         cli_err(
 2232             "wrong brick type: %s, use "
 2233             "<HOSTNAME>:<export-dir-abs-path>",
 2234             words[dst]);
 2235         ret = -1;
 2236         goto out;
 2237     } else {
 2238         delimiter = strrchr((char *)words[dst], '/');
 2239         ret = gf_canonicalize_path(delimiter);
 2240         if (ret)
 2241             goto out;
 2242     }
 2243 
 2244     ret = dict_set_str(dict, "dst-brick", (char *)words[dst]);
 2245     if (ret)
 2246         goto out;
 2247     ret = 0;
 2248 out:
 2249     return ret;
 2250 }
 2251 
 2252 int32_t
 2253 cli_cmd_volume_reset_brick_parse(const char **words, int wordcount,
 2254                                  dict_t **options)
 2255 {
 2256     int ret = -1;
 2257     char *volname = NULL;
 2258     dict_t *dict = NULL;
 2259 
 2260     if (wordcount < 5 || wordcount > 7)
 2261         goto out;
 2262 
 2263     dict = dict_new();
 2264 
 2265     if (!dict)
 2266         goto out;
 2267 
 2268     volname = (char *)words[2];
 2269 
 2270     ret = dict_set_str(dict, "volname", volname);
 2271     if (ret)
 2272         goto out;
 2273 
 2274     if (wordcount == 5) {
 2275         if (strcmp(words[4], "start")) {
 2276             cli_err(
 2277                 "Invalid option '%s' for reset-brick. Please "
 2278                 "enter valid reset-brick command",
 2279                 words[4]);
 2280             ret = -1;
 2281             goto out;
 2282         }
 2283 
 2284         ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, -1);
 2285         if (ret)
 2286             goto out;
 2287 
 2288         ret = dict_set_str(dict, "operation", "GF_RESET_OP_START");
 2289         if (ret)
 2290             goto out;
 2291     } else if (wordcount == 6) {
 2292         if (strcmp(words[5], "commit")) {
 2293             cli_err(
 2294                 "Invalid option '%s' for reset-brick. Please "
 2295                 "enter valid reset-brick command",
 2296                 words[5]);
 2297             ret = -1;
 2298             goto out;
 2299         }
 2300 
 2301         ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, 4);
 2302         if (ret)
 2303             goto out;
 2304 
 2305         ret = dict_set_str(dict, "operation", "GF_RESET_OP_COMMIT");
 2306         if (ret)
 2307             goto out;
 2308     } else if (wordcount == 7) {
 2309         if (strcmp(words[5], "commit") || strcmp(words[6], "force")) {
 2310             cli_err(
 2311                 "Invalid option '%s %s' for reset-brick. Please "
 2312                 "enter valid reset-brick command",
 2313                 words[5], words[6]);
 2314             ret = -1;
 2315             goto out;
 2316         }
 2317 
 2318         ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, 4);
 2319         if (ret)
 2320             goto out;
 2321 
 2322         ret = dict_set_str(dict, "operation", "GF_RESET_OP_COMMIT_FORCE");
 2323         if (ret)
 2324             goto out;
 2325     }
 2326 
 2327     *options = dict;
 2328 
 2329 out:
 2330     if (ret) {
 2331         gf_log("cli", GF_LOG_ERROR, "Unable to parse reset-brick CLI");
 2332         if (dict)
 2333             dict_unref(dict);
 2334     }
 2335 
 2336     return ret;
 2337 }
 2338 
 2339 int32_t
 2340 cli_cmd_volume_replace_brick_parse(const char **words, int wordcount,
 2341                                    dict_t **options)
 2342 {
 2343     int ret = -1;
 2344     char *volname = NULL;
 2345     dict_t *dict = NULL;
 2346 
 2347     GF_ASSERT(words);
 2348     GF_ASSERT(options);
 2349 
 2350     if (wordcount != 7) {
 2351         ret = -1;
 2352         goto out;
 2353     }
 2354 
 2355     dict = dict_new();
 2356 
 2357     if (!dict) {
 2358         gf_log("cli", GF_LOG_ERROR, "Failed to allocate dictionary");
 2359         goto out;
 2360     }
 2361 
 2362     volname = (char *)words[2];
 2363 
 2364     GF_ASSERT(volname);
 2365 
 2366     ret = dict_set_str(dict, "volname", volname);
 2367     if (ret)
 2368         goto out;
 2369 
 2370     ret = cli_cmd_brick_op_validate_bricks(words, dict, 3, 4);
 2371     if (ret)
 2372         goto out;
 2373 
 2374     /* commit force option */
 2375     if (strcmp("commit", words[5]) || strcmp("force", words[6])) {
 2376         cli_err(
 2377             "Invalid option '%s' '%s' for replace-brick. Please "
 2378             "enter valid replace-brick command",
 2379             words[5], words[6]);
 2380         ret = -1;
 2381         goto out;
 2382     }
 2383 
 2384     ret = dict_set_str(dict, "operation", "GF_REPLACE_OP_COMMIT_FORCE");
 2385     if (ret)
 2386         goto out;
 2387 
 2388     *options = dict;
 2389 
 2390 out:
 2391     if (ret) {
 2392         gf_log("cli", GF_LOG_ERROR, "Unable to parse reset-brick CLI");
 2393         if (dict)
 2394             dict_unref(dict);
 2395     }
 2396 
 2397     return ret;
 2398 }
 2399 
 2400 int32_t
 2401 cli_cmd_log_filename_parse(const char **words, int wordcount, dict_t **options)
 2402 {
 2403     dict_t *dict = NULL;
 2404     char *volname = NULL;
 2405     char *str = NULL;
 2406     int ret = -1;
 2407     char *delimiter = NULL;
 2408 
 2409     GF_ASSERT(words);
 2410     GF_ASSERT(options);
 2411 
 2412     dict = dict_new();
 2413     if (!dict)
 2414         goto out;
 2415 
 2416     volname = (char *)words[3];
 2417     GF_ASSERT(volname);
 2418 
 2419     ret = dict_set_str(dict, "volname", volname);
 2420     if (ret)
 2421         goto out;
 2422 
 2423     str = (char *)words[4];
 2424     if (strchr(str, ':')) {
 2425         delimiter = strchr(words[4], ':');
 2426         if (!delimiter || delimiter == words[4] || *(delimiter + 1) != '/') {
 2427             cli_err(
 2428                 "wrong brick type: %s, use <HOSTNAME>:"
 2429                 "<export-dir-abs-path>",
 2430                 words[4]);
 2431             ret = -1;
 2432             goto out;
 2433         } else {
 2434             ret = gf_canonicalize_path(delimiter + 1);
 2435             if (ret)
 2436                 goto out;
 2437         }
 2438         ret = dict_set_str(dict, "brick", str);
 2439         if (ret)
 2440             goto out;
 2441         /* Path */
 2442         str = (char *)words[5];
 2443         ret = dict_set_str(dict, "path", str);
 2444         if (ret)
 2445             goto out;
 2446     } else {
 2447         ret = dict_set_str(dict, "path", str);
 2448         if (ret)
 2449             goto out;
 2450     }
 2451 
 2452     *options = dict;
 2453 
 2454 out:
 2455     if (ret && dict)
 2456         dict_unref(dict);
 2457 
 2458     return ret;
 2459 }
 2460 
 2461 int32_t
 2462 cli_cmd_log_level_parse(const char **words, int worcount, dict_t **options)
 2463 {
 2464     dict_t *dict = NULL;
 2465     int ret = -1;
 2466 
 2467     GF_ASSERT(words);
 2468     GF_ASSERT(options);
 2469 
 2470     /*
 2471      * loglevel command format:
 2472      *  > volume log level <VOL> <XLATOR[*]> <LOGLEVEL>
 2473      *  > volume log level colon-o posix WARNING
 2474      *  > volume log level colon-o replicate* DEBUG
 2475      *  > volume log level coon-o * TRACE
 2476      */
 2477 
 2478     GF_ASSERT((strncmp(words[0], "volume", 6) == 0));
 2479     GF_ASSERT((strncmp(words[1], "log", 3) == 0));
 2480     GF_ASSERT((strncmp(words[2], "level", 5) == 0));
 2481 
 2482     ret = glusterd_check_log_level(words[5]);
 2483     if (ret == -1) {
 2484         cli_err("Invalid log level [%s] specified", words[5]);
 2485         cli_err(
 2486             "Valid values for loglevel: (DEBUG|WARNING|ERROR"
 2487             "|CRITICAL|NONE|TRACE)");
 2488         goto out;
 2489     }
 2490 
 2491     dict = dict_new();
 2492     if (!dict)
 2493         goto out;
 2494 
 2495     GF_ASSERT(words[3]);
 2496     GF_ASSERT(words[4]);
 2497 
 2498     ret = dict_set_str(dict, "volname", (char *)words[3]);
 2499     if (ret)
 2500         goto out;
 2501 
 2502     ret = dict_set_str(dict, "xlator", (char *)words[4]);
 2503     if (ret)
 2504         goto out;
 2505 
 2506     ret = dict_set_str(dict, "loglevel", (char *)words[5]);
 2507     if (ret)
 2508         goto out;
 2509 
 2510     *options = dict;
 2511 
 2512 out:
 2513     if (ret && dict)
 2514         dict_unref(dict);
 2515 
 2516     return ret;
 2517 }
 2518 
 2519 int32_t
 2520 cli_cmd_log_locate_parse(const char **words, int wordcount, dict_t **options)
 2521 {
 2522     dict_t *dict = NULL;
 2523     char *volname = NULL;
 2524     char *str = NULL;
 2525     int ret = -1;
 2526     char *delimiter = NULL;
 2527 
 2528     GF_ASSERT(words);
 2529     GF_ASSERT(options);
 2530 
 2531     dict = dict_new();
 2532     if (!dict)
 2533         goto out;
 2534 
 2535     volname = (char *)words[3];
 2536     GF_ASSERT(volname);
 2537 
 2538     ret = dict_set_str(dict, "volname", volname);
 2539     if (ret)
 2540         goto out;
 2541 
 2542     if (words[4]) {
 2543         delimiter = strchr(words[4], ':');
 2544         if (!delimiter || delimiter == words[4] || *(delimiter + 1) != '/') {
 2545             cli_err(
 2546                 "wrong brick type: %s, use <HOSTNAME>:"
 2547                 "<export-dir-abs-path>",
 2548                 words[4]);
 2549             ret = -1;
 2550             goto out;
 2551         } else {
 2552             ret = gf_canonicalize_path(delimiter + 1);
 2553             if (ret)
 2554                 goto out;
 2555         }
 2556         str = (char *)words[4];
 2557         ret = dict_set_str(dict, "brick", str);
 2558         if (ret)
 2559             goto out;
 2560     }
 2561 
 2562     *options = dict;
 2563 
 2564 out:
 2565     if (ret && dict)
 2566         dict_unref(dict);
 2567 
 2568     return ret;
 2569 }
 2570 
 2571 int32_t
 2572 cli_cmd_log_rotate_parse(const char **words, int wordcount, dict_t **options)
 2573 {
 2574     dict_t *dict = NULL;
 2575     char *volname = NULL;
 2576     char *str = NULL;
 2577     int ret = -1;
 2578     char *delimiter = NULL;
 2579 
 2580     GF_ASSERT(words);
 2581     GF_ASSERT(options);
 2582 
 2583     dict = dict_new();
 2584     if (!dict)
 2585         goto out;
 2586 
 2587     if (strcmp("rotate", words[3]) == 0)
 2588         volname = (char *)words[2];
 2589     GF_ASSERT(volname);
 2590 
 2591     ret = dict_set_str(dict, "volname", volname);
 2592     if (ret)
 2593         goto out;
 2594 
 2595     if (words[4]) {
 2596         delimiter = strchr(words[4], ':');
 2597         if (!delimiter || delimiter == words[4] || *(delimiter + 1) != '/') {
 2598             cli_err(
 2599                 "wrong brick type: %s, use <HOSTNAME>:"
 2600                 "<export-dir-abs-path>",
 2601                 words[4]);
 2602             ret = -1;
 2603             goto out;
 2604         } else {
 2605             ret = gf_canonicalize_path(delimiter + 1);
 2606             if (ret)
 2607                 goto out;
 2608         }
 2609         str = (char *)words[4];
 2610         ret = dict_set_str(dict, "brick", str);
 2611         if (ret)
 2612             goto out;
 2613     }
 2614 
 2615     *options = dict;
 2616 
 2617 out:
 2618     if (ret && dict)
 2619         dict_unref(dict);
 2620 
 2621     return ret;
 2622 }
 2623 
 2624 static gf_boolean_t
 2625 gsyncd_url_check(const char *w)
 2626 {
 2627     return !!strpbrk(w, ":/");
 2628 }
 2629 
 2630 static gf_boolean_t
 2631 valid_slave_gsyncd_url(const char *w)
 2632 {
 2633     if (strstr(w, ":::"))
 2634         return _gf_false;
 2635     else if (strstr(w, "::"))
 2636         return _gf_true;
 2637     else
 2638         return _gf_false;
 2639 }
 2640 
 2641 static gf_boolean_t
 2642 gsyncd_glob_check(const char *w)
 2643 {
 2644     return !!strpbrk(w, "*?[");
 2645 }
 2646 
 2647 static int
 2648 config_parse(const char **words, int wordcount, dict_t *dict, unsigned cmdi,
 2649              unsigned glob)
 2650 {
 2651     int32_t ret = -1;
 2652     int32_t i = -1;
 2653     char *append_str = NULL;
 2654     size_t append_len = 0;
 2655     char *subop = NULL;
 2656     char *ret_chkpt = NULL;
 2657     struct tm checkpoint_time;
 2658     char chkpt_buf[20] = "";
 2659 
 2660     switch ((wordcount - 1) - cmdi) {
 2661         case 0:
 2662             subop = gf_strdup("get-all");
 2663             break;
 2664         case 1:
 2665             if (words[cmdi + 1][0] == '!') {
 2666                 (words[cmdi + 1])++;
 2667                 if (gf_asprintf(&subop, "del%s", glob ? "-glob" : "") == -1)
 2668                     subop = NULL;
 2669             } else
 2670                 subop = gf_strdup("get");
 2671 
 2672             ret = dict_set_str(dict, "op_name", ((char *)words[cmdi + 1]));
 2673             if (ret < 0)
 2674                 goto out;
 2675             break;
 2676         default:
 2677             if (gf_asprintf(&subop, "set%s", glob ? "-glob" : "") == -1)
 2678                 subop = NULL;
 2679 
 2680             ret = dict_set_str(dict, "op_name", ((char *)words[cmdi + 1]));
 2681             if (ret < 0)
 2682                 goto out;
 2683 
 2684             /* join the varargs by spaces to get the op_value */
 2685 
 2686             for (i = cmdi + 2; i < wordcount; i++)
 2687                 append_len += (strlen(words[i]) + 1);
 2688             /* trailing strcat will add two bytes, make space for that */
 2689             append_len++;
 2690 
 2691             /* strcat is used on this allocation and hence expected to be
 2692              * initiatlized to 0. So GF_CALLOC is used.
 2693              */
 2694             append_str = GF_CALLOC(1, append_len, cli_mt_append_str);
 2695             if (!append_str) {
 2696                 ret = -1;
 2697                 goto out;
 2698             }
 2699 
 2700             for (i = cmdi + 2; i < wordcount; i++) {
 2701                 strcat(append_str, words[i]);
 2702                 strcat(append_str, " ");
 2703             }
 2704             append_str[append_len - 2] = '\0';
 2705             /* "checkpoint now" is special: we resolve that "now" */
 2706             if ((strcmp(words[cmdi + 1], "checkpoint") == 0) &&
 2707                 (strcmp(append_str, "now") == 0)) {
 2708                 struct timeval tv = {
 2709                     0,
 2710                 };
 2711 
 2712                 ret = gettimeofday(&tv, NULL);
 2713                 if (ret == -1)
 2714                     goto out;
 2715 
 2716                 GF_FREE(append_str);
 2717                 append_str = GF_MALLOC(300, cli_mt_append_str);
 2718                 if (!append_str) {
 2719                     ret = -1;
 2720                     goto out;
 2721                 }
 2722                 snprintf(append_str, 300, "%" GF_PRI_SECOND, tv.tv_sec);
 2723             } else if ((strcmp(words[cmdi + 1], "checkpoint") == 0) &&
 2724                        (strcmp(append_str, "now") != 0)) {
 2725                 memset(&checkpoint_time, 0, sizeof(struct tm));
 2726                 ret_chkpt = strptime(append_str, "%Y-%m-%d %H:%M:%S",
 2727                                      &checkpoint_time);
 2728 
 2729                 if (ret_chkpt == NULL || *ret_chkpt != '\0') {
 2730                     ret = -1;
 2731                     cli_err(
 2732                         "Invalid Checkpoint label. Use format "
 2733                         "\"Y-m-d H:M:S\", Example: 2016-10-25 15:30:45");
 2734                     goto out;
 2735                 }
 2736                 GF_FREE(append_str);
 2737                 append_str = GF_MALLOC(300, cli_mt_append_str);
 2738                 if (!append_str) {
 2739                     ret = -1;
 2740                     goto out;
 2741                 }
 2742                 strftime(chkpt_buf, sizeof(chkpt_buf), "%s", &checkpoint_time);
 2743                 snprintf(append_str, 300, "%s", chkpt_buf);
 2744             }
 2745 
 2746             ret = dict_set_dynstr(dict, "op_value", append_str);
 2747             if (ret != 0) {
 2748                 goto out;
 2749             }
 2750             append_str = NULL;
 2751     }
 2752 
 2753     ret = -1;
 2754     if (subop) {
 2755         ret = dict_set_dynstr(dict, "subop", subop);
 2756         if (!ret)
 2757             subop = NULL;
 2758     }
 2759 
 2760 out:
 2761     GF_FREE(append_str);
 2762     GF_FREE(subop);
 2763 
 2764     gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret);
 2765     return ret;
 2766 }
 2767 
 2768 /* ssh_port_parse: Parses and validates when ssh_port is given.
 2769  *                 ssh_index refers to index of ssh_port and
 2770  *                 type refers to either push-pem or no-verify
 2771  */
 2772 
 2773 static int32_t
 2774 parse_ssh_port(const char **words, int wordcount, dict_t *dict, unsigned *cmdi,
 2775                int ssh_index, char *type)
 2776 {
 2777     int ret = 0;
 2778     char *end_ptr = NULL;
 2779     int64_t limit = 0;
 2780 
 2781     if (!strcmp((char *)words[ssh_index], "ssh-port")) {
 2782         if (strcmp((char *)words[ssh_index - 1], "create")) {
 2783             ret = -1;
 2784             goto out;
 2785         }
 2786         (*cmdi)++;
 2787         limit = strtol(words[ssh_index + 1], &end_ptr, 10);
 2788         if (errno == ERANGE || errno == EINVAL || limit <= 0 ||
 2789             strcmp(end_ptr, "") != 0) {
 2790             ret = -1;
 2791             cli_err("Please enter an integer value for ssh_port ");
 2792             goto out;
 2793         }
 2794 
 2795         ret = dict_set_int32(dict, "ssh_port", limit);
 2796         if (ret)
 2797             goto out;
 2798         (*cmdi)++;
 2799     } else if (strcmp((char *)words[ssh_index + 1], "create")) {
 2800         ret = -1;
 2801         goto out;
 2802     }
 2803 
 2804     ret = dict_set_int32(dict, type, 1);
 2805     if (ret)
 2806         goto out;
 2807     (*cmdi)++;
 2808 
 2809 out:
 2810     return ret;
 2811 }
 2812 
 2813 static int32_t
 2814 force_push_pem_no_verify_parse(const char **words, int wordcount, dict_t *dict,
 2815                                unsigned *cmdi)
 2816 {
 2817     int32_t ret = 0;
 2818 
 2819     if (!strcmp((char *)words[wordcount - 1], "force")) {
 2820         if ((strcmp((char *)words[wordcount - 2], "start")) &&
 2821             (strcmp((char *)words[wordcount - 2], "stop")) &&
 2822             (strcmp((char *)words[wordcount - 2], "create")) &&
 2823             (strcmp((char *)words[wordcount - 2], "no-verify")) &&
 2824             (strcmp((char *)words[wordcount - 2], "push-pem")) &&
 2825             (strcmp((char *)words[wordcount - 2], "pause")) &&
 2826             (strcmp((char *)words[wordcount - 2], "resume"))) {
 2827             ret = -1;
 2828             goto out;
 2829         }
 2830         ret = dict_set_int32n(dict, "force", SLEN("force"), 1);
 2831         if (ret)
 2832             goto out;
 2833         (*cmdi)++;
 2834 
 2835         if (!strcmp((char *)words[wordcount - 2], "push-pem")) {
 2836             ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 4,
 2837                                  "push_pem");
 2838             if (ret)
 2839                 goto out;
 2840         } else if (!strcmp((char *)words[wordcount - 2], "no-verify")) {
 2841             ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 4,
 2842                                  "no_verify");
 2843             if (ret)
 2844                 goto out;
 2845         }
 2846     } else if (!strcmp((char *)words[wordcount - 1], "push-pem")) {
 2847         ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 3,
 2848                              "push_pem");
 2849         if (ret)
 2850             goto out;
 2851     } else if (!strcmp((char *)words[wordcount - 1], "no-verify")) {
 2852         ret = parse_ssh_port(words, wordcount, dict, cmdi, wordcount - 3,
 2853                              "no_verify");
 2854         if (ret)
 2855             goto out;
 2856     }
 2857 
 2858 out:
 2859     gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret);
 2860     return ret;
 2861 }
 2862 
 2863 int32_t
 2864 cli_cmd_gsync_set_parse(struct cli_state *state, const char **words,
 2865                         int wordcount, dict_t **options, char **errstr)
 2866 {
 2867     int32_t ret = -1;
 2868     dict_t *dict = NULL;
 2869     gf1_cli_gsync_set type = GF_GSYNC_OPTION_TYPE_NONE;
 2870     int i = 0;
 2871     unsigned masteri = 0;
 2872     unsigned slavei = 0;
 2873     unsigned glob = 0;
 2874     unsigned cmdi = 0;
 2875     static char *opwords[] = {"create",    "status",   "start",  "stop",
 2876                               "config",    "force",    "delete", "ssh-port",
 2877                               "no-verify", "push-pem", "detail", "pause",
 2878                               "resume",    NULL};
 2879     char *w = NULL;
 2880     char *save_ptr = NULL;
 2881     char *slave_temp = NULL;
 2882     char *token = NULL;
 2883     gf_answer_t answer = GF_ANSWER_NO;
 2884     const char *question = NULL;
 2885 
 2886     GF_ASSERT(words);
 2887     GF_ASSERT(options);
 2888 
 2889     dict = dict_new();
 2890     if (!dict)
 2891         goto out;
 2892 
 2893     /* new syntax:
 2894      *
 2895      * volume geo-replication $m $s create [[ssh-port n] [[no-verify] |
 2896      * [push-pem]]] [force] volume geo-replication [$m [$s]] status [detail]
 2897      * volume geo-replication [$m] $s config [[!]$opt [$val]]
 2898      * volume geo-replication $m $s start|stop [force]
 2899      * volume geo-replication $m $s delete [reset-sync-time]
 2900      * volume geo-replication $m $s pause [force]
 2901      * volume geo-replication $m $s resume [force]
 2902      */
 2903 
 2904     if (wordcount < 3)
 2905         goto out;
 2906 
 2907     for (i = 2; i <= 3 && i < wordcount - 1; i++) {
 2908         if (gsyncd_glob_check(words[i]))
 2909             glob = i;
 2910         if (gsyncd_url_check(words[i])) {
 2911             slavei = i;
 2912             break;
 2913         }
 2914     }
 2915 
 2916     if (glob && !slavei)
 2917         /* glob is allowed only for config, thus it implies there is a
 2918          * slave argument; but that might have not been recognized on
 2919          * the first scan as it's url characteristics has been covered
 2920          * by the glob syntax.
 2921          *
 2922          * In this case, the slave is perforce the last glob-word -- the
 2923          * upcoming one is neither glob, nor url, so it's definitely not
 2924          * the slave.
 2925          */
 2926         slavei = glob;
 2927     if (slavei) {
 2928         cmdi = slavei + 1;
 2929         if (slavei == 3)
 2930             masteri = 2;
 2931     } else if (i <= 4) {
 2932         if (strtail("detail", (char *)words[wordcount - 1])) {
 2933             cmdi = wordcount - 2;
 2934             if (i == 4)
 2935                 masteri = 2;
 2936         } else {
 2937             /* no $s, can only be status cmd
 2938              * (with either a single $m before it or nothing)
 2939              * -- these conditions imply that i <= 3 after
 2940              * the iteration and that i is the successor of
 2941              * the (0 or 1 length) sequence of $m-s.
 2942              */
 2943             cmdi = i;
 2944             if (i == 3)
 2945                 masteri = 2;
 2946         }
 2947     } else
 2948         goto out;
 2949 
 2950     /* now check if input really complies syntax
 2951      * (in a somewhat redundant way, in favor
 2952      * transparent soundness)
 2953      */
 2954 
 2955     if (masteri && gsyncd_url_check(words[masteri]))
 2956         goto out;
 2957 
 2958     if (slavei && !glob && !valid_slave_gsyncd_url(words[slavei])) {
 2959         gf_asprintf(errstr, "Invalid slave url: %s", words[slavei]);
 2960         goto out;
 2961     }
 2962 
 2963     w = str_getunamb(words[cmdi], opwords);
 2964     if (!w)
 2965         goto out;
 2966 
 2967     if (strcmp(w, "create") == 0) {
 2968         type = GF_GSYNC_OPTION_TYPE_CREATE;
 2969 
 2970         if (!masteri || !slavei)
 2971             goto out;
 2972     } else if (strcmp(w, "status") == 0) {
 2973         type = GF_GSYNC_OPTION_TYPE_STATUS;
 2974 
 2975         if (slavei && !masteri)
 2976             goto out;
 2977     } else if (strcmp(w, "config") == 0) {
 2978         type = GF_GSYNC_OPTION_TYPE_CONFIG;
 2979 
 2980         if (!slavei)
 2981             goto out;
 2982     } else if (strcmp(w, "start") == 0) {
 2983         type = GF_GSYNC_OPTION_TYPE_START;
 2984 
 2985         if (!masteri || !slavei)
 2986             goto out;
 2987     } else if (strcmp(w, "stop") == 0) {
 2988         type = GF_GSYNC_OPTION_TYPE_STOP;
 2989 
 2990         if (!masteri || !slavei)
 2991             goto out;
 2992     } else if (strcmp(w, "delete") == 0) {
 2993         type = GF_GSYNC_OPTION_TYPE_DELETE;
 2994 
 2995         if (!masteri || !slavei)
 2996             goto out;
 2997     } else if (strcmp(w, "pause") == 0) {
 2998         type = GF_GSYNC_OPTION_TYPE_PAUSE;
 2999 
 3000         if (!masteri || !slavei)
 3001             goto out;
 3002     } else if (strcmp(w, "resume") == 0) {
 3003         type = GF_GSYNC_OPTION_TYPE_RESUME;
 3004 
 3005         if (!masteri || !slavei)
 3006             goto out;
 3007     } else
 3008         GF_ASSERT(!"opword mismatch");
 3009 
 3010     ret = force_push_pem_no_verify_parse(words, wordcount, dict, &cmdi);
 3011     if (ret)
 3012         goto out;
 3013 
 3014     if (strtail("detail", (char *)words[wordcount - 1])) {
 3015         if (!strtail("status", (char *)words[wordcount - 2])) {
 3016             ret = -1;
 3017             goto out;
 3018         }
 3019 
 3020         ret = dict_set_uint32(dict, "status-detail", _gf_true);
 3021         if (ret)
 3022             goto out;
 3023         cmdi++;
 3024     }
 3025 
 3026     if (type == GF_GSYNC_OPTION_TYPE_DELETE &&
 3027         !strcmp((char *)words[wordcount - 1], "reset-sync-time")) {
 3028         if (strcmp((char *)words[wordcount - 2], "delete")) {
 3029             ret = -1;
 3030             goto out;
 3031         }
 3032         ret = dict_set_uint32(dict, "reset-sync-time", _gf_true);
 3033         if (ret)
 3034             goto out;
 3035         cmdi++;
 3036     }
 3037 
 3038     if (type != GF_GSYNC_OPTION_TYPE_CONFIG && (cmdi < wordcount - 1 || glob)) {
 3039         ret = -1;
 3040         goto out;
 3041     }
 3042 
 3043     /* If got so far, input is valid, assemble the message */
 3044 
 3045     ret = 0;
 3046 
 3047     if (masteri) {
 3048         ret = dict_set_str(dict, "master", (char *)words[masteri]);
 3049         if (!ret)
 3050             ret = dict_set_str(dict, "volname", (char *)words[masteri]);
 3051     }
 3052     if (!ret && slavei) {
 3053         /* If geo-rep is created with root user using the syntax
 3054          * gluster vol geo-rep <mastervol> root@<slavehost> ...
 3055          * pass down only <slavehost> else pass as it is.
 3056          */
 3057         slave_temp = gf_strdup(words[slavei]);
 3058         if (slave_temp == NULL) {
 3059             ret = -1;
 3060             goto out;
 3061         }
 3062         token = strtok_r(slave_temp, "@", &save_ptr);
 3063         if (token && !strcmp(token, "root")) {
 3064             ret = dict_set_str(dict, "slave", (char *)words[slavei] + 5);
 3065         } else {
 3066             ret = dict_set_str(dict, "slave", (char *)words[slavei]);
 3067         }
 3068     }
 3069     if (!ret)
 3070         ret = dict_set_int32(dict, "type", type);
 3071     if (!ret && type == GF_GSYNC_OPTION_TYPE_CONFIG) {
 3072         if (!strcmp((char *)words[wordcount - 2], "ignore-deletes") &&
 3073             !strcmp((char *)words[wordcount - 1], "true")) {
 3074             question =
 3075                 "There exists ~15 seconds delay for the option to take"
 3076                 " effect from stime of the corresponding brick. Please"
 3077                 " check the log for the time, the option is effective."
 3078                 " Proceed";
 3079 
 3080             answer = cli_cmd_get_confirmation(state, question);
 3081 
 3082             if (GF_ANSWER_NO == answer) {
 3083                 gf_log("cli", GF_LOG_INFO,
 3084                        "Operation "
 3085                        "cancelled, exiting");
 3086                 *errstr = gf_strdup("Aborted by user.");
 3087                 ret = -1;
 3088                 goto out;
 3089             }
 3090         }
 3091 
 3092         ret = config_parse(words, wordcount, dict, cmdi, glob);
 3093     }
 3094 
 3095 out:
 3096     if (slave_temp)
 3097         GF_FREE(slave_temp);
 3098     if (ret && dict)
 3099         dict_unref(dict);
 3100     else
 3101         *options = dict;
 3102 
 3103     return ret;
 3104 }
 3105 
 3106 int32_t
 3107 cli_cmd_volume_profile_parse(const char **words, int wordcount,
 3108                              dict_t **options)
 3109 {
 3110     dict_t *dict = NULL;
 3111     char *volname = NULL;
 3112     int ret = -1;
 3113     gf1_cli_stats_op op = GF_CLI_STATS_NONE;
 3114     gf1_cli_info_op info_op = GF_CLI_INFO_NONE;
 3115     gf_boolean_t is_peek = _gf_false;
 3116 
 3117     static char *opwords[] = {"start", "stop", "info", NULL};
 3118     char *w = NULL;
 3119 
 3120     GF_ASSERT(words);
 3121     GF_ASSERT(options);
 3122 
 3123     dict = dict_new();
 3124     if (!dict)
 3125         goto out;
 3126 
 3127     if (wordcount < 4)
 3128         goto out;
 3129 
 3130     volname = (char *)words[2];
 3131 
 3132     ret = dict_set_str(dict, "volname", volname);
 3133     if (ret)
 3134         goto out;
 3135 
 3136     w = str_getunamb(words[3], opwords);
 3137     if (!w) {
 3138         ret = -1;
 3139         goto out;
 3140     }
 3141 
 3142     if ((strcmp(w, "start") == 0 || strcmp(w, "stop") == 0) && wordcount > 5) {
 3143         ret = -1;
 3144         goto out;
 3145     }
 3146 
 3147     if (strcmp(w, "info") == 0 && wordcount > 7) {
 3148         ret = -1;
 3149         goto out;
 3150     }
 3151 
 3152     if (strcmp(w, "start") == 0) {
 3153         op = GF_CLI_STATS_START;
 3154     } else if (strcmp(w, "stop") == 0) {
 3155         op = GF_CLI_STATS_STOP;
 3156     } else if (strcmp(w, "info") == 0) {
 3157         op = GF_CLI_STATS_INFO;
 3158         info_op = GF_CLI_INFO_ALL;
 3159         if (wordcount > 4) {
 3160             if (strcmp(words[4], "incremental") == 0) {
 3161                 info_op = GF_CLI_INFO_INCREMENTAL;
 3162                 if (wordcount > 5 && strcmp(words[5], "peek") == 0) {
 3163                     is_peek = _gf_true;
 3164                 }
 3165             } else if (strcmp(words[4], "cumulative") == 0) {
 3166                 info_op = GF_CLI_INFO_CUMULATIVE;
 3167             } else if (strcmp(words[4], "clear") == 0) {
 3168                 info_op = GF_CLI_INFO_CLEAR;
 3169             } else if (strcmp(words[4], "peek") == 0) {
 3170                 is_peek = _gf_true;
 3171             }
 3172         }
 3173     } else
 3174         GF_ASSERT(!"opword mismatch");
 3175 
 3176     ret = dict_set_int32(dict, "op", (int32_t)op);
 3177     if (ret)
 3178         goto out;
 3179 
 3180     ret = dict_set_int32(dict, "info-op", (int32_t)info_op);
 3181     if (ret)
 3182         goto out;
 3183 
 3184     ret = dict_set_int32(dict, "peek", is_peek);
 3185     if (ret)
 3186         goto out;
 3187 
 3188     if (!strcmp(words[wordcount - 1], "nfs")) {
 3189         ret = dict_set_int32(dict, "nfs", _gf_true);
 3190         if (ret)
 3191             goto out;
 3192     }
 3193 
 3194     *options = dict;
 3195 out:
 3196     if (ret && dict)
 3197         dict_unref(dict);
 3198     return ret;
 3199 }
 3200 
 3201 int32_t
 3202 cli_cmd_volume_top_parse(const char **words, int wordcount, dict_t **options)
 3203 {
 3204     dict_t *dict = NULL;
 3205     char *volname = NULL;
 3206     char *value = NULL;
 3207     char *key = NULL;
 3208     int ret = -1;
 3209     gf1_cli_stats_op op = GF_CLI_STATS_NONE;
 3210     gf1_cli_top_op top_op = GF_CLI_TOP_NONE;
 3211     int32_t list_cnt = -1;
 3212     int index = 0;
 3213     int perf = 0;
 3214     int32_t blk_size = 0;
 3215     int count = 0;
 3216     gf_boolean_t nfs = _gf_false;
 3217     char *delimiter = NULL;
 3218     static char *opwords[] = {"open",       "read",    "write",
 3219                               "opendir",    "readdir", "read-perf",
 3220                               "write-perf", "clear",   NULL};
 3221     char *w = NULL;
 3222 
 3223     GF_ASSERT(words);
 3224     GF_ASSERT(options);
 3225 
 3226     dict = dict_new();
 3227     if (!dict)
 3228         goto out;
 3229 
 3230     if (wordcount < 4)
 3231         goto out;
 3232 
 3233     volname = (char *)words[2];
 3234 
 3235     ret = dict_set_str(dict, "volname", volname);
 3236     if (ret)
 3237         goto out;
 3238 
 3239     op = GF_CLI_STATS_TOP;
 3240     ret = dict_set_int32(dict, "op", (int32_t)op);
 3241     if (ret)
 3242         goto out;
 3243 
 3244     w = str_getunamb(words[3], opwords);
 3245     if (!w) {
 3246         ret = -1;
 3247         goto out;
 3248     }
 3249     if (strcmp(w, "open") == 0) {
 3250         top_op = GF_CLI_TOP_OPEN;
 3251     } else if (strcmp(w, "read") == 0) {
 3252         top_op = GF_CLI_TOP_READ;
 3253     } else if (strcmp(w, "write") == 0) {
 3254         top_op = GF_CLI_TOP_WRITE;
 3255     } else if (strcmp(w, "opendir") == 0) {
 3256         top_op = GF_CLI_TOP_OPENDIR;
 3257     } else if (strcmp(w, "readdir") == 0) {
 3258         top_op = GF_CLI_TOP_READDIR;
 3259     } else if (strcmp(w, "read-perf") == 0) {
 3260         top_op = GF_CLI_TOP_READ_PERF;
 3261         perf = 1;
 3262     } else if (strcmp(w, "write-perf") == 0) {
 3263         top_op = GF_CLI_TOP_WRITE_PERF;
 3264         perf = 1;
 3265     } else if (strcmp(w, "clear") == 0) {
 3266         ret = dict_set_int32(dict, "clear-stats", 1);
 3267         if (ret) {
 3268             gf_log("cli", GF_LOG_ERROR, "Could not set clear-stats in dict");
 3269             goto out;
 3270         }
 3271     } else
 3272         GF_ASSERT(!"opword mismatch");
 3273     ret = dict_set_int32(dict, "top-op", (int32_t)top_op);
 3274     if (ret)
 3275         goto out;
 3276 
 3277     if ((wordcount > 4) && !strcmp(words[4], "nfs")) {
 3278         nfs = _gf_true;
 3279         ret = dict_set_int32(dict, "nfs", nfs);
 3280         if (ret)
 3281             goto out;
 3282         index = 5;
 3283     } else {
 3284         index = 4;
 3285     }
 3286 
 3287     for (; index < wordcount; index += 2) {
 3288         key = (char *)words[index];
 3289         value = (char *)words[index + 1];
 3290 
 3291         if (!key || !value) {
 3292             ret = -1;
 3293             goto out;
 3294         }
 3295         if (!strcmp(key, "brick")) {
 3296             delimiter = strchr(value, ':');
 3297             if (!delimiter || delimiter == value || *(delimiter + 1) != '/') {
 3298                 cli_err(
 3299                     "wrong brick type: %s, use <HOSTNAME>:"
 3300                     "<export-dir-abs-path>",
 3301                     value);
 3302                 ret = -1;
 3303                 goto out;
 3304             } else {
 3305                 ret = gf_canonicalize_path(delimiter + 1);
 3306                 if (ret)
 3307                     goto out;
 3308             }
 3309             ret = dict_set_str(dict, "brick", value);
 3310 
 3311         } else if (!strcmp(key, "list-cnt")) {
 3312             ret = gf_is_str_int(value);
 3313             if (!ret)
 3314                 list_cnt = atoi(value);
 3315             if (ret || (list_cnt < 0) || (list_cnt > 100)) {
 3316                 cli_err("list-cnt should be between 0 to 100");
 3317                 ret = -1;
 3318                 goto out;
 3319             }
 3320         } else if (perf && !nfs && !strcmp(key, "bs")) {
 3321             ret = gf_is_str_int(value);
 3322             if (!ret)
 3323                 blk_size = atoi(value);
 3324             if (ret || (blk_size <= 0)) {
 3325                 if (blk_size < 0)
 3326                     cli_err(
 3327                         "block size is an invalid"
 3328                         " number");
 3329                 else
 3330                     cli_err(
 3331                         "block size should be an "
 3332                         "integer greater than zero");
 3333                 ret = -1;
 3334                 goto out;
 3335             }
 3336             ret = dict_set_uint32(dict, "blk-size", (uint32_t)blk_size);
 3337         } else if (perf && !nfs && !strcmp(key, "count")) {
 3338             ret = gf_is_str_int(value);
 3339             if (!ret)
 3340                 count = atoi(value);
 3341             if (ret || (count <= 0)) {
 3342                 if (count < 0)
 3343                     cli_err("count is an invalid number");
 3344                 else
 3345                     cli_err(
 3346                         "count should be an integer "
 3347                         "greater than zero");
 3348 
 3349                 ret = -1;
 3350                 goto out;
 3351             }
 3352             ret = dict_set_uint32(dict, "blk-cnt", count);
 3353         } else {
 3354             ret = -1;
 3355             goto out;
 3356         }
 3357         if (ret) {
 3358             gf_log("", GF_LOG_WARNING,
 3359                    "Dict set failed for "
 3360                    "key %s",
 3361                    key);
 3362             goto out;
 3363         }
 3364     }
 3365     if (list_cnt == -1)
 3366         list_cnt = 100;
 3367     ret = dict_set_int32(dict, "list-cnt", list_cnt);
 3368     if (ret) {
 3369         gf_log("", GF_LOG_WARNING, "Dict set failed for list_cnt");
 3370         goto out;
 3371     }
 3372 
 3373     if ((blk_size > 0) ^ (count > 0)) {
 3374         cli_err("Need to give both 'bs' and 'count'");
 3375         ret = -1;
 3376         goto out;
 3377     } else if (((uint64_t)blk_size * count) > (10 * GF_UNIT_GB)) {
 3378         cli_err("'bs * count' value %" PRIu64
 3379                 " is greater than "
 3380                 "maximum allowed value of 10GB",
 3381                 ((uint64_t)blk_size * count));
 3382         ret = -1;
 3383         goto out;
 3384     }
 3385 
 3386     *options = dict;
 3387 out:
 3388     if (ret && dict)
 3389         dict_unref(dict);
 3390     return ret;
 3391 }
 3392 
 3393 uint32_t
 3394 cli_cmd_get_statusop(const char *arg)
 3395 {
 3396     int i = 0;
 3397     uint32_t ret = GF_CLI_STATUS_NONE;
 3398     char *w = NULL;
 3399     static char *opwords[] = {"detail",   "mem",   "clients",     "fd", "inode",
 3400                               "callpool", "tasks", "client-list", NULL};
 3401     static struct {
 3402         char *opname;
 3403         uint32_t opcode;
 3404     } optable[] = {{"detail", GF_CLI_STATUS_DETAIL},
 3405                    {"mem", GF_CLI_STATUS_MEM},
 3406                    {"clients", GF_CLI_STATUS_CLIENTS},
 3407                    {"fd", GF_CLI_STATUS_FD},
 3408                    {"inode", GF_CLI_STATUS_INODE},
 3409                    {"callpool", GF_CLI_STATUS_CALLPOOL},
 3410                    {"tasks", GF_CLI_STATUS_TASKS},
 3411                    {"client-list", GF_CLI_STATUS_CLIENT_LIST},
 3412                    {NULL}};
 3413 
 3414     w = str_getunamb(arg, opwords);
 3415     if (!w) {
 3416         gf_log("cli", GF_LOG_DEBUG, "Not a status op  %s", arg);
 3417         goto out;
 3418     }
 3419 
 3420     for (i = 0; optable[i].opname; i++) {
 3421         if (!strcmp(w, optable[i].opname)) {
 3422             ret = optable[i].opcode;
 3423             break;
 3424         }
 3425     }
 3426 
 3427 out:
 3428     return ret;
 3429 }
 3430 
 3431 int
 3432 cli_cmd_volume_status_parse(const char **words, int wordcount, dict_t **options)
 3433 {
 3434     dict_t *dict = NULL;
 3435     int ret = -1;
 3436     uint32_t cmd = 0;
 3437 
 3438     GF_ASSERT(options);
 3439 
 3440     dict = dict_new();
 3441     if (!dict)
 3442         goto out;
 3443 
 3444     switch (wordcount) {
 3445         case 2:
 3446             cmd = GF_CLI_STATUS_ALL;
 3447             ret = 0;
 3448             break;
 3449 
 3450         case 3:
 3451             if (!strcmp(words[2], "all")) {
 3452                 cmd = GF_CLI_STATUS_ALL;
 3453                 ret = 0;
 3454 
 3455             } else {
 3456                 cmd = GF_CLI_STATUS_VOL;
 3457                 ret = dict_set_str(dict, "volname", (char *)words[2]);
 3458             }
 3459 
 3460             break;
 3461 
 3462         case 4:
 3463             cmd = cli_cmd_get_statusop(words[3]);
 3464 
 3465             if (!strcmp(words[2], "all")) {
 3466                 if (cmd == GF_CLI_STATUS_NONE) {
 3467                     cli_err("%s is not a valid status option", words[3]);
 3468                     ret = -1;
 3469                     goto out;
 3470                 }
 3471                 cmd |= GF_CLI_STATUS_ALL;
 3472                 ret = 0;
 3473 
 3474             } else {
 3475                 ret = dict_set_str(dict, "volname", (char *)words[2]);
 3476                 if (ret)
 3477                     goto out;
 3478 
 3479                 if (cmd == GF_CLI_STATUS_NONE) {
 3480                     if (!strcmp(words[3], "nfs")) {
 3481                         cmd |= GF_CLI_STATUS_NFS;
 3482                     } else if (!strcmp(words[3], "shd")) {
 3483                         cmd |= GF_CLI_STATUS_SHD;
 3484                     } else if (!strcmp(words[3], "quotad")) {
 3485                         cmd |= GF_CLI_STATUS_QUOTAD;
 3486                     } else if (!strcmp(words[3], "snapd")) {
 3487                         cmd |= GF_CLI_STATUS_SNAPD;
 3488                     } else if (!strcmp(words[3], "bitd")) {
 3489                         cmd |= GF_CLI_STATUS_BITD;
 3490                     } else if (!strcmp(words[3], "scrub")) {
 3491                         cmd |= GF_CLI_STATUS_SCRUB;
 3492                     } else {
 3493                         cmd = GF_CLI_STATUS_BRICK;
 3494                         ret = dict_set_str(dict, "brick", (char *)words[3]);
 3495                     }
 3496 
 3497                 } else {
 3498                     cmd |= GF_CLI_STATUS_VOL;
 3499                     ret = 0;
 3500                 }
 3501             }
 3502 
 3503             break;
 3504 
 3505         case 5:
 3506             if (!strcmp(words[2], "all")) {
 3507                 cli_err("Cannot specify brick/nfs for \"all\"");
 3508                 ret = -1;
 3509                 goto out;
 3510             }
 3511 
 3512             cmd = cli_cmd_get_statusop(words[4]);
 3513             if (cmd == GF_CLI_STATUS_NONE) {
 3514                 cli_err("%s is not a valid status option", words[4]);
 3515                 ret = -1;
 3516                 goto out;
 3517             }
 3518 
 3519             ret = dict_set_str(dict, "volname", (char *)words[2]);
 3520             if (ret)
 3521                 goto out;
 3522 
 3523             if (!strcmp(words[3], "nfs")) {
 3524                 if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_DETAIL ||
 3525                     cmd == GF_CLI_STATUS_TASKS) {
 3526                     cli_err(
 3527                         "Detail/FD/Tasks status not available"
 3528                         " for NFS Servers");
 3529                     ret = -1;
 3530                     goto out;
 3531                 }
 3532                 cmd |= GF_CLI_STATUS_NFS;
 3533             } else if (!strcmp(words[3], "shd")) {
 3534                 if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS ||
 3535                     cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_TASKS) {
 3536                     cli_err(
 3537                         "Detail/FD/Clients/Tasks status not "
 3538                         "available for Self-heal Daemons");
 3539                     ret = -1;
 3540                     goto out;
 3541                 }
 3542                 cmd |= GF_CLI_STATUS_SHD;
 3543             } else if (!strcmp(words[3], "quotad")) {
 3544                 if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS ||
 3545                     cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_INODE) {
 3546                     cli_err(
 3547                         "Detail/FD/Clients/Inode status not "
 3548                         "available for Quota Daemon");
 3549                     ret = -1;
 3550                     goto out;
 3551                 }
 3552                 cmd |= GF_CLI_STATUS_QUOTAD;
 3553             } else if (!strcmp(words[3], "snapd")) {
 3554                 if (cmd == GF_CLI_STATUS_FD || cmd == GF_CLI_STATUS_CLIENTS ||
 3555                     cmd == GF_CLI_STATUS_DETAIL || cmd == GF_CLI_STATUS_INODE) {
 3556                     cli_err(
 3557                         "Detail/FD/Clients/Inode status not "
 3558                         "available for snap daemon");
 3559                     ret = -1;
 3560                     goto out;
 3561                 }
 3562                 cmd |= GF_CLI_STATUS_SNAPD;
 3563             } else {
 3564                 if (cmd == GF_CLI_STATUS_TASKS) {
 3565                     cli_err(
 3566                         "Tasks status not available for "
 3567                         "bricks");
 3568                     ret = -1;
 3569                     goto out;
 3570                 }
 3571                 cmd |= GF_CLI_STATUS_BRICK;
 3572                 ret = dict_set_str(dict, "brick", (char *)words[3]);
 3573             }
 3574             break;
 3575 
 3576         default:
 3577             goto out;
 3578     }
 3579 
 3580     if (ret)
 3581         goto out;
 3582 
 3583     ret = dict_set_int32(dict, "cmd", cmd);
 3584     if (ret)
 3585         goto out;
 3586 
 3587     *options = dict;
 3588 
 3589 out:
 3590     if (ret && dict)
 3591         dict_unref(dict);
 3592 
 3593     return ret;
 3594 }
 3595 
 3596 gf_boolean_t
 3597 cli_cmd_validate_dumpoption(const char *arg, char **option)
 3598 {
 3599     static char *opwords[] = {"all",   "nfs",    "mem",   "iobuf",   "callpool",
 3600                               "priv",  "fd",     "inode", "history", "inodectx",
 3601                               "fdctx", "quotad", NULL};
 3602     char *w = NULL;
 3603 
 3604     w = str_getunamb(arg, opwords);
 3605     if (!w) {
 3606         gf_log("cli", GF_LOG_DEBUG, "Unknown statedump option  %s", arg);
 3607         return _gf_false;
 3608     }
 3609     *option = w;
 3610     return _gf_true;
 3611 }
 3612 
 3613 int
 3614 cli_cmd_volume_statedump_options_parse(const char **words, int wordcount,
 3615                                        dict_t **options)
 3616 {
 3617     int ret = 0;
 3618     int i = 0;
 3619     dict_t *dict = NULL;
 3620     int option_cnt = 0;
 3621     char *option = NULL;
 3622     char *option_str = NULL;
 3623     char *tmp_str = NULL;
 3624     char *tmp = NULL;
 3625     char *ip_addr = NULL;
 3626     char *pid = NULL;
 3627 
 3628     if ((wordcount >= 5) && ((strcmp(words[3], "client")) == 0)) {
 3629         tmp = gf_strdup(words[4]);
 3630         if (!tmp) {
 3631             ret = -1;
 3632             goto out;
 3633         }
 3634         ip_addr = strtok(tmp, ":");
 3635         pid = strtok(NULL, ":");
 3636         if (valid_internet_address(ip_addr, _gf_true, _gf_false) && pid &&
 3637             gf_valid_pid(pid, strlen(pid))) {
 3638             ret = gf_asprintf(&option_str, "%s %s %s", words[3], ip_addr, pid);
 3639             if (ret < 0) {
 3640                 goto out;
 3641             }
 3642             option_cnt = 3;
 3643         } else {
 3644             ret = -1;
 3645             goto out;
 3646         }
 3647     } else {
 3648         for (i = 3; i < wordcount; i++, option_cnt++) {
 3649             if (!cli_cmd_validate_dumpoption(words[i], &option)) {
 3650                 ret = -1;
 3651                 goto out;
 3652             }
 3653             tmp_str = option_str;
 3654             option_str = NULL;
 3655             ret = gf_asprintf(&option_str, "%s%s ", tmp_str ? tmp_str : "",
 3656                               option);
 3657             GF_FREE(tmp_str);
 3658             if (ret < 0) {
 3659                 goto out;
 3660             }
 3661         }
 3662         if (option_str && (strstr(option_str, "nfs")) &&
 3663             strstr(option_str, "quotad")) {
 3664             ret = -1;
 3665             goto out;
 3666         }
 3667     }
 3668 
 3669     dict = dict_new();
 3670     if (!dict) {
 3671         ret = -1;
 3672         goto out;
 3673     }
 3674 
 3675     /* dynamic string in dict is freed up when dict is freed up, and hence
 3676     if option_str is NULL pass in an duplicate empty string to the same */
 3677     ret = dict_set_dynstr(dict, "options",
 3678                           (option_str ? option_str : gf_strdup("")));
 3679     if (ret)
 3680         goto out;
 3681     option_str = NULL;
 3682 
 3683     ret = dict_set_int32(dict, "option_cnt", option_cnt);
 3684     if (ret)
 3685         goto out;
 3686 
 3687     *options = dict;
 3688 out:
 3689     GF_FREE(tmp);
 3690     GF_FREE(option_str);
 3691     if (ret && dict)
 3692         dict_unref(dict);
 3693     if (ret)
 3694         gf_log("cli", GF_LOG_ERROR, "Error parsing dumpoptions");
 3695     return ret;
 3696 }
 3697 
 3698 int
 3699 cli_cmd_volume_clrlks_opts_parse(const char **words, int wordcount,
 3700                                  dict_t **options)
 3701 {
 3702     int ret = -1;
 3703     int i = 0;
 3704     dict_t *dict = NULL;
 3705     char *kind_opts[4] = {"blocked", "granted", "all", NULL};
 3706     char *types[4] = {"inode", "entry", "posix", NULL};
 3707     char *free_ptr = NULL;
 3708 
 3709     dict = dict_new();
 3710     if (!dict)
 3711         goto out;
 3712 
 3713     if (strcmp(words[4], "kind"))
 3714         goto out;
 3715 
 3716     for (i = 0; kind_opts[i]; i++) {
 3717         if (!strcmp(words[5], kind_opts[i])) {
 3718             free_ptr = gf_strdup(words[5]);
 3719             ret = dict_set_dynstr(dict, "kind", free_ptr);
 3720             if (ret)
 3721                 goto out;
 3722             free_ptr = NULL;
 3723             break;
 3724         }
 3725     }
 3726     if (i == 3)
 3727         goto out;
 3728 
 3729     ret = -1;
 3730     for (i = 0; types[i]; i++) {
 3731         if (!strcmp(words[6], types[i])) {
 3732             free_ptr = gf_strdup(words[6]);
 3733             ret = dict_set_dynstr(dict, "type", free_ptr);
 3734             if (ret)
 3735                 goto out;
 3736             free_ptr = NULL;
 3737             break;
 3738         }
 3739     }
 3740     if (i == 3)
 3741         goto out;
 3742 
 3743     if (wordcount == 8) {
 3744         free_ptr = gf_strdup(words[7]);
 3745         ret = dict_set_dynstr(dict, "opts", free_ptr);
 3746         if (ret)
 3747             goto out;
 3748         free_ptr = NULL;
 3749     }
 3750 
 3751     ret = 0;
 3752     *options = dict;
 3753 out:
 3754     if (ret) {
 3755         GF_FREE(free_ptr);
 3756         dict_unref(dict);
 3757     }
 3758 
 3759     return ret;
 3760 }
 3761 
 3762 static int
 3763 extract_hostname_path_from_token(const char *tmp_words, char **hostname,
 3764                                  char **path)
 3765 {
 3766     int ret = 0;
 3767     char *delimiter = NULL;
 3768     char *tmp_host = NULL;
 3769     char *host_name = NULL;
 3770     char *words = NULL;
 3771     int str_len = 0;
 3772     *hostname = NULL;
 3773     *path = NULL;
 3774 
 3775     str_len = strlen(tmp_words) + 1;
 3776     words = GF_MALLOC(str_len, gf_common_mt_char);
 3777     if (!words) {
 3778         ret = -1;
 3779         goto out;
 3780     }
 3781 
 3782     snprintf(words, str_len, "%s", tmp_words);
 3783 
 3784     if (validate_brick_name(words)) {
 3785         cli_err(
 3786             "Wrong brick type: %s, use <HOSTNAME>:"
 3787             "<export-dir-abs-path>",
 3788             words);
 3789         ret = -1;
 3790         goto out;
 3791     } else {
 3792         delimiter = strrchr(words, ':');
 3793         ret = gf_canonicalize_path(delimiter + 1);
 3794         if (ret) {
 3795             goto out;
 3796         } else {
 3797             str_len = strlen(delimiter + 1) + 1;
 3798             *path = GF_MALLOC(str_len, gf_common_mt_char);
 3799             if (!*path) {
 3800                 ret = -1;
 3801                 goto out;
 3802             }
 3803             snprintf(*path, str_len, "%s", delimiter + 1);
 3804         }
 3805     }
 3806 
 3807     tmp_host = gf_strdup(words);
 3808     if (!tmp_host) {
 3809         gf_log("cli", GF_LOG_ERROR, "Out of memory");
 3810         ret = -1;
 3811         goto out;
 3812     }
 3813     get_host_name(tmp_host, &host_name);
 3814     if (!host_name) {
 3815         ret = -1;
 3816         gf_log("cli", GF_LOG_ERROR,
 3817                "Unable to allocate "
 3818                "memory");
 3819         goto out;
 3820     }
 3821     if (!(strcmp(host_name, "localhost") && strcmp(host_name, "127.0.0.1") &&
 3822           strncmp(host_name, "0.", 2))) {
 3823         cli_err(
 3824             "Please provide a valid hostname/ip other "
 3825             "than localhost, 127.0.0.1 or loopback "
 3826             "address (0.0.0.0 to 0.255.255.255).");
 3827         ret = -1;
 3828         goto out;
 3829     }
 3830     if (!valid_internet_address(host_name, _gf_false, _gf_false)) {
 3831         cli_err(
 3832             "internet address '%s' does not conform to "
 3833             "standards",
 3834             host_name);
 3835         ret = -1;
 3836         goto out;
 3837     }
 3838 
 3839     str_len = strlen(host_name) + 1;
 3840     *hostname = GF_MALLOC(str_len, gf_common_mt_char);
 3841     if (!*hostname) {
 3842         ret = -1;
 3843         goto out;
 3844     }
 3845     snprintf(*hostname, str_len, "%s", host_name);
 3846     ret = 0;
 3847 
 3848 out:
 3849     GF_FREE(words);
 3850     GF_FREE(tmp_host);
 3851     return ret;
 3852 }
 3853 
 3854 static int
 3855 set_hostname_path_in_dict(const char *token, dict_t *dict, int heal_op)
 3856 {
 3857     char *hostname = NULL;
 3858     char *path = NULL;
 3859     int ret = 0;
 3860 
 3861     ret = extract_hostname_path_from_token(token, &hostname, &path);
 3862     if (ret)
 3863         goto out;
 3864 
 3865     switch (heal_op) {
 3866         case GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK:
 3867             ret = dict_set_dynstr(dict, "heal-source-hostname", hostname);
 3868             if (ret)
 3869                 goto out;
 3870             hostname = NULL;
 3871             ret = dict_set_dynstr(dict, "heal-source-brickpath", path);
 3872             if (ret) {
 3873                 goto out;
 3874             }
 3875             path = NULL;
 3876             break;
 3877         case GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA:
 3878             ret = dict_set_dynstr(dict, "per-replica-cmd-hostname", hostname);
 3879             if (ret)
 3880                 goto out;
 3881             hostname = NULL;
 3882             ret = dict_set_dynstr(dict, "per-replica-cmd-path", path);
 3883             if (ret) {
 3884                 goto out;
 3885             }
 3886             path = NULL;
 3887             break;
 3888         default:
 3889             ret = -1;
 3890             break;
 3891     }
 3892 
 3893 out:
 3894     GF_FREE(hostname);
 3895     GF_FREE(path);
 3896     return ret;
 3897 }
 3898 
 3899 static int
 3900 heal_command_type_get(const char *command)
 3901 {
 3902     int i = 0;
 3903     /* subcommands are set as NULL */
 3904     char *heal_cmds[GF_SHD_OP_HEAL_DISABLE + 1] = {
 3905         [GF_SHD_OP_INVALID] = NULL,
 3906         [GF_SHD_OP_HEAL_INDEX] = NULL,
 3907         [GF_SHD_OP_HEAL_FULL] = "full",
 3908         [GF_SHD_OP_INDEX_SUMMARY] = "info",
 3909         [GF_SHD_OP_SPLIT_BRAIN_FILES] = NULL,
 3910         [GF_SHD_OP_STATISTICS] = "statistics",
 3911         [GF_SHD_OP_STATISTICS_HEAL_COUNT] = NULL,
 3912         [GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA] = NULL,
 3913         [GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE] = NULL,
 3914         [GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK] = NULL,
 3915         [GF_SHD_OP_HEAL_ENABLE] = "enable",
 3916         [GF_SHD_OP_HEAL_DISABLE] = "disable",
 3917     };
 3918 
 3919     for (i = 0; i <= GF_SHD_OP_HEAL_DISABLE; i++) {
 3920         if (heal_cmds[i] && (strcmp(heal_cmds[i], command) == 0))
 3921             return i;
 3922     }
 3923 
 3924     return GF_SHD_OP_INVALID;
 3925 }
 3926 
 3927 int
 3928 cli_cmd_volume_heal_options_parse(const char **words, int wordcount,
 3929                                   dict_t **options)
 3930 {
 3931     int ret = 0;
 3932     dict_t *dict = NULL;
 3933     gf_xl_afr_op_t op = GF_SHD_OP_INVALID;
 3934 
 3935     dict = dict_new();
 3936     if (!dict) {
 3937         gf_log(THIS->name, GF_LOG_ERROR, "Failed to create the dict");
 3938         ret = -1;
 3939         goto out;
 3940     }
 3941 
 3942     ret = dict_set_str(dict, "volname", (char *)words[2]);
 3943     if (ret) {
 3944         gf_log(THIS->name, GF_LOG_ERROR, "failed to set volname");
 3945         goto out;
 3946     }
 3947 
 3948     if (wordcount == 3) {
 3949         ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_HEAL_INDEX);
 3950         goto done;
 3951     }
 3952 
 3953     if (wordcount == 4) {
 3954         op = heal_command_type_get(words[3]);
 3955         if (op == GF_SHD_OP_INVALID) {
 3956             ret = -1;
 3957             goto out;
 3958         }
 3959 
 3960         ret = dict_set_int32(dict, "heal-op", op);
 3961         goto done;
 3962     }
 3963 
 3964     if (wordcount == 5) {
 3965         if (strcmp(words[3], "info") && strcmp(words[3], "statistics") &&
 3966             strcmp(words[3], "granular-entry-heal")) {
 3967             ret = -1;
 3968             goto out;
 3969         }
 3970 
 3971         if (!strcmp(words[3], "info")) {
 3972             if (!strcmp(words[4], "split-brain")) {
 3973                 ret = dict_set_int32(dict, "heal-op",
 3974                                      GF_SHD_OP_SPLIT_BRAIN_FILES);
 3975                 goto done;
 3976             }
 3977             if (!strcmp(words[4], "summary")) {
 3978                 ret = dict_set_int32(dict, "heal-op", GF_SHD_OP_HEAL_SUMMARY);
 3979                 goto done;
 3980             }
 3981         }
 3982 
 3983         if (!strcmp(words[3], "statistics")) {
 3984             if (!strcmp(words[4], "heal-count")) {
 3985                 ret = dict_set_int32(dict, "heal-op",
 3986                                      GF_SHD_OP_STATISTICS_HEAL_COUNT);
 3987                 goto done;
 3988             }
 3989         }
 3990 
 3991         if (!strcmp(words[3], "granular-entry-heal")) {
 3992             if (!strcmp(words[4], "enable")) {
 3993                 ret = dict_set_int32(dict, "heal-op",
 3994                                      GF_SHD_OP_GRANULAR_ENTRY_HEAL_ENABLE);
 3995                 goto done;
 3996             } else if (!strcmp(words[4], "disable")) {
 3997                 ret = dict_set_int32(dict, "heal-op",
 3998                                      GF_SHD_OP_GRANULAR_ENTRY_HEAL_DISABLE);
 3999                 goto done;
 4000             }
 4001         }
 4002 
 4003         ret = -1;
 4004         goto out;
 4005     }
 4006     if (wordcount == 6) {
 4007         if (strcmp(words[3], "split-brain")) {
 4008             ret = -1;
 4009             goto out;
 4010         }
 4011         if (!strcmp(words[4], "bigger-file")) {
 4012             ret = dict_set_int32(dict, "heal-op",
 4013                                  GF_SHD_OP_SBRAIN_HEAL_FROM_BIGGER_FILE);
 4014             if (ret)
 4015                 goto out;
 4016             ret = dict_set_str(dict, "file", (char *)words[5]);
 4017             if (ret)
 4018                 goto out;
 4019             goto done;
 4020         }
 4021         if (!strcmp(words[4], "latest-mtime")) {
 4022             ret = dict_set_int32(dict, "heal-op",
 4023                                  GF_SHD_OP_SBRAIN_HEAL_FROM_LATEST_MTIME);
 4024             if (ret)
 4025                 goto out;
 4026             ret = dict_set_str(dict, "file", (char *)words[5]);
 4027             if (ret)
 4028                 goto out;
 4029             goto done;
 4030         }
 4031         if (!strcmp(words[4], "source-brick")) {
 4032             ret = dict_set_int32(dict, "heal-op",
 4033                                  GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK);
 4034             if (ret)
 4035                 goto out;
 4036             ret = set_hostname_path_in_dict(words[5], dict,
 4037                                             GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK);
 4038             if (ret)
 4039                 goto out;
 4040             goto done;
 4041         }
 4042         ret = -1;
 4043         goto out;
 4044     }
 4045     if (wordcount == 7) {
 4046         if (!strcmp(words[3], "statistics") &&
 4047             !strcmp(words[4], "heal-count") && !strcmp(words[5], "replica")) {
 4048             ret = dict_set_int32(dict, "heal-op",
 4049                                  GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA);
 4050             if (ret)
 4051                 goto out;
 4052             ret = set_hostname_path_in_dict(
 4053                 words[6], dict, GF_SHD_OP_STATISTICS_HEAL_COUNT_PER_REPLICA);
 4054             if (ret)
 4055                 goto out;
 4056             goto done;
 4057         }
 4058         if (!strcmp(words[3], "split-brain") &&
 4059             !strcmp(words[4], "source-brick")) {
 4060             ret = dict_set_int32(dict, "heal-op",
 4061                                  GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK);
 4062             ret = set_hostname_path_in_dict(words[5], dict,
 4063                                             GF_SHD_OP_SBRAIN_HEAL_FROM_BRICK);
 4064             if (ret)
 4065                 goto out;
 4066             ret = dict_set_str(dict, "file", (char *)words[6]);
 4067             if (ret)
 4068                 goto out;
 4069             goto done;
 4070         }
 4071     }
 4072     ret = -1;
 4073     goto out;
 4074 done:
 4075     *options = dict;
 4076 out:
 4077     if (ret && dict) {
 4078         dict_unref(dict);
 4079         *options = NULL;
 4080     }
 4081 
 4082     return ret;
 4083 }
 4084 
 4085 int
 4086 cli_cmd_volume_defrag_parse(const char **words, int wordcount, dict_t **options)
 4087 {
 4088     dict_t *dict = NULL;
 4089     int ret = -1;
 4090     char *option = NULL;
 4091     char *volname = NULL;
 4092     char *command = NULL;
 4093     gf_cli_defrag_type cmd = 0;
 4094 
 4095     GF_ASSERT(words);
 4096     GF_ASSERT(options);
 4097 
 4098     dict = dict_new();
 4099     if (!dict)
 4100         goto out;
 4101 
 4102     if (!((wordcount == 4) || (wordcount == 5)))
 4103         goto out;
 4104 
 4105     if (wordcount == 4) {
 4106         if (strcmp(words[3], "start") && strcmp(words[3], "stop") &&
 4107             strcmp(words[3], "status"))
 4108             goto out;
 4109     } else {
 4110         if (strcmp(words[3], "fix-layout") && strcmp(words[3], "start"))
 4111             goto out;
 4112     }
 4113 
 4114     volname = (char *)words[2];
 4115 
 4116     if (wordcount == 4) {
 4117         command = (char *)words[3];
 4118     }
 4119     if (wordcount == 5) {
 4120         if ((strcmp(words[3], "fix-layout") || strcmp(words[4], "start")) &&
 4121             (strcmp(words[3], "start") || strcmp(words[4], "force"))) {
 4122             ret = -1;
 4123             goto out;
 4124         }
 4125         command = (char *)words[3];
 4126         option = (char *)words[4];
 4127     }
 4128 
 4129     if (strcmp(command, "start") == 0) {
 4130         cmd = GF_DEFRAG_CMD_START;
 4131         if (option && strcmp(option, "force") == 0) {
 4132             cmd = GF_DEFRAG_CMD_START_FORCE;
 4133         }
 4134         goto done;
 4135     }
 4136 
 4137     if (strcmp(command, "fix-layout") == 0) {
 4138         cmd = GF_DEFRAG_CMD_START_LAYOUT_FIX;
 4139         goto done;
 4140     }
 4141     if (strcmp(command, "stop") == 0) {
 4142         cmd = GF_DEFRAG_CMD_STOP;
 4143         goto done;
 4144     }
 4145     if (strcmp(command, "status") == 0) {
 4146         cmd = GF_DEFRAG_CMD_STATUS;
 4147     }
 4148 
 4149 done:
 4150     ret = dict_set_str(dict, "volname", volname);
 4151 
 4152     if (ret) {
 4153         gf_log(THIS->name, GF_LOG_ERROR, "failed to set dict");
 4154         goto out;
 4155     }
 4156 
 4157     ret = dict_set_int32(dict, "rebalance-command", (int32_t)cmd);
 4158 
 4159     if (ret) {
 4160         gf_log(THIS->name, GF_LOG_ERROR, "failed to set dict");
 4161         goto out;
 4162     }
 4163 
 4164     *options = dict;
 4165 
 4166 out:
 4167     if (ret && dict)
 4168         dict_unref(dict);
 4169 
 4170     return ret;
 4171 }
 4172 
 4173 int32_t
 4174 cli_snap_create_desc_parse(dict_t *dict, const char **words, size_t wordcount,
 4175                            int32_t desc_opt_loc)
 4176 {
 4177     int32_t ret = -1;
 4178     char *desc = NULL;
 4179     int32_t desc_len = 0;
 4180     int len;
 4181 
 4182     desc = GF_MALLOC(MAX_SNAP_DESCRIPTION_LEN + 1, gf_common_mt_char);
 4183     if (!desc) {
 4184         ret = -1;
 4185         goto out;
 4186     }
 4187 
 4188     len = strlen(words[desc_opt_loc]);
 4189     if (len >= MAX_SNAP_DESCRIPTION_LEN) {
 4190         cli_out(
 4191             "snapshot create: description truncated: "
 4192             "Description provided is longer than 1024 characters");
 4193         desc_len = MAX_SNAP_DESCRIPTION_LEN;
 4194     } else {
 4195         desc_len = len;
 4196     }
 4197 
 4198     snprintf(desc, desc_len + 1, "%s", words[desc_opt_loc]);
 4199     /* Calculating the size of the description as given by the user */
 4200 
 4201     ret = dict_set_dynstr(dict, "description", desc);
 4202     if (ret) {
 4203         gf_log("cli", GF_LOG_ERROR,
 4204                "Unable to save snap "
 4205                "description");
 4206         goto out;
 4207     }
 4208 
 4209     ret = 0;
 4210 out:
 4211     if (ret && desc)
 4212         GF_FREE(desc);
 4213 
 4214     return ret;
 4215 }
 4216 
 4217 /* Function to check whether the Volume name is repeated */
 4218 int
 4219 cli_check_if_volname_repeated(const char **words, unsigned int start_index,
 4220                               uint64_t cur_index)
 4221 {
 4222     uint64_t i = -1;
 4223     int ret = 0;
 4224 
 4225     GF_ASSERT(words);
 4226 
 4227     for (i = start_index; i < cur_index; i++) {
 4228         if (strcmp(words[i], words[cur_index]) == 0) {
 4229             ret = -1;
 4230             goto out;
 4231         }
 4232     }
 4233 out:
 4234     return ret;
 4235 }
 4236 
 4237 /* snapshot clone <clonename> <snapname>
 4238  * @arg-0, dict     : Request Dictionary to be sent to server side.
 4239  * @arg-1, words    : Contains individual words of CLI command.
 4240  * @arg-2, wordcount: Contains number of words present in the CLI command.
 4241  *
 4242  * return value : -1 on failure
 4243  *                 0 on success
 4244  */
 4245 int
 4246 cli_snap_clone_parse(dict_t *dict, const char **words, int wordcount)
 4247 {
 4248     uint64_t i = 0;
 4249     int ret = -1;
 4250     char *clonename = NULL;
 4251     unsigned int cmdi = 2;
 4252     /* cmdi is command index, here cmdi is "2" (gluster snapshot clone)*/
 4253 
 4254     GF_ASSERT(words);
 4255     GF_ASSERT(dict);
 4256 
 4257     if (wordcount == cmdi + 1) {
 4258         cli_err("Invalid Syntax.");
 4259         gf_log("cli", GF_LOG_ERROR,
 4260                "Invalid number of  words for snap clone command");
 4261         goto out;
 4262     }
 4263 
 4264     if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) {
 4265         cli_err(
 4266             "snapshot clone: failed: clonename cannot exceed "
 4267             "255 characters.");
 4268         gf_log("cli", GF_LOG_ERROR, "Clone name too long");
 4269 
 4270         goto out;
 4271     }
 4272 
 4273     clonename = (char *)words[cmdi];
 4274     for (i = 0; i < strlen(clonename); i++) {
 4275         /* Following volume name convention */
 4276         if (!isalnum(clonename[i]) &&
 4277             (clonename[i] != '_' && (clonename[i] != '-'))) {
 4278             /* TODO : Is this message enough?? */
 4279             cli_err(
 4280                 "Clonename can contain only alphanumeric, "
 4281                 "\"-\" and \"_\" characters");
 4282             goto out;
 4283         }
 4284     }
 4285 
 4286     ret = dict_set_int32(dict, "volcount", 1);
 4287     if (ret) {
 4288         gf_log("cli", GF_LOG_ERROR, "Could not save volcount");
 4289         goto out;
 4290     }
 4291 
 4292     ret = dict_set_str(dict, "clonename", (char *)words[cmdi]);
 4293     if (ret) {
 4294         gf_log("cli", GF_LOG_ERROR,
 4295                "Could not save clone "
 4296                "name(%s)",
 4297                (char *)words[cmdi]);
 4298         goto out;
 4299     }
 4300 
 4301     /* Filling snap name in the dictionary */
 4302     ret = dict_set_str(dict, "snapname", (char *)words[cmdi + 1]);
 4303     if (ret) {
 4304         gf_log("cli", GF_LOG_ERROR,
 4305                "Could not "
 4306                "save snap name(%s)",
 4307                (char *)words[cmdi + 1]);
 4308         goto out;
 4309     }
 4310 
 4311     ret = 0;
 4312 
 4313 out:
 4314     return ret;
 4315 }
 4316 
 4317 /* snapshot create <snapname> <vol-name(s)> [description <description>]
 4318  *                                           [force]
 4319  * @arg-0, dict     : Request Dictionary to be sent to server side.
 4320  * @arg-1, words    : Contains individual words of CLI command.
 4321  * @arg-2, wordcount: Contains number of words present in the CLI command.
 4322  *
 4323  * return value : -1 on failure
 4324  *                 0 on success
 4325  */
 4326 int
 4327 cli_snap_create_parse(dict_t *dict, const char **words, int wordcount)
 4328 {
 4329     uint64_t i = 0;
 4330     int ret = -1;
 4331     uint64_t volcount = 0;
 4332     char key[PATH_MAX] = "";
 4333     char *snapname = NULL;
 4334     unsigned int cmdi = 2;
 4335     int flags = 0;
 4336     /* cmdi is command index, here cmdi is "2" (gluster snapshot create)*/
 4337 
 4338     GF_ASSERT(words);
 4339     GF_ASSERT(dict);
 4340 
 4341     if (wordcount <= cmdi + 1) {
 4342         cli_err("Invalid Syntax.");
 4343         gf_log("cli", GF_LOG_ERROR, "Too less words for snap create command");
 4344         goto out;
 4345     }
 4346 
 4347     if (strlen(words[cmdi]) >= GLUSTERD_MAX_SNAP_NAME) {
 4348         cli_err(
 4349             "snapshot create: failed: snapname cannot exceed "
 4350             "255 characters.");
 4351         gf_log("cli", GF_LOG_ERROR, "Snapname too long");
 4352 
 4353         goto out;
 4354     }
 4355 
 4356     snapname = (char *)words[cmdi];
 4357     for (i = 0; i < strlen(snapname); i++) {
 4358         /* Following volume name convention */
 4359         if (!isalnum(snapname[i]) &&
 4360             (snapname[i] != '_' && (snapname[i] != '-'))) {
 4361             /* TODO : Is this message enough?? */
 4362             cli_err(
 4363                 "Snapname can contain only alphanumeric, "
 4364                 "\"-\" and \"_\" characters");
 4365             goto out;
 4366         }
 4367     }
 4368 
 4369     ret = dict_set_str(dict, "snapname", (char *)words[cmdi]);
 4370     if (ret) {
 4371         gf_log("cli", GF_LOG_ERROR,
 4372                "Could not save snap "
 4373                "name(%s)",
 4374                (char *)words[cmdi]);
 4375         goto out;
 4376     }
 4377 
 4378     /* Filling volume name in the dictionary */
 4379     for (i = cmdi + 1;
 4380          i < wordcount && (strcmp(words[i], "description")) != 0 &&
 4381          (strcmp(words[i], "force") != 0) &&
 4382          (strcmp(words[i], "no-timestamp") != 0);
 4383          i++) {
 4384         volcount++;
 4385         /* volume index starts from 1 */
 4386         ret = snprintf(key, sizeof(key), "volname%" PRIu64, volcount);
 4387         if (ret < 0) {
 4388             goto out;
 4389         }
 4390 
 4391         ret = dict_set_str(dict, key, (char *)words[i]);
 4392         if (ret) {
 4393             gf_log("cli", GF_LOG_ERROR,
 4394                    "Could not "
 4395                    "save volume name(%s)",
 4396                    (char *)words[i]);
 4397             goto out;
 4398         }
 4399 
 4400         if (i >= cmdi + 2) {
 4401             ret = -1;
 4402             cli_err(
 4403                 "Creating multiple volume snapshot is not "
 4404                 "supported as of now");
 4405             goto out;
 4406         }
 4407         /* TODO : remove this above condition check once
 4408          * multiple volume snapshot is supported */
 4409     }
 4410 
 4411     if (volcount == 0) {
 4412         ret = -1;
 4413         cli_err("Please provide the volume name");
 4414         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4415         goto out;
 4416     }
 4417 
 4418     ret = dict_set_int32(dict, "volcount", volcount);
 4419     if (ret) {
 4420         gf_log("cli", GF_LOG_ERROR, "Could not save volcount");
 4421         goto out;
 4422     }
 4423 
 4424     /* Verify how we got out of "for" loop,
 4425      * if it is by reaching wordcount limit then goto "out",
 4426      * because we need not parse for "description","force" and
 4427      * "no-timestamp" after this.
 4428      */
 4429     if (i == wordcount) {
 4430         goto out;
 4431     }
 4432 
 4433     if (strcmp(words[i], "no-timestamp") == 0) {
 4434         ret = dict_set_int32n(dict, "no-timestamp", SLEN("no-timestamp"), 1);
 4435         if (ret) {
 4436             gf_log("cli", GF_LOG_ERROR,
 4437                    "Could not save "
 4438                    "time-stamp option");
 4439         }
 4440         if (i == (wordcount - 1))
 4441             goto out;
 4442         i++;
 4443     }
 4444 
 4445     if ((strcmp(words[i], "description")) == 0) {
 4446         ++i;
 4447         if (i > (wordcount - 1)) {
 4448             ret = -1;
 4449             cli_err("Please provide a description");
 4450             gf_log("cli", GF_LOG_ERROR, "Description not provided");
 4451             goto out;
 4452         }
 4453 
 4454         ret = cli_snap_create_desc_parse(dict, words, wordcount, i);
 4455         if (ret) {
 4456             gf_log("cli", GF_LOG_ERROR,
 4457                    "Could not save snap "
 4458                    "description");
 4459             goto out;
 4460         }
 4461 
 4462         if (i == (wordcount - 1))
 4463             goto out;
 4464         i++;
 4465         /* point the index to next word.
 4466          * As description might be follwed by force option.
 4467          * Before that, check if wordcount limit is reached
 4468          */
 4469     }
 4470 
 4471     if (strcmp(words[i], "force") == 0) {
 4472         flags = GF_CLI_FLAG_OP_FORCE;
 4473 
 4474     } else {
 4475         ret = -1;
 4476         cli_err("Invalid Syntax.");
 4477         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4478         goto out;
 4479     }
 4480 
 4481     /* Check if the command has anything after "force" keyword */
 4482     if (++i < wordcount) {
 4483         ret = -1;
 4484         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4485         goto out;
 4486     }
 4487 
 4488     ret = 0;
 4489 
 4490 out:
 4491     if (ret == 0) {
 4492         /*Adding force flag in either of the case i.e force set
 4493          * or unset*/
 4494         ret = dict_set_int32(dict, "flags", flags);
 4495         if (ret) {
 4496             gf_log("cli", GF_LOG_ERROR,
 4497                    "Could not save "
 4498                    "snap force option");
 4499         }
 4500     }
 4501     return ret;
 4502 }
 4503 
 4504 /* snapshot list [volname]
 4505  * @arg-0, dict     : Request Dictionary to be sent to server side.
 4506  * @arg-1, words    : Contains individual words of CLI command.
 4507  * @arg-2, wordcount: Contains number of words present in the CLI command.
 4508  *
 4509  * return value : -1 on failure
 4510  *                 0 on success
 4511  */
 4512 int
 4513 cli_snap_list_parse(dict_t *dict, const char **words, int wordcount)
 4514 {
 4515     int ret = -1;
 4516 
 4517     GF_ASSERT(words);
 4518     GF_ASSERT(dict);
 4519 
 4520     if (wordcount < 2 || wordcount > 3) {
 4521         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4522         goto out;
 4523     }
 4524 
 4525     if (wordcount == 2) {
 4526         ret = 0;
 4527         goto out;
 4528     }
 4529 
 4530     ret = dict_set_str(dict, "volname", (char *)words[2]);
 4531     if (ret) {
 4532         gf_log("cli", GF_LOG_ERROR, "Failed to save volname in dictionary");
 4533         goto out;
 4534     }
 4535 out:
 4536     return ret;
 4537 }
 4538 
 4539 /* snapshot info [(snapname |  volume <volname>)]
 4540  * @arg-0, dict     : Request Dictionary to be sent to server side.
 4541  * @arg-1, words    : Contains individual words of CLI command.
 4542  * @arg-2, wordcount: Contains number of words present in the CLI command.
 4543  *
 4544  * return value : -1 on failure
 4545  *                 0 on success
 4546  */
 4547 int
 4548 cli_snap_info_parse(dict_t *dict, const char **words, int wordcount)
 4549 {
 4550     int ret = -1;
 4551     int32_t cmd = GF_SNAP_INFO_TYPE_ALL;
 4552     unsigned int cmdi = 2;
 4553     /* cmdi is command index, here cmdi is "2" (gluster snapshot info)*/
 4554 
 4555     GF_ASSERT(words);
 4556     GF_ASSERT(dict);
 4557 
 4558     if (wordcount > 4 || wordcount < cmdi) {
 4559         gf_log("cli", GF_LOG_ERROR, "Invalid syntax");
 4560         goto out;
 4561     }
 4562 
 4563     if (wordcount == cmdi) {
 4564         ret = 0;
 4565         goto out;
 4566     }
 4567 
 4568     /* If 3rd word is not "volume", then it must
 4569      * be snapname.
 4570      */
 4571     if (strcmp(words[cmdi], "volume") != 0) {
 4572         ret = dict_set_str(dict, "snapname", (char *)words[cmdi]);
 4573         if (ret) {
 4574             gf_log("cli", GF_LOG_ERROR,
 4575                    "Unable to save "
 4576                    "snapname %s",
 4577                    words[cmdi]);
 4578             goto out;
 4579         }
 4580 
 4581         /* Once snap name is parsed, if we encounter any other
 4582          * word then fail it. Invalid Syntax.
 4583          * example : snapshot info <snapname> word
 4584          */
 4585         if ((cmdi + 1) != wordcount) {
 4586             ret = -1;
 4587             gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4588             goto out;
 4589         }
 4590 
 4591         cmd = GF_SNAP_INFO_TYPE_SNAP;
 4592         ret = 0;
 4593         goto out;
 4594         /* No need to continue the parsing once we
 4595          * get the snapname
 4596          */
 4597     }
 4598 
 4599     /* If 3rd word is "volume", then check if next word
 4600      * is present. As, "snapshot info volume" is an
 4601      * invalid command.
 4602      */
 4603     if ((cmdi + 1) == wordcount) {
 4604         ret = -1;
 4605         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4606         goto out;
 4607     }
 4608 
 4609     ret = dict_set_str(dict, "volname", (char *)words[wordcount - 1]);
 4610     if (ret) {
 4611         gf_log("cli", GF_LOG_ERROR,
 4612                "Could not save "
 4613                "volume name %s",
 4614                words[wordcount - 1]);
 4615         goto out;
 4616     }
 4617     cmd = GF_SNAP_INFO_TYPE_VOL;
 4618 out:
 4619     if (ret == 0) {
 4620         ret = dict_set_int32(dict, "sub-cmd", cmd);
 4621         if (ret) {
 4622             gf_log("cli", GF_LOG_ERROR,
 4623                    "Could not save "
 4624                    "type of snapshot info");
 4625         }
 4626     }
 4627     return ret;
 4628 }
 4629 
 4630 /* snapshot restore <snapname>
 4631  * @arg-0, dict     : Request Dictionary to be sent to server side.
 4632  * @arg-1, words    : Contains individual words of CLI command.
 4633  * @arg-2, wordcount: Contains number of words present in the CLI command.
 4634  *
 4635  * return value : -1 on failure
 4636  *                 0 on success
 4637  */
 4638 int
 4639 cli_snap_restore_parse(dict_t *dict, const char **words, int wordcount,
 4640                        struct cli_state *state)
 4641 {
 4642     int ret = -1;
 4643     const char *question = NULL;
 4644     gf_answer_t answer = GF_ANSWER_NO;
 4645 
 4646     GF_ASSERT(words);
 4647     GF_ASSERT(dict);
 4648 
 4649     if (wordcount != 3) {
 4650         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4651         goto out;
 4652     }
 4653 
 4654     ret = dict_set_str(dict, "snapname", (char *)words[2]);
 4655     if (ret) {
 4656         gf_log("cli", GF_LOG_ERROR, "Unable to save snap-name %s", words[2]);
 4657         goto out;
 4658     }
 4659 
 4660     question =
 4661         "Restore operation will replace the "
 4662         "original volume with the snapshotted volume. "
 4663         "Do you still want to continue?";
 4664 
 4665     answer = cli_cmd_get_confirmation(state, question);
 4666     if (GF_ANSWER_NO == answer) {
 4667         ret = 1;
 4668         gf_log("cli", GF_LOG_ERROR,
 4669                "User cancelled a snapshot "
 4670                "restore operation for snap %s",
 4671                (char *)words[2]);
 4672         goto out;
 4673     }
 4674 out:
 4675     return ret;
 4676 }
 4677 
 4678 /* snapshot activate <snapname> [force]
 4679  * @arg-0, dict     : Request Dictionary to be sent to server side.
 4680  * @arg-1, words    : Contains individual words of CLI command.
 4681  * @arg-2, wordcount: Contains number of words present in the CLI command.
 4682  *
 4683  * return value : -1 on failure
 4684  *                 0 on success
 4685  */
 4686 int
 4687 cli_snap_activate_parse(dict_t *dict, const char **words, int wordcount)
 4688 {
 4689     int ret = -1;
 4690     int flags = 0;
 4691 
 4692     GF_ASSERT(words);
 4693     GF_ASSERT(dict);
 4694 
 4695     if ((wordcount < 3) || (wordcount > 4)) {
 4696         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4697         goto out;
 4698     }
 4699 
 4700     ret = dict_set_str(dict, "snapname", (char *)words[2]);
 4701     if (ret) {
 4702         gf_log("cli", GF_LOG_ERROR, "Unable to save snap-name %s", words[2]);
 4703         goto out;
 4704     }
 4705 
 4706     if (wordcount == 4) {
 4707         if (!strcmp("force", (char *)words[3])) {
 4708             flags = GF_CLI_FLAG_OP_FORCE;
 4709         } else {
 4710             gf_log("cli", GF_LOG_ERROR, "Invalid option");
 4711             ret = -1;
 4712             goto out;
 4713         }
 4714     }
 4715     ret = dict_set_int32(dict, "flags", flags);
 4716     if (ret) {
 4717         gf_log("cli", GF_LOG_ERROR, "Unable to save force option");
 4718         goto out;
 4719     }
 4720 out:
 4721     return ret;
 4722 }
 4723 
 4724 /* snapshot deactivate <snapname>
 4725  * @arg-0, dict     : Request Dictionary to be sent to server side.
 4726  * @arg-1, words    : Contains individual words of CLI command.
 4727  * @arg-2, wordcount: Contains number of words present in the CLI command.
 4728  *
 4729  * return value : -1 on failure
 4730  *                 0 on success
 4731  *                 1 if user cancelled the request
 4732  */
 4733 int
 4734 cli_snap_deactivate_parse(dict_t *dict, const char **words, int wordcount,
 4735                           struct cli_state *state)
 4736 {
 4737     int ret = -1;
 4738     gf_answer_t answer = GF_ANSWER_NO;
 4739     const char *question =
 4740         "Deactivating snap will make its "
 4741         "data inaccessible. Do you want to "
 4742         "continue?";
 4743 
 4744     GF_ASSERT(words);
 4745     GF_ASSERT(dict);
 4746 
 4747     if ((wordcount != 3)) {
 4748         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4749         goto out;
 4750     }
 4751 
 4752     ret = dict_set_str(dict, "snapname", (char *)words[2]);
 4753     if (ret) {
 4754         gf_log("cli", GF_LOG_ERROR, "Unable to save snap-name %s", words[2]);
 4755         goto out;
 4756     }
 4757 
 4758     answer = cli_cmd_get_confirmation(state, question);
 4759     if (GF_ANSWER_NO == answer) {
 4760         ret = 1;
 4761         gf_log("cli", GF_LOG_DEBUG,
 4762                "User cancelled "
 4763                "snapshot deactivate operation");
 4764         goto out;
 4765     }
 4766 
 4767 out:
 4768     return ret;
 4769 }
 4770 
 4771 /* snapshot delete (all | snapname | volume <volname>)
 4772  * @arg-0, dict     : Request Dictionary to be sent to server side.
 4773  * @arg-1, words    : Contains individual words of CLI command.
 4774  * @arg-2, wordcount: Contains number of words present in the CLI command.
 4775  *
 4776  * return value : -1 on failure
 4777  *                 0 on success
 4778  *                 1 if user cancel the operation
 4779  */
 4780 int
 4781 cli_snap_delete_parse(dict_t *dict, const char **words, int wordcount,
 4782                       struct cli_state *state)
 4783 {
 4784     int ret = -1;
 4785     const char *question = NULL;
 4786     int32_t cmd = -1;
 4787     unsigned int cmdi = 2;
 4788     gf_answer_t answer = GF_ANSWER_NO;
 4789 
 4790     GF_ASSERT(words);
 4791     GF_ASSERT(dict);
 4792 
 4793     if (wordcount > 4 || wordcount <= cmdi) {
 4794         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4795         goto out;
 4796     }
 4797 
 4798     question =
 4799         "Deleting snap will erase all the information about "
 4800         "the snap. Do you still want to continue?";
 4801 
 4802     if (strcmp(words[cmdi], "all") == 0) {
 4803         ret = 0;
 4804         cmd = GF_SNAP_DELETE_TYPE_ALL;
 4805     } else if (strcmp(words[cmdi], "volume") == 0) {
 4806         if (++cmdi == wordcount) {
 4807             ret = -1;
 4808             gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4809             goto out;
 4810         }
 4811 
 4812         ret = dict_set_str(dict, "volname", (char *)words[cmdi]);
 4813         if (ret) {
 4814             gf_log("cli", GF_LOG_ERROR,
 4815                    "Could not save "
 4816                    "volume name %s",
 4817                    words[wordcount - 1]);
 4818             goto out;
 4819         }
 4820         cmd = GF_SNAP_DELETE_TYPE_VOL;
 4821     } else {
 4822         ret = dict_set_str(dict, "snapname", (char *)words[cmdi]);
 4823         if (ret) {
 4824             gf_log("cli", GF_LOG_ERROR,
 4825                    "Unable to save "
 4826                    "snapname %s",
 4827                    words[2]);
 4828             goto out;
 4829         }
 4830         cmd = GF_SNAP_DELETE_TYPE_SNAP;
 4831     }
 4832 
 4833     if ((cmdi + 1) != wordcount) {
 4834         ret = -1;
 4835         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4836         goto out;
 4837     }
 4838 
 4839     if (cmd == GF_SNAP_DELETE_TYPE_SNAP) {
 4840         answer = cli_cmd_get_confirmation(state, question);
 4841         if (GF_ANSWER_NO == answer) {
 4842             ret = 1;
 4843             gf_log("cli", GF_LOG_DEBUG,
 4844                    "User cancelled "
 4845                    "snapshot delete operation for snap %s",
 4846                    (char *)words[2]);
 4847             goto out;
 4848         }
 4849     }
 4850 
 4851     ret = dict_set_int32(dict, "sub-cmd", cmd);
 4852     if (ret) {
 4853         gf_log("cli", GF_LOG_ERROR,
 4854                "Could not save "
 4855                "type of snapshot delete");
 4856     }
 4857 out:
 4858     return ret;
 4859 }
 4860 
 4861 /* snapshot status [(snapname | volume <volname>)]
 4862  * @arg-0, dict     : Request Dictionary to be sent to server side.
 4863  * @arg-1, words    : Contains individual words of CLI command.
 4864  * @arg-2, wordcount: Contains number of words present in the CLI command.
 4865  *
 4866  * return value : -1 on failure
 4867  *                 0 on success
 4868  */
 4869 int
 4870 cli_snap_status_parse(dict_t *dict, const char **words, int wordcount)
 4871 {
 4872     int ret = -1;
 4873     int32_t cmd = GF_SNAP_STATUS_TYPE_ALL;
 4874     unsigned int cmdi = 2;
 4875     /* cmdi is command index, here cmdi is "2" (gluster snapshot status)*/
 4876 
 4877     GF_ASSERT(words);
 4878     GF_ASSERT(dict);
 4879 
 4880     if (wordcount > 4 || wordcount < cmdi) {
 4881         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4882         goto out;
 4883     }
 4884 
 4885     if (wordcount == cmdi) {
 4886         ret = 0;
 4887         goto out;
 4888     }
 4889 
 4890     /* if 3rd word is not "volume", then it must be "snapname"
 4891      */
 4892     if (strcmp(words[cmdi], "volume") != 0) {
 4893         ret = dict_set_str(dict, "snapname", (char *)words[cmdi]);
 4894         if (ret) {
 4895             gf_log("cli", GF_LOG_ERROR,
 4896                    "Count not save "
 4897                    "snap name %s",
 4898                    words[cmdi]);
 4899             goto out;
 4900         }
 4901 
 4902         if ((cmdi + 1) != wordcount) {
 4903             ret = -1;
 4904             gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4905             goto out;
 4906         }
 4907 
 4908         ret = 0;
 4909         cmd = GF_SNAP_STATUS_TYPE_SNAP;
 4910         goto out;
 4911     }
 4912 
 4913     /* If 3rd word is "volume", then check if next word is present.
 4914      * As, "snapshot info volume" is an invalid command
 4915      */
 4916     if ((cmdi + 1) == wordcount) {
 4917         ret = -1;
 4918         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 4919         goto out;
 4920     }
 4921 
 4922     ret = dict_set_str(dict, "volname", (char *)words[wordcount - 1]);
 4923     if (ret) {
 4924         gf_log("cli", GF_LOG_ERROR,
 4925                "Count not save "
 4926                "volume name %s",
 4927                words[wordcount - 1]);
 4928         goto out;
 4929     }
 4930     cmd = GF_SNAP_STATUS_TYPE_VOL;
 4931 
 4932 out:
 4933     if (ret == 0) {
 4934         ret = dict_set_int32(dict, "sub-cmd", cmd);
 4935         if (ret) {
 4936             gf_log("cli", GF_LOG_ERROR,
 4937                    "Could not save cmd "
 4938                    "of snapshot status");
 4939         }
 4940     }
 4941 
 4942     return ret;
 4943 }
 4944 
 4945 /* return value:
 4946  *      -1  in case of failure.
 4947  *       0  in case of success.
 4948  */
 4949 int32_t
 4950 cli_snap_config_limit_parse(const char **words, dict_t *dict,
 4951                             unsigned int wordcount, unsigned int index,
 4952                             char *key)
 4953 {
 4954     int ret = -1;
 4955     int limit = 0;
 4956     char *end_ptr = NULL;
 4957 
 4958     GF_ASSERT(words);
 4959     GF_ASSERT(dict);
 4960     GF_ASSERT(key);
 4961 
 4962     if (index >= wordcount) {
 4963         ret = -1;
 4964         cli_err("Please provide a value for %s.", key);
 4965         gf_log("cli", GF_LOG_ERROR, "Value not provided for %s", key);
 4966         goto out;
 4967     }
 4968 
 4969     limit = strtol(words[index], &end_ptr, 10);
 4970 
 4971     if (limit <= 0 || strcmp(end_ptr, "") != 0) {
 4972         ret = -1;
 4973         cli_err(
 4974             "Please enter an integer value "
 4975             "greater than zero for %s",
 4976             key);
 4977         goto out;
 4978     }
 4979 
 4980     ret = dict_set_int32(dict, key, limit);
 4981     if (ret) {
 4982         gf_log("cli", GF_LOG_ERROR,
 4983                "Could not set "
 4984                "%s in dictionary",
 4985                key);
 4986         goto out;
 4987     }
 4988 
 4989     ret = dict_set_dynstr_with_alloc(dict, "globalname", "All");
 4990     if (ret) {
 4991         gf_log("cli", GF_LOG_ERROR, "Could not set global key");
 4992         goto out;
 4993     }
 4994     ret = dict_set_int32(dict, "hold_global_locks", _gf_true);
 4995     if (ret) {
 4996         gf_log("cli", GF_LOG_ERROR, "Could not set global locks");
 4997         goto out;
 4998     }
 4999 
 5000 out:
 5001     return ret;
 5002 }
 5003 
 5004 /* function cli_snap_config_parse
 5005  * Config Syntax : gluster snapshot config [volname]
 5006  *                                         [snap-max-hard-limit <count>]
 5007  *                                         [snap-max-soft-limit <count>]
 5008  *
 5009    return value: <0  on failure
 5010                   1  if user cancels the operation, or limit value is out of
 5011                                                                       range
 5012                   0  on success
 5013 
 5014   NOTE : snap-max-soft-limit can only be set for system.
 5015 */
 5016 int32_t
 5017 cli_snap_config_parse(const char **words, int wordcount, dict_t *dict,
 5018                       struct cli_state *state)
 5019 {
 5020     int ret = -1;
 5021     gf_answer_t answer = GF_ANSWER_NO;
 5022     gf_boolean_t vol_presence = _gf_false;
 5023     struct snap_config_opt_vals_ *conf_vals = NULL;
 5024     int8_t hard_limit = 0;
 5025     int8_t soft_limit = 0;
 5026     int8_t config_type = -1;
 5027     const char *question = NULL;
 5028     unsigned int cmdi = 2;
 5029     /* cmdi is command index, here cmdi is "2" (gluster snapshot config)*/
 5030 
 5031     GF_ASSERT(words);
 5032     GF_ASSERT(dict);
 5033     GF_ASSERT(state);
 5034 
 5035     if ((wordcount < 2) || (wordcount > 7)) {
 5036         gf_log("cli", GF_LOG_ERROR, "Invalid wordcount(%d)", wordcount);
 5037         goto out;
 5038     }
 5039 
 5040     if (wordcount == 2) {
 5041         config_type = GF_SNAP_CONFIG_DISPLAY;
 5042         ret = 0;
 5043         goto set;
 5044     }
 5045 
 5046     /* auto-delete cannot be a volume name */
 5047     /* Check whether the 3rd word is volname */
 5048     if (strcmp(words[cmdi], "snap-max-hard-limit") != 0 &&
 5049         strcmp(words[cmdi], "snap-max-soft-limit") != 0 &&
 5050         strcmp(words[cmdi], "auto-delete") != 0 &&
 5051         strcmp(words[cmdi], "activate-on-create") != 0) {
 5052         ret = dict_set_str(dict, "volname", (char *)words[cmdi]);
 5053         if (ret) {
 5054             gf_log("cli", GF_LOG_ERROR, "Failed to set volname");
 5055             goto out;
 5056         }
 5057         cmdi++;
 5058         vol_presence = _gf_true;
 5059 
 5060         if (cmdi == wordcount) {
 5061             config_type = GF_SNAP_CONFIG_DISPLAY;
 5062             ret = 0;
 5063             goto set;
 5064         }
 5065     }
 5066 
 5067     config_type = GF_SNAP_CONFIG_TYPE_SET;
 5068 
 5069     if (strcmp(words[cmdi], "snap-max-hard-limit") == 0) {
 5070         ret = cli_snap_config_limit_parse(words, dict, wordcount, ++cmdi,
 5071                                           "snap-max-hard-limit");
 5072         if (ret) {
 5073             gf_log("cli", GF_LOG_ERROR,
 5074                    "Failed to parse snap "
 5075                    "config hard limit");
 5076             goto out;
 5077         }
 5078         hard_limit = 1;
 5079 
 5080         if (++cmdi == wordcount) {
 5081             ret = 0;
 5082             goto set;
 5083         }
 5084     }
 5085 
 5086     if (strcmp(words[cmdi], "snap-max-soft-limit") == 0) {
 5087         if (vol_presence == 1) {
 5088             ret = -1;
 5089             cli_err(
 5090                 "Soft limit cannot be set to individual "
 5091                 "volumes.");
 5092             gf_log("cli", GF_LOG_ERROR,
 5093                    "Soft limit cannot be "
 5094                    "set to volumes");
 5095             goto out;
 5096         }
 5097 
 5098         ret = cli_snap_config_limit_parse(words, dict, wordcount, ++cmdi,
 5099                                           "snap-max-soft-limit");
 5100         if (ret) {
 5101             gf_log("cli", GF_LOG_ERROR,
 5102                    "Failed to parse snap "
 5103                    "config soft limit");
 5104             goto out;
 5105         }
 5106 
 5107         if (++cmdi != wordcount) {
 5108             ret = -1;
 5109             gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 5110             goto out;
 5111         }
 5112         soft_limit = 1;
 5113     }
 5114 
 5115     if (hard_limit || soft_limit)
 5116         goto set;
 5117 
 5118     if (strcmp(words[cmdi], "auto-delete") == 0) {
 5119         if (vol_presence == 1) {
 5120             ret = -1;
 5121             cli_err(
 5122                 "As of now, auto-delete option cannot be set "
 5123                 "to volumes");
 5124             gf_log("cli", GF_LOG_ERROR,
 5125                    "auto-delete option "
 5126                    "cannot be set to volumes");
 5127             goto out;
 5128         }
 5129 
 5130         if (++cmdi >= wordcount) {
 5131             ret = -1;
 5132             gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 5133             goto out;
 5134         }
 5135 
 5136         ret = dict_set_str(dict, "auto-delete", (char *)words[cmdi]);
 5137         if (ret) {
 5138             gf_log("cli", GF_LOG_ERROR,
 5139                    "Failed to set "
 5140                    "value of auto-delete in request "
 5141                    "dictionary");
 5142             goto out;
 5143         }
 5144 
 5145         if (++cmdi != wordcount) {
 5146             ret = -1;
 5147             gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 5148             goto out;
 5149         }
 5150     } else if (strcmp(words[cmdi], "activate-on-create") == 0) {
 5151         if (vol_presence == 1) {
 5152             ret = -1;
 5153             cli_err(
 5154                 "As of now, activate-on-create option "
 5155                 "cannot be set to volumes");
 5156             gf_log("cli", GF_LOG_ERROR,
 5157                    "activate-on-create "
 5158                    "option cannot be set to volumes");
 5159             goto out;
 5160         }
 5161 
 5162         if (++cmdi >= wordcount) {
 5163             ret = -1;
 5164             gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 5165             goto out;
 5166         }
 5167 
 5168         ret = dict_set_str(dict, "snap-activate-on-create",
 5169                            (char *)words[cmdi]);
 5170         if (ret) {
 5171             gf_log("cli", GF_LOG_ERROR,
 5172                    "Failed to set value "
 5173                    "of activate-on-create in request dictionary");
 5174             goto out;
 5175         }
 5176 
 5177         if (++cmdi != wordcount) {
 5178             ret = -1;
 5179             gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 5180             goto out;
 5181         }
 5182     } else {
 5183         ret = -1;
 5184         gf_log("cli", GF_LOG_ERROR, "Invalid Syntax");
 5185         goto out;
 5186     }
 5187 
 5188     ret = 0; /* Success */
 5189 
 5190 set:
 5191     ret = dict_set_int32(dict, "config-command", config_type);
 5192     if (ret) {
 5193         gf_log("cli", GF_LOG_ERROR,
 5194                "Unable to set "
 5195                "config-command");
 5196         goto out;
 5197     }
 5198 
 5199     if (config_type == GF_SNAP_CONFIG_TYPE_SET && (hard_limit || soft_limit)) {
 5200         conf_vals = snap_confopt_vals;
 5201         if (hard_limit && soft_limit) {
 5202             question = conf_vals[GF_SNAP_CONFIG_SET_BOTH].question;
 5203         } else if (soft_limit) {
 5204             question = conf_vals[GF_SNAP_CONFIG_SET_SOFT].question;
 5205         } else if (hard_limit) {
 5206             question = conf_vals[GF_SNAP_CONFIG_SET_HARD].question;
 5207         }
 5208 
 5209         answer = cli_cmd_get_confirmation(state, question);
 5210         if (GF_ANSWER_NO == answer) {
 5211             ret = 1;
 5212             gf_log("cli", GF_LOG_DEBUG,
 5213                    "User cancelled "
 5214                    "snapshot config operation");
 5215         }
 5216     }
 5217 
 5218 out:
 5219     return ret;
 5220 }
 5221 
 5222 int
 5223 validate_op_name(const char *op, const char *opname, char **opwords)
 5224 {
 5225     int ret = -1;
 5226     int i = 0;
 5227 
 5228     GF_ASSERT(opname);
 5229     GF_ASSERT(opwords);
 5230 
 5231     for (i = 0; opwords[i] != NULL; i++) {
 5232         if (strcmp(opwords[i], opname) == 0) {
 5233             cli_out("\"%s\" cannot be a %s", opname, op);
 5234             goto out;
 5235         }
 5236     }
 5237     ret = 0;
 5238 out:
 5239     return ret;
 5240 }
 5241 
 5242 int32_t
 5243 cli_cmd_snapshot_parse(const char **words, int wordcount, dict_t **options,
 5244                        struct cli_state *state)
 5245 {
 5246     int32_t ret = -1;
 5247     dict_t *dict = NULL;
 5248     gf1_cli_snapshot type = GF_SNAP_OPTION_TYPE_NONE;
 5249     char *w = NULL;
 5250     static char *opwords[] = {"create",     "delete", "restore", "activate",
 5251                               "deactivate", "list",   "status",  "config",