summaryrefslogtreecommitdiffstats
path: root/cli/src
diff options
context:
space:
mode:
Diffstat (limited to 'cli/src')
-rw-r--r--cli/src/Makefile.am23
-rw-r--r--cli/src/cli-cmd-parser.c451
-rw-r--r--cli/src/cli-cmd-probe.c89
-rw-r--r--cli/src/cli-cmd-volume.c485
-rw-r--r--cli/src/cli-cmd.c199
-rw-r--r--cli/src/cli-cmd.h46
-rw-r--r--cli/src/cli-mem-types.h37
-rw-r--r--cli/src/cli-rl.c368
-rw-r--r--cli/src/cli.c467
-rw-r--r--cli/src/cli.h139
-rw-r--r--cli/src/cli3_1-cops.c805
-rw-r--r--cli/src/input.c98
-rw-r--r--cli/src/registry.c386
13 files changed, 3593 insertions, 0 deletions
diff --git a/cli/src/Makefile.am b/cli/src/Makefile.am
new file mode 100644
index 000000000..8e1732603
--- /dev/null
+++ b/cli/src/Makefile.am
@@ -0,0 +1,23 @@
+sbin_PROGRAMS = gluster
+
+gluster_SOURCES = cli.c registry.c input.c cli-cmd.c cli-rl.c \
+ cli-cmd-volume.c cli-cmd-probe.c cli3_1-cops.c cli-cmd-parser.c
+
+gluster_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la $(GF_LDADD)\
+ $(RLLIBS) $(top_builddir)/xlators/protocol/lib/src/libgfproto1.la\
+ $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la
+
+gluster_LDFLAGS = $(GF_LDFLAGS) $(GF_GLUSTERFS_LDFLAGS)
+noinst_HEADERS = cli.h cli-mem-types.h cli-cmd.h
+
+AM_CFLAGS = -fPIC -Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -D$(GF_HOST_OS)\
+ -I$(top_srcdir)/libglusterfs/src -I$(top_srcdir)/rpc/rpc-lib/src\
+ -I$(top_srcdir)/xlators/protocol/lib/src\
+ -DDATADIR=\"$(localstatedir)\" \
+ -DCONFDIR=\"$(sysconfdir)/glusterfs\" $(GF_GLUSTERFS_CFLAGS)
+
+
+CLEANFILES =
+
+$(top_builddir)/libglusterfs/src/libglusterfs.la:
+ $(MAKE) -C $(top_builddir)/libglusterfs/src/ all
diff --git a/cli/src/cli-cmd-parser.c b/cli/src/cli-cmd-parser.c
new file mode 100644
index 000000000..68c6a29c8
--- /dev/null
+++ b/cli/src/cli-cmd-parser.c
@@ -0,0 +1,451 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "cli.h"
+#include "cli-cmd.h"
+#include "cli-mem-types.h"
+#include "protocol-common.h"
+#include "dict.h"
+#include "gluster1.h"
+
+int32_t
+cli_cmd_volume_create_parse (const char **words, int wordcount, dict_t **options)
+{
+ dict_t *dict = NULL;
+ char *volname = NULL;
+ int ret = -1;
+ gf1_cluster_type type = GF_CLUSTER_TYPE_NONE;
+ int count = 0;
+ int brick_count = 0, brick_index = 0;
+ char brick_list[8192] = {0,};
+
+ GF_ASSERT (words);
+ GF_ASSERT (options);
+
+ GF_ASSERT ((strcmp (words[0], "volume")) == 0);
+ GF_ASSERT ((strcmp (words[1], "create")) == 0);
+
+ dict = dict_new ();
+
+ if (!dict)
+ goto out;
+
+ volname = (char *)words[2];
+
+ GF_ASSERT (volname);
+
+ ret = dict_set_str (dict, "volname", volname);
+
+ if (ret)
+ goto out;
+
+ if ((strcasecmp (words[3], "replica")) == 0) {
+ type = GF_CLUSTER_TYPE_REPLICATE;
+ count = strtol (words[4], NULL, 0);
+ if (!count) {
+ /* Wrong number of replica count */
+ ret = -1;
+ goto out;
+ }
+ ret = dict_set_int32 (dict, "replica-count", count);
+ if (ret)
+ goto out;
+
+ brick_index = 5;
+ } else if ((strcasecmp (words[3], "stripe")) == 0) {
+ type = GF_CLUSTER_TYPE_STRIPE;
+ count = strtol (words[4], NULL, 0);
+ if (!count) {
+ /* Wrong number of stripe count */
+ ret = -1;
+ goto out;
+ }
+ ret = dict_set_int32 (dict, "stripe-count", count);
+ if (ret)
+ goto out;
+ brick_index = 5;
+ } else {
+ type = GF_CLUSTER_TYPE_NONE;
+ brick_index = 3;
+ }
+
+ ret = dict_set_int32 (dict, "type", type);
+ if (ret)
+ goto out;
+ strcpy (brick_list, " ");
+ while (brick_index < wordcount) {
+ GF_ASSERT (words[brick_index]);
+ if (!strchr (words[brick_index], ':')) {
+ gf_log ("cli", GF_LOG_ERROR,
+ "wrong brick type, use <HOSTNAME>:<export-dir>");
+ ret = -1;
+ goto out;
+ }
+
+ strcat (brick_list, words[brick_index]);
+ strcat (brick_list, " ");
+ ++brick_count;
+ ++brick_index;
+ /*
+ char key[50];
+ snprintf (key, 50, "brick%d", ++brick_count);
+ ret = dict_set_str (dict, key, (char *)words[brick_index++]);
+
+ if (ret)
+ goto out;
+ */
+ }
+ ret = dict_set_str (dict, "bricks", brick_list);
+ if (ret)
+ goto out;
+
+ ret = dict_set_int32 (dict, "count", brick_count);
+ if (ret)
+ goto out;
+
+ *options = dict;
+
+out:
+ if (ret) {
+ gf_log ("cli", GF_LOG_ERROR, "Unable to parse create volume CLI");
+ if (dict)
+ dict_destroy (dict);
+ }
+
+ return ret;
+}
+
+
+int32_t
+cli_cmd_volume_set_parse (const char **words, int wordcount, dict_t **options)
+{
+ dict_t *dict = NULL;
+ char *volname = NULL;
+ int ret = -1;
+ int count = 0;
+ char *key = NULL;
+ char *value = NULL;
+ int i = 0;
+ char str[50] = {0,};
+
+ GF_ASSERT (words);
+ GF_ASSERT (options);
+
+ GF_ASSERT ((strcmp (words[0], "volume")) == 0);
+ GF_ASSERT ((strcmp (words[1], "set")) == 0);
+
+ dict = dict_new ();
+
+ if (!dict)
+ goto out;
+
+ volname = (char *)words[2];
+
+ GF_ASSERT (volname);
+
+ ret = dict_set_str (dict, "volname", volname);
+
+ if (ret)
+ goto out;
+
+ for (i = 3; i < wordcount; i++) {
+ key = strtok ((char *)words[i], "=");
+ value = strtok (NULL, "=");
+
+ GF_ASSERT (key);
+ GF_ASSERT (value);
+
+ count++;
+
+ sprintf (str, "key%d", count);
+ ret = dict_set_str (dict, str, key);
+ if (ret)
+ goto out;
+
+ sprintf (str, "value%d", count);
+ ret = dict_set_str (dict, str, value);
+
+ if (ret)
+ goto out;
+ }
+
+ ret = dict_set_int32 (dict, "count", count);
+
+ if (ret)
+ goto out;
+
+ *options = dict;
+
+out:
+ if (ret) {
+ if (dict)
+ dict_destroy (dict);
+ }
+
+ return ret;
+}
+
+int32_t
+cli_cmd_volume_add_brick_parse (const char **words, int wordcount,
+ dict_t **options)
+{
+ dict_t *dict = NULL;
+ char *volname = NULL;
+ int ret = -1;
+ gf1_cluster_type type = GF_CLUSTER_TYPE_NONE;
+ int count = 0;
+ char key[50];
+ int brick_count = 0, brick_index = 0;
+
+ GF_ASSERT (words);
+ GF_ASSERT (options);
+
+ GF_ASSERT ((strcmp (words[0], "volume")) == 0);
+ GF_ASSERT ((strcmp (words[1], "add-brick")) == 0);
+
+ dict = dict_new ();
+
+ if (!dict)
+ goto out;
+
+ volname = (char *)words[2];
+
+ GF_ASSERT (volname);
+
+ ret = dict_set_str (dict, "volname", volname);
+
+ if (ret)
+ goto out;
+
+ if ((strcasecmp (words[3], "replica")) == 0) {
+ type = GF_CLUSTER_TYPE_REPLICATE;
+ count = strtol (words[4], NULL, 0);
+ brick_index = 5;
+ } else if ((strcasecmp (words[3], "stripe")) == 0) {
+ type = GF_CLUSTER_TYPE_STRIPE;
+ count = strtol (words[4], NULL, 0);
+ brick_index = 5;
+ } else {
+ brick_index = 3;
+ }
+
+ ret = dict_set_int32 (dict, "type", type);
+
+ if (ret)
+ goto out;
+
+ while (brick_index < wordcount) {
+ GF_ASSERT (words[brick_index]);
+
+ snprintf (key, 50, "brick%d", ++brick_count);
+ ret = dict_set_str (dict, key, (char *)words[brick_index++]);
+
+ if (ret)
+ goto out;
+ }
+
+ ret = dict_set_int32 (dict, "count", brick_count);
+
+ if (ret)
+ goto out;
+
+ *options = dict;
+
+out:
+ if (ret) {
+ gf_log ("cli", GF_LOG_ERROR, "Unable to parse add-brick CLI");
+ if (dict)
+ dict_destroy (dict);
+ }
+
+ return ret;
+}
+
+
+int32_t
+cli_cmd_volume_remove_brick_parse (const char **words, int wordcount,
+ dict_t **options)
+{
+ dict_t *dict = NULL;
+ char *volname = NULL;
+ int ret = -1;
+ gf1_cluster_type type = GF_CLUSTER_TYPE_NONE;
+ int count = 0;
+ char key[50];
+ int brick_count = 0, brick_index = 0;
+
+ GF_ASSERT (words);
+ GF_ASSERT (options);
+
+ GF_ASSERT ((strcmp (words[0], "volume")) == 0);
+ GF_ASSERT ((strcmp (words[1], "remove-brick")) == 0);
+
+ dict = dict_new ();
+
+ if (!dict)
+ goto out;
+
+ volname = (char *)words[2];
+
+ GF_ASSERT (volname);
+
+ ret = dict_set_str (dict, "volname", volname);
+
+ if (ret)
+ goto out;
+
+ if ((strcasecmp (words[3], "replica")) == 0) {
+ type = GF_CLUSTER_TYPE_REPLICATE;
+ count = strtol (words[4], NULL, 0);
+ brick_index = 5;
+ } else if ((strcasecmp (words[3], "stripe")) == 0) {
+ type = GF_CLUSTER_TYPE_STRIPE;
+ count = strtol (words[4], NULL, 0);
+ brick_index = 5;
+ } else {
+ brick_index = 3;
+ }
+
+ ret = dict_set_int32 (dict, "type", type);
+
+ if (ret)
+ goto out;
+
+ while (brick_index < wordcount) {
+ GF_ASSERT (words[brick_index]);
+
+ snprintf (key, 50, "brick%d", ++brick_count);
+ ret = dict_set_str (dict, key, (char *)words[brick_index++]);
+
+ if (ret)
+ goto out;
+ }
+
+ ret = dict_set_int32 (dict, "count", brick_count);
+
+ if (ret)
+ goto out;
+
+ *options = dict;
+
+out:
+ if (ret) {
+ gf_log ("cli", GF_LOG_ERROR, "Unable to parse remove-brick CLI");
+ if (dict)
+ dict_destroy (dict);
+ }
+
+ return ret;
+}
+
+
+int32_t
+cli_cmd_volume_replace_brick_parse (const char **words, int wordcount,
+ dict_t **options)
+{
+ dict_t *dict = NULL;
+ char *volname = NULL;
+ int ret = -1;
+ char *op = NULL;
+ int op_index = 0;
+ gf1_cli_replace_op replace_op = GF_REPLACE_OP_NONE;
+
+ GF_ASSERT (words);
+ GF_ASSERT (options);
+
+ GF_ASSERT ((strcmp (words[0], "volume")) == 0);
+ GF_ASSERT ((strcmp (words[1], "replace-brick")) == 0);
+
+ dict = dict_new ();
+
+ if (!dict)
+ goto out;
+
+ volname = (char *)words[2];
+
+ GF_ASSERT (volname);
+
+ ret = dict_set_str (dict, "volname", volname);
+
+ if (ret)
+ goto out;
+
+ if (strchr ((char *)words[3], ':')) {
+ ret = dict_set_str (dict, "src-brick", (char *)words[3]);
+
+ if (ret)
+ goto out;
+
+ GF_ASSERT (words[4]);
+
+ ret = dict_set_str (dict, "dst-brick", (char *)words[4]);
+
+ if (ret)
+ goto out;
+
+ op_index = 5;
+ } else {
+ op_index = 3;
+ }
+
+ GF_ASSERT (words[op_index]);
+
+ op = (char *) words[op_index];
+
+ if (!strcasecmp ("start", op)) {
+ replace_op = GF_REPLACE_OP_START;
+ } else if (!strcasecmp ("stop", op)) {
+ replace_op = GF_REPLACE_OP_STOP;
+ } else if (!strcasecmp ("pause", op)) {
+ replace_op = GF_REPLACE_OP_PAUSE;
+ } else if (!strcasecmp ("abort", op)) {
+ replace_op = GF_REPLACE_OP_ABORT;
+ } else if (!strcasecmp ("status", op)) {
+ replace_op = GF_REPLACE_OP_STATUS;
+ }
+
+ GF_ASSERT (replace_op != GF_REPLACE_OP_NONE);
+
+ ret = dict_set_int32 (dict, "operation", (int32_t) replace_op);
+
+ if (ret)
+ goto out;
+
+ *options = dict;
+
+out:
+ if (ret) {
+ gf_log ("cli", GF_LOG_ERROR, "Unable to parse remove-brick CLI");
+ if (dict)
+ dict_destroy (dict);
+ }
+
+ return ret;
+}
diff --git a/cli/src/cli-cmd-probe.c b/cli/src/cli-cmd-probe.c
new file mode 100644
index 000000000..dccdaedbe
--- /dev/null
+++ b/cli/src/cli-cmd-probe.c
@@ -0,0 +1,89 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "cli.h"
+#include "cli-cmd.h"
+#include "cli-mem-types.h"
+#include "protocol-common.h"
+
+extern struct rpc_clnt *global_rpc;
+
+extern rpc_clnt_prog_t *cli_rpc_prog;
+
+int
+cli_cmd_probe_cbk (struct cli_state *state, struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+
+ //cli_out ("probe not implemented\n");
+ proc = &cli_rpc_prog->proctable[GF1_CLI_PROBE];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, "localhost");
+ }
+
+out:
+ if (ret)
+ cli_out ("Probe failed!");
+ return ret;
+}
+
+
+
+struct cli_cmd cli_probe_cmds[] = {
+ { "probe <VOLNAME>",
+ cli_cmd_probe_cbk },
+
+
+ { NULL, NULL }
+};
+
+
+int
+cli_cmd_probe_register (struct cli_state *state)
+{
+ int ret = 0;
+ struct cli_cmd *cmd = NULL;
+
+ for (cmd = cli_probe_cmds; cmd->pattern; cmd++) {
+ ret = cli_cmd_register (&state->tree, cmd->pattern, cmd->cbk);
+ if (ret)
+ goto out;
+ }
+out:
+ return ret;
+}
diff --git a/cli/src/cli-cmd-volume.c b/cli/src/cli-cmd-volume.c
new file mode 100644
index 000000000..227438db4
--- /dev/null
+++ b/cli/src/cli-cmd-volume.c
@@ -0,0 +1,485 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "cli.h"
+#include "cli-cmd.h"
+#include "cli-mem-types.h"
+
+extern struct rpc_clnt *global_rpc;
+
+extern rpc_clnt_prog_t *cli_rpc_prog;
+
+int
+cli_cmd_volume_info_cbk (struct cli_state *state, struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_GET_VOLUME];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, "localhost");
+ }
+
+out:
+ if (ret)
+ cli_out ("Probe failed!");
+ return ret;
+
+}
+
+
+int
+cli_cmd_volume_create_cbk (struct cli_state *state, struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ dict_t *options = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_CREATE_VOLUME];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ ret = cli_cmd_volume_create_parse (words, wordcount, &options);
+
+ if (ret)
+ goto out;
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, options);
+ }
+
+out:
+ if (ret) {
+ char *volname = (char *) words[2];
+ cli_out ("Creating Volume %s failed",volname );
+ }
+ return ret;
+}
+
+
+int
+cli_cmd_volume_delete_cbk (struct cli_state *state, struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ char *volname = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_DELETE_VOLUME];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ //TODO: Build validation here
+ volname = (char *)words[2];
+ GF_ASSERT (volname);
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, volname);
+ }
+
+out:
+ if (ret)
+ cli_out ("Deleting Volume %s failed", volname);
+
+ return ret;
+}
+
+
+int
+cli_cmd_volume_start_cbk (struct cli_state *state, struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ char *volname = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_START_VOLUME];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ //TODO: Build validation here
+ volname = (char *)words[2];
+ GF_ASSERT (volname);
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, volname);
+ }
+
+out:
+ if (ret)
+ cli_out ("Starting Volume %s failed", volname);
+
+ return ret;
+}
+
+
+int
+cli_cmd_volume_stop_cbk (struct cli_state *state, struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ char *volname = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_STOP_VOLUME];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ //TODO: Build validation here
+ volname = (char *)words[2];
+ GF_ASSERT (volname);
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, volname);
+ }
+
+out:
+ if (ret)
+ cli_out ("Stopping Volume %s failed", volname);
+
+ return ret;
+}
+
+
+int
+cli_cmd_volume_rename_cbk (struct cli_state *state, struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ dict_t *dict = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_RENAME_VOLUME];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ dict = dict_new ();
+
+ if (dict)
+ goto out;
+
+ GF_ASSERT (words[2]);
+ GF_ASSERT (words[3]);
+
+ //TODO: Build validation here
+ ret = dict_set_str (dict, "old-volname", (char *)words[2]);
+
+ if (ret)
+ goto out;
+
+ ret = dict_set_str (dict, "new-volname", (char *)words[3]);
+
+ if (ret)
+ goto out;
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, dict);
+ }
+
+out:
+ if (ret) {
+ char *volname = (char *) words[2];
+ if (dict)
+ dict_destroy (dict);
+ cli_out ("Renaming Volume %s failed", volname );
+ }
+
+ return ret;
+}
+
+
+int
+cli_cmd_volume_defrag_cbk (struct cli_state *state, struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ char *volname = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_DEFRAG_VOLUME];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ //TODO: Build validation here
+ volname = (char *)words[2];
+ GF_ASSERT (volname);
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, volname);
+ }
+
+out:
+ if (ret)
+ cli_out ("Defrag of Volume %s failed", volname);
+
+ return 0;
+}
+
+
+int
+cli_cmd_volume_set_cbk (struct cli_state *state, struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ char *volname = NULL;
+ dict_t *dict = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_SET_VOLUME];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ volname = (char *)words[2];
+ GF_ASSERT (volname);
+
+ GF_ASSERT (words[3]);
+
+ ret = cli_cmd_volume_set_parse (words, wordcount, &dict);
+
+ if (ret)
+ goto out;
+
+ //TODO: Build validation here
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, dict);
+ }
+
+out:
+ if (ret) {
+ if (dict)
+ dict_destroy (dict);
+ cli_out ("Changing option on Volume %s failed", volname);
+ }
+
+ return 0;
+}
+
+
+int
+cli_cmd_volume_add_brick_cbk (struct cli_state *state,
+ struct cli_cmd_word *word, const char **words,
+ int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ dict_t *options = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_ADD_BRICK];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ ret = cli_cmd_volume_add_brick_parse (words, wordcount, &options);
+
+ if (ret)
+ goto out;
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, options);
+ }
+
+out:
+ if (ret) {
+ char *volname = (char *) words[2];
+ cli_out ("Adding brick to Volume %s failed",volname );
+ }
+ return ret;
+}
+
+
+int
+cli_cmd_volume_remove_brick_cbk (struct cli_state *state,
+ struct cli_cmd_word *word, const char **words,
+ int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ dict_t *options = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_REMOVE_BRICK];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ ret = cli_cmd_volume_remove_brick_parse (words, wordcount, &options);
+
+ if (ret)
+ goto out;
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, options);
+ }
+
+out:
+ if (ret) {
+ char *volname = (char *) words[2];
+ cli_out ("Removing brick from Volume %s failed",volname );
+ }
+ return ret;
+
+}
+
+
+
+
+int
+cli_cmd_volume_replace_brick_cbk (struct cli_state *state,
+ struct cli_cmd_word *word,
+ const char **words,
+ int wordcount)
+{
+ int ret = -1;
+ rpc_clnt_procedure_t *proc = NULL;
+ call_frame_t *frame = NULL;
+ dict_t *options = NULL;
+
+ proc = &cli_rpc_prog->proctable[GF1_CLI_REPLACE_BRICK];
+
+ frame = create_frame (THIS, THIS->ctx->pool);
+ if (!frame)
+ goto out;
+
+ ret = cli_cmd_volume_replace_brick_parse (words, wordcount, &options);
+
+ if (ret)
+ goto out;
+
+ if (proc->fn) {
+ ret = proc->fn (frame, THIS, options);
+ }
+
+out:
+ if (ret) {
+ char *volname = (char *) words[2];
+ cli_out ("Replacing brick from Volume %s failed",volname );
+ }
+ return ret;
+
+}
+
+
+int
+cli_cmd_volume_set_transport_cbk (struct cli_state *state,
+ struct cli_cmd_word *word,
+ const char **words, int wordcount)
+{
+ cli_out ("volume set-transport not implemented\n");
+ return 0;
+}
+
+
+struct cli_cmd volume_cmds[] = {
+ { "volume info [all|<VOLNAME>]",
+ cli_cmd_volume_info_cbk },
+
+ { "volume create <NEW-VOLNAME> [stripe <COUNT>] [replicate <COUNT>] <NEW-BRICK> ...",
+ cli_cmd_volume_create_cbk },
+
+ { "volume delete <VOLNAME>",
+ cli_cmd_volume_delete_cbk },
+
+ { "volume start <VOLNAME>",
+ cli_cmd_volume_start_cbk },
+
+ { "volume stop <VOLNAME>",
+ cli_cmd_volume_stop_cbk },
+
+ { "volume rename <VOLNAME> <NEW-VOLNAME>",
+ cli_cmd_volume_rename_cbk },
+
+ { "volume add-brick <VOLNAME> [(replica <COUNT>)|(stripe <COUNT>)] <NEW-BRICK> ...",
+ cli_cmd_volume_add_brick_cbk },
+
+ { "volume remove-brick <VOLNAME> [(replica <COUNT>)|(stripe <COUNT>)] <BRICK> ...",
+ cli_cmd_volume_remove_brick_cbk },
+
+ { "volume defrag <VOLNAME>",
+ cli_cmd_volume_defrag_cbk },
+
+ { "volume replace-brick <VOLNAME> (<BRICK> <NEW-BRICK>)|pause|abort|start|status",
+ cli_cmd_volume_replace_brick_cbk },
+
+ { "volume set-transport <VOLNAME> <TRANSPORT-TYPE> [<TRANSPORT-TYPE>] ...",
+ cli_cmd_volume_set_transport_cbk },
+
+ { "volume set <VOLNAME> <KEY> <VALUE>",
+ cli_cmd_volume_set_cbk },
+
+ { NULL, NULL }
+};
+
+
+int
+cli_cmd_volume_register (struct cli_state *state)
+{
+ int ret = 0;
+ struct cli_cmd *cmd = NULL;
+
+ for (cmd = volume_cmds; cmd->pattern; cmd++) {
+ ret = cli_cmd_register (&state->tree, cmd->pattern, cmd->cbk);
+ if (ret)
+ goto out;
+ }
+out:
+ return ret;
+}
diff --git a/cli/src/cli-cmd.c b/cli/src/cli-cmd.c
new file mode 100644
index 000000000..a91dd77e2
--- /dev/null
+++ b/cli/src/cli-cmd.c
@@ -0,0 +1,199 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "cli.h"
+#include "cli-cmd.h"
+#include "cli-mem-types.h"
+
+#include <fnmatch.h>
+
+static int cmd_done;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+int
+cli_cmd_process (struct cli_state *state, int argc, char **argv)
+{
+ int ret = 0;
+ struct cli_cmd_word *word = NULL;
+ struct cli_cmd_word *next = NULL;
+ int i = 0;
+
+ word = &state->tree.root;
+
+ for (i = 0; i < argc; i++) {
+ next = cli_cmd_nextword (word, argv[i]);
+
+ word = next;
+ if (!word)
+ break;
+
+ if (word->cbkfn)
+ break;
+ }
+
+ if (!word) {
+ cli_out ("unrecognized word: %s (position %d)\n",
+ argv[i], i);
+ return -1;
+ }
+
+ if (!word->cbkfn) {
+ cli_out ("unrecognized command\n");
+ return -1;
+ }
+
+ ret = word->cbkfn (state, word, (const char **)argv, argc);
+
+ return ret;
+}
+
+
+int
+cli_cmd_input_token_count (const char *text)
+{
+ int count = 0;
+ const char *trav = NULL;
+ int is_spc = 1;
+
+ for (trav = text; *trav; trav++) {
+ if (*trav == ' ') {
+ is_spc = 1;
+ } else {
+ if (is_spc) {
+ count++;
+ is_spc = 0;
+ }
+ }
+ }
+
+ return count;
+}
+
+
+int
+cli_cmd_process_line (struct cli_state *state, const char *text)
+{
+ int count = 0;
+ char **tokens = NULL;
+ char **tokenp = NULL;
+ char *token = NULL;
+ char *copy = NULL;
+ char *saveptr = NULL;
+ int i = 0;
+ int ret = -1;
+
+ count = cli_cmd_input_token_count (text);
+
+ tokens = calloc (count + 1, sizeof (*tokens));
+ if (!tokens)
+ return -1;
+
+ copy = strdup (text);
+ if (!copy)
+ goto out;
+
+ tokenp = tokens;
+
+ for (token = strtok_r (copy, " \t\r\n", &saveptr); token;
+ token = strtok_r (NULL, " \t\r\n", &saveptr)) {
+ *tokenp = strdup (token);
+
+ if (!*tokenp)
+ goto out;
+ tokenp++;
+ i++;
+
+ }
+
+ ret = cli_cmd_process (state, count, tokens);
+out:
+ if (copy)
+ free (copy);
+
+ if (tokens)
+ cli_cmd_tokens_destroy (tokens);
+
+ return ret;
+}
+
+
+int
+cli_cmds_register (struct cli_state *state)
+{
+ int ret = 0;
+
+ ret = cli_cmd_volume_register (state);
+ if (ret)
+ goto out;
+
+ ret = cli_cmd_probe_register (state);
+ if (ret)
+ goto out;
+
+out:
+ return ret;
+}
+
+int
+cli_cmd_await_response ()
+{
+ pthread_mutex_init (&cond_mutex, NULL);
+ pthread_cond_init (&cond, NULL);
+ cmd_done = 0;
+
+ pthread_mutex_lock (&cond_mutex);
+ {
+ while (!cmd_done) {
+ pthread_cond_wait (&cond, &cond_mutex);
+ }
+ }
+ pthread_mutex_unlock (&cond_mutex);
+
+ pthread_mutex_destroy (&cond_mutex);
+ pthread_cond_destroy (&cond);
+
+ return 0;
+}
+
+int
+cli_cmd_broadcast_response ()
+{
+ pthread_mutex_lock (&cond_mutex);
+ {
+ cmd_done = 1;
+ pthread_cond_broadcast (&cond);
+ }
+
+ pthread_mutex_unlock (&cond_mutex);
+
+ return 0;
+}
+
diff --git a/cli/src/cli-cmd.h b/cli/src/cli-cmd.h
new file mode 100644
index 000000000..d1da3e2ac
--- /dev/null
+++ b/cli/src/cli-cmd.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (c) 2006-2009 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CLI_CMD_H__
+#define __CLI_CMD_H__
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "cli.h"
+
+struct cli_cmd {
+ const char *pattern;
+ cli_cmd_cbk_t *cbk;
+};
+
+int cli_cmd_volume_register (struct cli_state *state);
+
+int cli_cmd_probe_register (struct cli_state *state);
+
+struct cli_cmd_word *cli_cmd_nextword (struct cli_cmd_word *word,
+ const char *text);
+void cli_cmd_tokens_destroy (char **tokens);
+
+int cli_cmd_await_response ();
+
+int cli_cmd_broadcast_response ();
+#endif /* __CLI_CMD_H__ */
diff --git a/cli/src/cli-mem-types.h b/cli/src/cli-mem-types.h
new file mode 100644
index 000000000..279e5e908
--- /dev/null
+++ b/cli/src/cli-mem-types.h
@@ -0,0 +1,37 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CLI_MEM_TYPES_H__
+#define __CLI_MEM_TYPES_H__
+
+#include "mem-types.h"
+
+#define CLI_MEM_TYPE_START (gf_common_mt_end + 1)
+
+enum cli_mem_types_ {
+ cli_mt_xlator_list_t = CLI_MEM_TYPE_START,
+ cli_mt_xlator_t,
+ cli_mt_xlator_cmdline_option_t,
+ cli_mt_char,
+ cli_mt_call_pool_t,
+ cli_mt_end
+
+};
+
+#endif
diff --git a/cli/src/cli-rl.c b/cli/src/cli-rl.c
new file mode 100644
index 000000000..bc2e80eba
--- /dev/null
+++ b/cli/src/cli-rl.c
@@ -0,0 +1,368 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "cli.h"
+#include "cli-cmd.h"
+#include "cli-mem-types.h"
+
+#include "event.h"
+
+#include <fnmatch.h>
+
+#ifdef HAVE_READLINE
+
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+
+int
+cli_rl_out (struct cli_state *state, const char *fmt, va_list ap)
+{
+ int tmp_rl_point = rl_point;
+ int n = rl_end;
+ int i = 0;
+ int ret = 0;
+
+ if (rl_end >= 0 ) {
+ rl_kill_text (0, rl_end);
+ rl_redisplay ();
+ }
+
+ printf ("\r");
+
+ for (i = 0; i <= strlen (state->prompt); i++)
+ printf (" ");
+
+ printf ("\r");
+
+ ret = vprintf (fmt, ap);
+
+ printf ("\n");
+ fflush(stdout);
+
+ if (n) {
+ rl_do_undo ();
+ rl_point = tmp_rl_point;
+ rl_reset_line_state ();
+ }
+
+ return ret;
+}
+
+
+void
+cli_rl_process_line (char *line)
+{
+ struct cli_state *state = NULL;
+ int ret = 0;
+
+ state = global_state;
+
+ state->rl_processing = 1;
+ {
+ ret = cli_cmd_process_line (state, line);
+ add_history (line);
+ }
+ state->rl_processing = 0;
+}
+
+
+int
+cli_rl_stdin (int fd, int idx, void *data,
+ int poll_out, int poll_in, int poll_err)
+{
+ rl_callback_read_char ();
+
+ return 0;
+}
+
+
+char *
+cli_rl_autocomplete_entry (const char *text, int times)
+{
+ struct cli_state *state = NULL;
+ char *retp = NULL;
+
+ state = global_state;
+
+ if (!state->matchesp)
+ return NULL;
+
+ retp = *state->matchesp;
+
+ state->matchesp++;
+
+ return retp ? strdup (retp) : NULL;
+}
+
+
+int
+cli_rl_token_count (const char *text)
+{
+ int count = 0;
+ const char *trav = NULL;
+ int is_spc = 1;
+
+ for (trav = text; *trav; trav++) {
+ if (*trav == ' ') {
+ is_spc = 1;
+ } else {
+ if (is_spc) {
+ count++;
+ is_spc = 0;
+ }
+ }
+ }
+
+ if (is_spc)
+ /* what needs to be autocompleted is a full
+ new word, and not extend the last word
+ */
+ count++;
+
+ return count;
+}
+
+
+char **
+cli_rl_tokenize (const char *text)
+{
+ int count = 0;
+ char **tokens = NULL;
+ char **tokenp = NULL;
+ char *token = NULL;
+ char *copy = NULL;
+ char *saveptr = NULL;
+ int i = 0;
+
+ count = cli_rl_token_count (text);
+
+ tokens = calloc (count + 1, sizeof (*tokens));
+ if (!tokens)
+ return NULL;
+
+ copy = strdup (text);
+ if (!copy)
+ goto out;
+
+ tokenp = tokens;
+
+ for (token = strtok_r (copy, " \t\r\n", &saveptr); token;
+ token = strtok_r (NULL, " \t\r\n", &saveptr)) {
+ *tokenp = strdup (token);
+
+ if (!*tokenp)
+ goto out;
+ tokenp++;
+ i++;
+
+ }
+
+ if (i < count) {
+ /* symoblize that what needs to be autocompleted is
+ the full set of possible nextwords, and not extend
+ the last word
+ */
+ *tokenp = strdup ("");
+ if (!*tokenp)
+ goto out;
+ tokenp++;
+ i++;
+ }
+
+out:
+ if (copy)
+ free (copy);
+
+ if (i < count) {
+ cli_cmd_tokens_destroy (tokens);
+ tokens = NULL;
+ }
+
+ return tokens;
+}
+
+
+char **
+cli_rl_get_matches (struct cli_state *state, struct cli_cmd_word *word,
+ const char *text)
+{
+ char **matches = NULL;
+ char **matchesp = NULL;
+ struct cli_cmd_word **next = NULL;
+ int count = 0;
+ int len = 0;
+
+ len = strlen (text);
+
+ if (!word->nextwords)
+ return NULL;
+
+ for (next = word->nextwords; *next; next++)
+ count++;
+
+ matches = calloc (count + 1, sizeof (*matches));
+ matchesp = matches;
+
+ for (next = word->nextwords; *next; next++) {
+ if ((*next)->match) {
+ continue;
+ }
+
+ if (strncmp ((*next)->word, text, len) == 0) {
+ *matchesp = strdup ((*next)->word);
+ matchesp++;
+ }
+ }
+
+ return matches;
+}
+
+
+int
+cli_rl_autocomplete_prepare (struct cli_state *state, const char *text)
+{
+ struct cli_cmd_word *word = NULL;
+ struct cli_cmd_word *next = NULL;
+ char **tokens = NULL;
+ char **tokenp = NULL;
+ char *token = NULL;
+ char **matches = NULL;
+
+ tokens = cli_rl_tokenize (text);
+ if (!tokens)
+ return 0;
+
+ word = &state->tree.root;
+
+ for (tokenp = tokens; (token = *tokenp); tokenp++) {
+ if (!*(tokenp+1)) {
+ /* last word */
+ break;
+ }
+
+ next = cli_cmd_nextword (word, token);
+ word = next;
+ if (!word)
+ break;
+ }
+
+ if (!word)
+ goto out;
+
+ matches = cli_rl_get_matches (state, word, token);
+
+ state->matches = matches;
+ state->matchesp = matches;
+
+out:
+ cli_cmd_tokens_destroy (tokens);
+ return 0;
+}
+
+
+int
+cli_rl_autocomplete_cleanup (struct cli_state *state)
+{
+ if (state->matches)
+ cli_cmd_tokens_destroy (state->matches);
+
+ state->matches = NULL;
+ state->matchesp = NULL;
+
+ return 0;
+}
+
+
+char **
+cli_rl_autocomplete (const char *text, int start, int end)
+{
+ struct cli_state *state = NULL;
+ char **matches = NULL;
+ char save = 0;
+
+ state = global_state;
+
+ /* hack to make the autocompletion code neater */
+ /* fake it as though the cursor is at the end of line */
+
+ save = rl_line_buffer[rl_point];
+ rl_line_buffer[rl_point] = 0;
+
+ cli_rl_autocomplete_prepare (state, rl_line_buffer);
+
+ matches = rl_completion_matches (text, cli_rl_autocomplete_entry);
+
+ cli_rl_autocomplete_cleanup (state);
+
+ rl_line_buffer[rl_point] = save;
+
+ return matches;
+}
+
+
+static char *
+complete_none (const char *txt, int times)
+{
+ return NULL;
+}
+
+
+int
+cli_rl_enable (struct cli_state *state)
+{
+ int ret = 0;
+
+ rl_pre_input_hook = NULL;
+ rl_attempted_completion_function = cli_rl_autocomplete;
+ rl_completion_entry_function = complete_none;
+
+ ret = event_register (state->ctx->event_pool, 0, cli_rl_stdin, state,
+ 1, 0);
+ if (ret == -1)
+ goto out;
+
+ state->rl_enabled = 1;
+ rl_callback_handler_install (state->prompt, cli_rl_process_line);
+
+out:
+ return state->rl_enabled;
+}
+
+#else /* HAVE_READLINE */
+
+int
+cli_rl_enable (struct cli_state *state)
+{
+ return 0;
+}
+
+#endif /* HAVE_READLINE */
diff --git a/cli/src/cli.c b/cli/src/cli.c
new file mode 100644
index 000000000..970db712f
--- /dev/null
+++ b/cli/src/cli.c
@@ -0,0 +1,467 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <netdb.h>
+#include <signal.h>
+#include <libgen.h>
+
+#include <sys/utsname.h>
+
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <semaphore.h>
+#include <errno.h>
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#ifdef HAVE_MALLOC_STATS
+#ifdef DEBUG
+#include <mcheck.h>
+#endif
+#endif
+
+#include "cli.h"
+#include "cli-cmd.h"
+#include "cli-mem-types.h"
+
+#include "xlator.h"
+#include "glusterfs.h"
+#include "compat.h"
+#include "logging.h"
+#include "dict.h"
+#include "list.h"
+#include "timer.h"
+#include "stack.h"
+#include "revision.h"
+#include "common-utils.h"
+#include "event.h"
+#include "globals.h"
+#include "syscall.h"
+
+#include <fnmatch.h>
+
+/* using argp for command line parsing */
+static char gf_doc[] = "";
+
+static char argp_doc[] = "COMMAND [PARAM ...]";
+
+const char *argp_program_version = "" \
+ PACKAGE_NAME" "PACKAGE_VERSION" built on "__DATE__" "__TIME__ \
+ "\nRepository revision: " GLUSTERFS_REPOSITORY_REVISION "\n" \
+ "Copyright (c) 2006-2010 Gluster Inc. " \
+ "<http://www.gluster.com>\n" \
+ "GlusterFS comes with ABSOLUTELY NO WARRANTY.\n" \
+ "You may redistribute copies of GlusterFS under the terms of "\
+ "the GNU General Public License.";
+
+const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
+
+static struct argp_option gf_options[] = {
+ {0, 0, 0, 0, "Basic options:"},
+ {"debug", ARGP_DEBUG_KEY, 0, 0,
+ "Process runs in foreground and logs to console"},
+ {0, }
+};
+
+struct rpc_clnt *global_rpc;
+
+rpc_clnt_prog_t *cli_rpc_prog;
+
+
+extern struct rpc_clnt_program cli3_1_prog;
+
+static error_t
+parse_opts (int key, char *arg, struct argp_state *argp_state)
+{
+ struct cli_state *state = NULL;
+ char **argv = NULL;
+
+ state = argp_state->input;
+
+ switch (key) {
+ case ARGP_DEBUG_KEY:
+ break;
+ case ARGP_KEY_ARG:
+ if (!state->argc) {
+ argv = calloc (state->argc + 2,
+ sizeof (*state->argv));
+ } else {
+ argv = realloc (state->argv, (state->argc + 2) *
+ sizeof (*state->argv));
+ }
+ if (!argv)
+ return -1;
+
+ state->argv = argv;
+
+ argv[state->argc] = strdup (arg);
+ if (!argv[state->argc])
+ return -1;
+ state->argc++;
+ argv[state->argc] = NULL;
+
+ break;
+ }
+
+ return 0;
+}
+
+
+static char *
+generate_uuid ()
+{
+ char tmp_str[1024] = {0,};
+ char hostname[256] = {0,};
+ struct timeval tv = {0,};
+ struct tm now = {0, };
+ char now_str[32];
+
+ if (gettimeofday (&tv, NULL) == -1) {
+ gf_log ("glusterfsd", GF_LOG_ERROR,
+ "gettimeofday: failed %s",
+ strerror (errno));
+ }
+
+ if (gethostname (hostname, 256) == -1) {
+ gf_log ("glusterfsd", GF_LOG_ERROR,
+ "gethostname: failed %s",
+ strerror (errno));
+ }
+
+ localtime_r (&tv.tv_sec, &now);
+ strftime (now_str, 32, "%Y/%m/%d-%H:%M:%S", &now);
+ snprintf (tmp_str, 1024, "%s-%d-%s:%"
+#ifdef GF_DARWIN_HOST_OS
+ PRId32,
+#else
+ "ld",
+#endif
+ hostname, getpid(), now_str, tv.tv_usec);
+
+ return gf_strdup (tmp_str);
+}
+
+static int
+glusterfs_ctx_defaults_init (glusterfs_ctx_t *ctx)
+{
+ cmd_args_t *cmd_args = NULL;
+ struct rlimit lim = {0, };
+ call_pool_t *pool = NULL;
+
+ xlator_mem_acct_init (THIS, cli_mt_end);
+
+ ctx->process_uuid = generate_uuid ();
+ if (!ctx->process_uuid)
+ return -1;
+
+ ctx->page_size = 128 * GF_UNIT_KB;
+
+ ctx->iobuf_pool = iobuf_pool_new (8 * GF_UNIT_MB, ctx->page_size);
+ if (!ctx->iobuf_pool)
+ return -1;
+
+ ctx->event_pool = event_pool_new (DEFAULT_EVENT_POOL_SIZE);
+ if (!ctx->event_pool)
+ return -1;
+
+ pool = GF_CALLOC (1, sizeof (call_pool_t),
+ cli_mt_call_pool_t);
+ if (!pool)
+ return -1;
+ INIT_LIST_HEAD (&pool->all_frames);
+ LOCK_INIT (&pool->lock);
+ ctx->pool = pool;
+
+ pthread_mutex_init (&(ctx->lock), NULL);
+
+ cmd_args = &ctx->cmd_args;
+
+ /* parsing command line arguments */
+ cmd_args->log_file = "/dev/stderr";
+ cmd_args->log_level = GF_LOG_NORMAL;
+
+ INIT_LIST_HEAD (&cmd_args->xlator_options);
+
+ lim.rlim_cur = RLIM_INFINITY;
+ lim.rlim_max = RLIM_INFINITY;
+ setrlimit (RLIMIT_CORE, &lim);
+
+ return 0;
+}
+
+
+static int
+logging_init (glusterfs_ctx_t *ctx)
+{
+ cmd_args_t *cmd_args = NULL;
+
+ cmd_args = &ctx->cmd_args;
+
+ if (gf_log_init (cmd_args->log_file) == -1) {
+ fprintf (stderr,
+ "failed to open logfile %s. exiting\n",
+ cmd_args->log_file);
+ return -1;
+ }
+
+ gf_log_set_loglevel (cmd_args->log_level);
+
+ return 0;
+}
+
+int
+cli_submit_request (void *req, call_frame_t *frame,
+ rpc_clnt_prog_t *prog,
+ int procnum, struct iobref *iobref,
+ cli_serialize_t sfunc, xlator_t *this,
+ fop_cbk_fn_t cbkfn)
+{
+ int ret = -1;
+ int count = 0;
+ char start_ping = 0;
+ struct iovec iov = {0, };
+ struct iobuf *iobuf = NULL;
+ char new_iobref = 0;
+
+ GF_ASSERT (this);
+
+ iobuf = iobuf_get (this->ctx->iobuf_pool);
+ if (!iobuf) {
+ goto out;
+ };
+
+ if (!iobref) {
+ iobref = iobref_new ();
+ if (!iobref) {
+ goto out;
+ }
+
+ new_iobref = 1;
+ }
+
+ iobref_add (iobref, iobuf);
+
+ iov.iov_base = iobuf->ptr;
+ iov.iov_len = 128 * GF_UNIT_KB;
+
+
+ /* Create the xdr payload */
+ if (req && sfunc) {
+ ret = sfunc (iov, req);
+ if (ret == -1) {
+ goto out;
+ }
+ iov.iov_len = ret;
+ count = 1;
+ }
+
+ /* Send the msg */
+ ret = rpc_clnt_submit (global_rpc, prog, procnum, cbkfn,
+ &iov, count,
+ NULL, 0, iobref, frame);
+
+ if (ret == 0) {
+ pthread_mutex_lock (&global_rpc->conn.lock);
+ {
+ if (!global_rpc->conn.ping_started) {
+ start_ping = 1;
+ }
+ }
+ pthread_mutex_unlock (&global_rpc->conn.lock);
+ }
+
+ if (start_ping)
+ //client_start_ping ((void *) this);
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int
+parse_cmdline (int argc, char *argv[], struct cli_state *state)
+{
+ int ret = 0;
+ struct argp argp = { 0,};
+
+ argp.options = gf_options;
+ argp.parser = parse_opts;
+ argp.args_doc = argp_doc;
+ argp.doc = gf_doc;
+
+ ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, NULL, state);
+
+ return ret;
+}
+
+
+int
+cli_cmd_tree_init (struct cli_cmd_tree *tree)
+{
+ struct cli_cmd_word *root = NULL;
+ int ret = 0;
+
+ root = &tree->root;
+ root->tree = tree;
+
+ return ret;
+}
+
+
+int
+cli_state_init (struct cli_state *state)
+{
+ struct cli_cmd_tree *tree = NULL;
+ int ret = 0;
+
+ tree = &state->tree;
+ tree->state = state;
+
+ ret = cli_cmd_tree_init (tree);
+
+ return ret;
+}
+
+
+int
+cli_out (const char *fmt, ...)
+{
+ struct cli_state *state = NULL;
+ va_list ap;
+
+ state = global_state;
+
+ va_start (ap, fmt);
+
+#ifdef HAVE_READLINE
+ if (state->rl_enabled && !state->rl_processing)
+ return cli_rl_out(state, fmt, ap);
+#endif
+
+ return vprintf (fmt, ap);
+}
+
+struct rpc_clnt *
+cli_rpc_init (struct cli_state *state)
+{
+ struct rpc_clnt *rpc = NULL;
+ struct rpc_clnt_config rpc_cfg = {0,};
+ dict_t *options = NULL;
+ int ret = -1;
+
+ rpc_cfg.remote_host = "localhost";
+ rpc_cfg.remote_port = CLI_GLUSTERD_PORT;
+
+ cli_rpc_prog = &cli3_1_prog;
+ options = dict_new ();
+ if (!options)
+ goto out;
+
+ ret = dict_set_str (options, "remote-host", "localhost");
+ if (ret)
+ goto out;
+
+ ret = dict_set_int32 (options, "remote-port", CLI_GLUSTERD_PORT);
+ if (ret)
+ goto out;
+
+ ret = dict_set_str (options, "transport.address-family", "inet");
+ if (ret)
+ goto out;
+
+ rpc = rpc_clnt_init (&rpc_cfg, options, THIS->ctx, THIS->name);
+
+out:
+ return rpc;
+}
+
+struct cli_state *global_state;
+
+int
+main (int argc, char *argv[])
+{
+ struct cli_state state = {0, };
+ int ret = -1;
+ glusterfs_ctx_t *ctx = NULL;
+
+ ret = glusterfs_globals_init ();
+ if (ret)
+ return ret;
+
+ ctx = glusterfs_ctx_get ();
+ if (!ctx)
+ return ENOMEM;
+
+ ret = glusterfs_ctx_defaults_init (ctx);
+ if (ret)
+ goto out;
+
+ ret = cli_state_init (&state);
+ if (ret)
+ goto out;
+
+ state.ctx = ctx;
+ global_state = &state;
+
+ ret = parse_cmdline (argc, argv, &state);
+ if (ret)
+ goto out;
+
+ ret = logging_init (ctx);
+ if (ret)
+ goto out;
+
+ ret = cli_cmds_register (&state);
+ if (ret)
+ goto out;
+
+ ret = cli_input_init (&state);
+ if (ret)
+ goto out;
+
+ global_rpc = cli_rpc_init (&state);
+ if (!global_rpc)
+ goto out;
+
+ ret = event_dispatch (ctx->event_pool);
+
+out:
+// glusterfs_ctx_destroy (ctx);
+
+ return ret;
+}
diff --git a/cli/src/cli.h b/cli/src/cli.h
new file mode 100644
index 000000000..c532babf4
--- /dev/null
+++ b/cli/src/cli.h
@@ -0,0 +1,139 @@
+/*
+ Copyright (c) 2006-2009 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CLI_H__
+#define __CLI_H__
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "rpc-clnt.h"
+#include "glusterfs.h"
+#include "protocol-common.h"
+
+#define DEFAULT_EVENT_POOL_SIZE 16384
+#define CLI_GLUSTERD_PORT 6969
+
+enum argp_option_keys {
+ ARGP_DEBUG_KEY = 133,
+};
+
+struct cli_state;
+struct cli_cmd_word;
+struct cli_cmd_tree;
+
+typedef int (cli_cmd_cbk_t)(struct cli_state *state,
+ struct cli_cmd_word *word,
+ const char **words,
+ int wordcount);
+typedef int (cli_cmd_match_t)(struct cli_cmd_word *word);
+typedef int (cli_cmd_filler_t)(struct cli_cmd_word *word);
+
+struct cli_cmd_word {
+ struct cli_cmd_tree *tree;
+ const char *word;
+ cli_cmd_filler_t *filler;
+ cli_cmd_match_t *match;
+ cli_cmd_cbk_t *cbkfn;
+
+ int nextwords_cnt;
+ struct cli_cmd_word **nextwords;
+};
+
+
+struct cli_cmd_tree {
+ struct cli_state *state;
+ struct cli_cmd_word root;
+};
+
+
+struct cli_state {
+ int argc;
+ char **argv;
+
+ char debug;
+
+ /* for events dispatching */
+ glusterfs_ctx_t *ctx;
+
+ /* registry of known commands */
+ struct cli_cmd_tree tree;
+
+ /* the thread which "executes" the command in non-interactive mode */
+ /* also the thread which reads from stdin in non-readline mode */
+ pthread_t input;
+
+ /* terminal I/O */
+ const char *prompt;
+ int rl_enabled;
+ int rl_processing;
+
+ /* autocompletion state */
+ char **matches;
+ char **matchesp;
+};
+
+
+typedef ssize_t (*cli_serialize_t) (struct iovec outmsg, void *args);
+
+extern struct cli_state *global_state; /* use only in readline callback */
+
+int cli_cmd_register (struct cli_cmd_tree *tree, const char *template,
+ cli_cmd_cbk_t cbk);
+int cli_cmds_register (struct cli_state *state);
+
+int cli_input_init (struct cli_state *state);
+
+int cli_cmd_process (struct cli_state *state, int argc, char *argv[]);
+int cli_cmd_process_line (struct cli_state *state, const char *line);
+
+int cli_rl_enable (struct cli_state *state);
+int cli_rl_out (struct cli_state *state, const char *fmt, va_list ap);
+
+int cli_out (const char *fmt, ...);
+
+int
+cli_submit_request (void *req, call_frame_t *frame,
+ rpc_clnt_prog_t *prog,
+ int procnum, struct iobref *iobref,
+ cli_serialize_t sfunc, xlator_t *this,
+ fop_cbk_fn_t cbkfn);
+
+int32_t
+cli_cmd_volume_create_parse (const char **words, int wordcount,
+ dict_t **options);
+
+int32_t
+cli_cmd_volume_set_parse (const char **words, int wordcount,
+ dict_t **options);
+
+int32_t
+cli_cmd_volume_add_brick_parse (const char **words, int wordcount,
+ dict_t **options);
+
+int32_t
+cli_cmd_volume_remove_brick_parse (const char **words, int wordcount,
+ dict_t **options);
+
+int32_t
+cli_cmd_volume_replace_brick_parse (const char **words, int wordcount,
+ dict_t **options);
+#endif /* __CLI_H__ */
diff --git a/cli/src/cli3_1-cops.c b/cli/src/cli3_1-cops.c
new file mode 100644
index 000000000..a3d5ddd89
--- /dev/null
+++ b/cli/src/cli3_1-cops.c
@@ -0,0 +1,805 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "cli.h"
+#include "gluster1.h"
+#include "cli-xdr.h"
+#include "compat-errno.h"
+#include "protocol-common.h"
+#include "cli-cmd.h"
+#include <sys/uio.h>
+
+extern rpc_clnt_prog_t *cli_rpc_prog;
+
+int
+gf_cli3_1_probe_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_probe_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_probe_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ //rsp.op_ret = -1;
+ //rsp.op_errno = EINVAL;
+ goto out;
+ }
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to probe");
+ cli_out ("Probe %s", (rsp.op_ret) ? "Unsuccessful": "Successful");
+
+ cli_cmd_broadcast_response ();
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int
+gf_cli3_1_create_volume_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_create_vol_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_create_vol_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to create volume");
+ cli_out ("Create Volume %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int
+gf_cli3_1_delete_volume_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_delete_vol_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_delete_vol_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to delete volume");
+ cli_out ("Delete Volume %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int
+gf_cli3_1_start_volume_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_start_vol_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_start_vol_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to start volume");
+ cli_out ("Start Volume %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int
+gf_cli3_1_stop_volume_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_stop_vol_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_stop_vol_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to stop volume");
+ cli_out ("Delete Volume %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int
+gf_cli3_1_defrag_volume_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_defrag_vol_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_defrag_vol_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to probe");
+ cli_out ("Defrag Volume %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int
+gf_cli3_1_rename_volume_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_rename_vol_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_rename_vol_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to probe");
+ cli_out ("Rename Volume %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int
+gf_cli3_1_set_volume_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_set_vol_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_set_vol_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to set");
+ cli_out ("Set Volume %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int
+gf_cli3_1_add_brick_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_add_brick_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_add_brick_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to add brick");
+ cli_out ("Add Brick %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+
+int
+gf_cli3_1_remove_brick_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_remove_brick_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_remove_brick_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to remove brick");
+ cli_out ("Remove Brick %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+
+int
+gf_cli3_1_replace_brick_cbk (struct rpc_req *req, struct iovec *iov,
+ int count, void *myframe)
+{
+ gf1_cli_replace_brick_rsp rsp = {0,};
+ int ret = 0;
+
+ if (-1 == req->rpc_status) {
+ goto out;
+ }
+
+ ret = gf_xdr_to_cli_replace_brick_req (*iov, &rsp);
+ if (ret < 0) {
+ gf_log ("", GF_LOG_ERROR, "error");
+ goto out;
+ }
+
+
+ gf_log ("cli", GF_LOG_NORMAL, "Received resp to replace brick");
+ cli_out ("Replace Brick %s", (rsp.op_ret) ? "Unsuccessful":
+ "Successful");
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+int32_t
+gf_cli3_1_probe (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_probe_req req = {0,};
+ int ret = 0;
+ char *hostname = NULL;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ hostname = data;
+
+ req.hostname = hostname;
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_PROBE, NULL, gf_xdr_from_cli_probe_req,
+ this, gf_cli3_1_probe_cbk);
+
+ if (!ret) {
+ //ret = cli_cmd_await_response ();
+ }
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+ return ret;
+}
+
+int32_t
+gf_cli3_1_create_volume (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_create_vol_req req = {0,};
+ int ret = 0;
+ dict_t *dict = NULL;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ dict = data;
+
+ ret = dict_get_str (dict, "volname", &req.volname);
+
+ if (ret)
+ goto out;
+
+ ret = dict_get_int32 (dict, "type", (int32_t *)&req.type);
+
+ if (ret)
+ goto out;
+
+ ret = dict_get_int32 (dict, "count", &req.count);
+
+ if (ret)
+ goto out;
+
+ ret = dict_allocate_and_serialize (dict,
+ &req.bricks.bricks_val,
+ (size_t *)&req.bricks.bricks_len);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "failed to get serialized length of dict");
+ goto out;
+ }
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_CREATE_VOLUME, NULL,
+ gf_xdr_from_cli_create_vol_req,
+ this, gf_cli3_1_create_volume_cbk);
+
+ if (!ret) {
+ //ret = cli_cmd_await_response ();
+ }
+
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ if (req.bricks.bricks_val) {
+ GF_FREE (req.bricks.bricks_val);
+ }
+
+ return ret;
+}
+
+int32_t
+gf_cli3_1_delete_volume (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_delete_vol_req req = {0,};
+ int ret = 0;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ req.volname = data;
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_DELETE_VOLUME, NULL,
+ gf_xdr_from_cli_delete_vol_req,
+ this, gf_cli3_1_delete_volume_cbk);
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ return ret;
+}
+
+int32_t
+gf_cli3_1_start_volume (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_start_vol_req req = {0,};
+ int ret = 0;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ req.volname = data;
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_START_VOLUME, NULL,
+ gf_xdr_from_cli_start_vol_req,
+ this, gf_cli3_1_start_volume_cbk);
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ return ret;
+}
+
+int32_t
+gf_cli3_1_stop_volume (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_stop_vol_req req = {0,};
+ int ret = 0;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ req.volname = data;
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_STOP_VOLUME, NULL,
+ gf_xdr_from_cli_stop_vol_req,
+ this, gf_cli3_1_stop_volume_cbk);
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ return ret;
+}
+
+int32_t
+gf_cli3_1_defrag_volume (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_defrag_vol_req req = {0,};
+ int ret = 0;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ req.volname = data;
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_DEFRAG_VOLUME, NULL,
+ gf_xdr_from_cli_defrag_vol_req,
+ this, gf_cli3_1_defrag_volume_cbk);
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ return ret;
+}
+
+int32_t
+gf_cli3_1_rename_volume (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_rename_vol_req req = {0,};
+ int ret = 0;
+ dict_t *dict = NULL;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ dict = data;
+
+ ret = dict_get_str (dict, "old-volname", &req.old_volname);
+
+ if (ret)
+ goto out;
+
+ ret = dict_get_str (dict, "new-volname", &req.new_volname);
+
+ if (ret)
+ goto out;
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_RENAME_VOLUME, NULL,
+ gf_xdr_from_cli_rename_vol_req,
+ this, gf_cli3_1_rename_volume_cbk);
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ return ret;
+}
+
+int32_t
+gf_cli3_1_set_volume (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_set_vol_req req = {0,};
+ int ret = 0;
+ dict_t *dict = NULL;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ dict = data;
+
+ ret = dict_get_str (dict, "volname", &req.volname);
+
+ if (ret)
+ goto out;
+
+ ret = dict_allocate_and_serialize (dict,
+ &req.dict.dict_val,
+ (size_t *)&req.dict.dict_len);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "failed to get serialized length of dict");
+ goto out;
+ }
+
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_SET_VOLUME, NULL,
+ gf_xdr_from_cli_set_vol_req,
+ this, gf_cli3_1_set_volume_cbk);
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ return ret;
+}
+
+int32_t
+gf_cli3_1_add_brick (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_add_brick_req req = {0,};
+ int ret = 0;
+ dict_t *dict = NULL;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ dict = data;
+
+ ret = dict_get_str (dict, "volname", &req.volname);
+
+ if (ret)
+ goto out;
+
+ ret = dict_get_int32 (dict, "type", (int32_t *)&req.type);
+
+ if (ret)
+ goto out;
+
+ ret = dict_get_int32 (dict, "count", &req.count);
+
+ if (ret)
+ goto out;
+
+ ret = dict_allocate_and_serialize (dict,
+ &req.bricks.bricks_val,
+ (size_t *)&req.bricks.bricks_len);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "failed to get serialized length of dict");
+ goto out;
+ }
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_ADD_BRICK, NULL,
+ gf_xdr_from_cli_add_brick_req,
+ this, gf_cli3_1_add_brick_cbk);
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ if (req.bricks.bricks_val) {
+ GF_FREE (req.bricks.bricks_val);
+ }
+
+ return ret;
+}
+
+int32_t
+gf_cli3_1_remove_brick (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_remove_brick_req req = {0,};
+ int ret = 0;
+ dict_t *dict = NULL;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ dict = data;
+
+ ret = dict_get_str (dict, "volname", &req.volname);
+
+ if (ret)
+ goto out;
+
+ ret = dict_get_int32 (dict, "type", (int32_t *)&req.type);
+
+ if (ret)
+ goto out;
+
+ ret = dict_get_int32 (dict, "count", &req.count);
+
+ if (ret)
+ goto out;
+
+ ret = dict_allocate_and_serialize (dict,
+ &req.bricks.bricks_val,
+ (size_t *)&req.bricks.bricks_len);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "failed to get serialized length of dict");
+ goto out;
+ }
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_REMOVE_BRICK, NULL,
+ gf_xdr_from_cli_remove_brick_req,
+ this, gf_cli3_1_remove_brick_cbk);
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ if (req.bricks.bricks_val) {
+ GF_FREE (req.bricks.bricks_val);
+ }
+
+ return ret;
+}
+
+int32_t
+gf_cli3_1_replace_brick (call_frame_t *frame, xlator_t *this,
+ void *data)
+{
+ gf1_cli_replace_brick_req req = {0,};
+ int ret = 0;
+ dict_t *dict = NULL;
+ char *src_brick = NULL;
+ char *dst_brick = NULL;
+
+ if (!frame || !this || !data) {
+ ret = -1;
+ goto out;
+ }
+
+ dict = data;
+
+ ret = dict_get_str (dict, "volname", &req.volname);
+
+ if (ret)
+ goto out;
+
+ ret = dict_get_int32 (dict, "operation", (int32_t *)&req.op);
+
+ if (ret)
+ goto out;
+
+ if (GF_REPLACE_OP_START == req.op) {
+ ret = dict_get_str (dict, "src-brick", &src_brick);
+
+ if (ret)
+ goto out;
+
+ req.src_brick.src_brick_len = strlen (src_brick);
+ req.src_brick.src_brick_val = src_brick;
+
+ ret = dict_get_str (dict, "src-brick", &dst_brick);
+
+ if (ret)
+ goto out;
+
+ req.dst_brick.dst_brick_len = strlen (dst_brick);
+ req.dst_brick.dst_brick_val = dst_brick;
+ }
+
+ ret = cli_submit_request (&req, frame, cli_rpc_prog,
+ GD_MGMT_CLI_REPLACE_BRICK, NULL,
+ gf_xdr_from_cli_replace_brick_req,
+ this, gf_cli3_1_replace_brick_cbk);
+
+out:
+ gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret);
+
+ if (req.src_brick.src_brick_val) {
+ GF_FREE (req.src_brick.src_brick_val);
+ }
+
+ if (req.dst_brick.dst_brick_val) {
+ GF_FREE (req.dst_brick.dst_brick_val);
+ }
+
+ return ret;
+}
+
+struct rpc_clnt_procedure gluster3_1_cli_actors[GF1_CLI_MAXVALUE] = {
+ [GF1_CLI_NULL] = {"NULL", NULL },
+ [GF1_CLI_PROBE] = { "PROBE_QUERY", gf_cli3_1_probe},
+ [GF1_CLI_CREATE_VOLUME] = {"CREATE_VOLUME", gf_cli3_1_create_volume},
+ [GF1_CLI_DELETE_VOLUME] = {"DELETE_VOLUME", gf_cli3_1_delete_volume},
+ [GF1_CLI_START_VOLUME] = {"START_VOLUME", gf_cli3_1_start_volume},
+ [GF1_CLI_STOP_VOLUME] = {"STOP_VOLUME", gf_cli3_1_stop_volume},
+ [GF1_CLI_RENAME_VOLUME] = {"RENAME_VOLUME", gf_cli3_1_rename_volume},
+ [GF1_CLI_DEFRAG_VOLUME] = {"DEFRAG_VOLUME", gf_cli3_1_defrag_volume},
+ [GF1_CLI_SET_VOLUME] = {"SET_VOLUME", gf_cli3_1_set_volume},
+ [GF1_CLI_ADD_BRICK] = {"ADD_BRICK", gf_cli3_1_add_brick},
+ [GF1_CLI_REMOVE_BRICK] = {"REMOVE_BRICK", gf_cli3_1_remove_brick},
+ [GF1_CLI_REPLACE_BRICK] = {"REPLACE_BRICK", gf_cli3_1_replace_brick},
+};
+
+struct rpc_clnt_program cli3_1_prog = {
+ .progname = "CLI 3.1",
+ .prognum = GLUSTER3_1_CLI_PROGRAM,
+ .progver = GLUSTER3_1_CLI_VERSION,
+ .proctable = gluster3_1_cli_actors,
+ .numproc = GLUSTER3_1_CLI_PROCCNT,
+};
diff --git a/cli/src/input.c b/cli/src/input.c
new file mode 100644
index 000000000..62bd8c406
--- /dev/null
+++ b/cli/src/input.c
@@ -0,0 +1,98 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "cli.h"
+#include "cli-mem-types.h"
+
+#define CMDBUFSIZ 1024
+
+#define cli_out(fmt...) fprintf (stdout, ##fmt)
+
+void *
+cli_batch (void *d)
+{
+ struct cli_state *state = NULL;
+ int ret = 0;
+
+ state = d;
+
+ ret = cli_cmd_process (state, state->argc, state->argv);
+ exit (ret);
+
+ return NULL;
+}
+
+
+void *
+cli_input (void *d)
+{
+ struct cli_state *state = NULL;
+ int ret = 0;
+ char cmdbuf[CMDBUFSIZ];
+ char *cmd = NULL;
+
+ state = d;
+
+ for (;;) {
+ cli_out ("%s", state->prompt);
+
+ cmd = fgets (cmdbuf, CMDBUFSIZ, stdin);
+ if (!cmd)
+ break;
+
+ printf ("processing command: '%s'\n", cmd);
+ ret = cli_cmd_process_line (state, cmd);
+ }
+
+ exit (ret);
+
+ return NULL;
+}
+
+
+int
+cli_input_init (struct cli_state *state)
+{
+ int ret = 0;
+
+ if (state->argc) {
+ ret = pthread_create (&state->input, NULL, cli_batch, state);
+ return ret;
+ }
+
+ state->prompt = "gluster> ";
+
+ cli_rl_enable (state);
+
+ if (!state->rl_enabled)
+ ret = pthread_create (&state->input, NULL, cli_input, state);
+
+ return ret;
+}
diff --git a/cli/src/registry.c b/cli/src/registry.c
new file mode 100644
index 000000000..0ced00787
--- /dev/null
+++ b/cli/src/registry.c
@@ -0,0 +1,386 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "cli.h"
+#include "cli-cmd.h"
+
+
+static int
+__is_spc (int ch)
+{
+ if (ch == ' ')
+ return 1;
+ return 0;
+}
+
+
+static int
+__is_div (int ch)
+{
+ switch (ch) {
+ case '(':
+ case ')':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '|':
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int
+__is_word (const char *word)
+{
+ return (!__is_div (*word) && !__is_spc (*word));
+}
+
+
+int
+counter_char (int ch)
+{
+ switch (ch) {
+ case '(':
+ return ')';
+ case '<':
+ return '>';
+ case '[':
+ return ']';
+ case '{':
+ return '}';
+ }
+
+ return -1;
+}
+
+
+const char *
+__is_template_balanced (const char *template)
+{
+ const char *trav = NULL;
+ int ch = 0;
+
+ trav = template;
+
+ while (*trav) {
+ ch = *trav;
+
+ switch (ch) {
+ case '<':
+ case '(':
+ case '[':
+ trav = __is_template_balanced (trav+1);
+ if (!trav)
+ return NULL;
+ if (*trav != counter_char (ch))
+ return NULL;
+ break;
+ case '>':
+ case ')':
+ case ']':
+ return trav;
+ }
+
+ trav++;
+ }
+
+ return trav;
+}
+
+
+int
+is_template_balanced (const char *template)
+{
+ const char *trav = NULL;
+
+ trav = __is_template_balanced (template);
+ if (!trav || *trav)
+ return -1;
+
+ return 0;
+}
+
+
+int
+cli_cmd_token_count (const char *template)
+{
+ int count = 0;
+ const char *trav = NULL;
+ int is_alnum = 0;
+
+ for (trav = template; *trav; trav++) {
+ switch (*trav) {
+ case '<':
+ case '>':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '|':
+ count++;
+ /* fall through */
+ case ' ':
+ is_alnum = 0;
+ break;
+ default:
+ if (!is_alnum) {
+ is_alnum = 1;
+ count++;
+ }
+ }
+ }
+
+ return count + 1;
+}
+
+
+void
+cli_cmd_tokens_destroy (char **tokens)
+{
+ char **tokenp = NULL;
+
+ if (!tokens)
+ return;
+
+ tokenp = tokens;
+ while (*tokenp) {
+ free (*tokenp);
+ tokenp++;
+ }
+
+ free (tokens);
+}
+
+
+int
+cli_cmd_tokens_fill (char **tokens, const char *template)
+{
+ const char *trav = NULL;
+ char **tokenp = NULL;
+ char *token = NULL;
+ int ret = 0;
+ int ch = 0;
+
+ tokenp = tokens;
+
+ for (trav = template; *trav; trav++) {
+ ch = *trav;
+
+ if (__is_spc (ch))
+ continue;
+
+ if (__is_div (ch)) {
+ token = calloc (2, 1);
+ if (!token)
+ return -1;
+ token[0] = ch;
+
+ *tokenp = token;
+ tokenp++;
+
+ continue;
+ }
+
+ token = strdup (trav);
+ *tokenp = token;
+ tokenp++;
+
+ for (token++; *token; token++) {
+ if (__is_spc (*token) || __is_div (*token)) {
+ *token = 0;
+ break;
+ }
+ trav++;
+ }
+ }
+
+ return ret;
+}
+
+
+char **
+cli_cmd_tokenize (const char *template)
+{
+ char **tokens = NULL;
+ int ret = 0;
+ int count = 0;
+
+ ret = is_template_balanced (template);
+ if (ret)
+ return NULL;
+
+ count = cli_cmd_token_count (template);
+ if (count <= 0)
+ return NULL;
+
+ tokens = calloc (count + 1, sizeof (char *));
+ if (!tokens)
+ return NULL;
+
+ ret = cli_cmd_tokens_fill (tokens, template);
+ if (ret)
+ goto err;
+
+ return tokens;
+err:
+ cli_cmd_tokens_destroy (tokens);
+ return NULL;
+}
+
+
+struct cli_cmd_word *
+cli_cmd_nextword (struct cli_cmd_word *word, const char *token)
+{
+ struct cli_cmd_word *next = NULL;
+ struct cli_cmd_word **trav = NULL;
+ int ret = 0;
+
+ if (!word->nextwords)
+ return NULL;
+
+ for (trav = word->nextwords; (next = *trav); trav++) {
+ if (next->match) {
+// ret = next->match ();
+ } else {
+ ret = strcmp (next->word, token);
+ }
+
+ if (ret == 0)
+ break;
+ }
+
+ return next;
+}
+
+
+struct cli_cmd_word *
+cli_cmd_newword (struct cli_cmd_word *word, const char *token)
+{
+ struct cli_cmd_word **nextwords = NULL;
+ struct cli_cmd_word *nextword = NULL;
+
+ nextwords = realloc (word->nextwords,
+ (word->nextwords_cnt + 2) * sizeof (*nextwords));
+ if (!nextwords)
+ return NULL;
+
+ word->nextwords = nextwords;
+
+ nextword = calloc (1, sizeof (*nextword));
+ if (!nextword)
+ return NULL;
+
+ nextword->word = strdup (token);
+ if (!nextword->word) {
+ free (nextword);
+ return NULL;
+ }
+
+ nextword->tree = word->tree;
+ nextwords[word->nextwords_cnt++] = nextword;
+ nextwords[word->nextwords_cnt] = NULL;
+
+ return nextword;
+}
+
+
+int
+cli_cmd_ingest (struct cli_cmd_tree *tree, char **tokens, cli_cmd_cbk_t *cbkfn)
+{
+ int ret = 0;
+ char **tokenp = NULL;
+ char *token = NULL;
+ struct cli_cmd_word *word = NULL;
+ struct cli_cmd_word *next = NULL;
+
+ word = &tree->root;
+
+ for (tokenp = tokens; (token = *tokenp); tokenp++) {
+ if (!__is_word (token))
+ break;
+
+ next = cli_cmd_nextword (word, token);
+ if (!next)
+ next = cli_cmd_newword (word, token);
+
+ word = next;
+ if (!word)
+ break;
+ }
+
+ if (!word)
+ return -1;
+
+ if (word->cbkfn) {
+ /* warning - command already registered */
+ }
+
+ word->cbkfn = cbkfn;
+
+ /* end of static strings in command template */
+
+ /* TODO: autocompletion beyond this point is just "nice to have" */
+
+ return ret;
+}
+
+
+int
+cli_cmd_register (struct cli_cmd_tree *tree, const char *template,
+ cli_cmd_cbk_t cbk)
+{
+ char **tokens = NULL;
+ int ret = 0;
+
+ if (!template)
+ return -1;
+
+ tokens = cli_cmd_tokenize (template);
+ if (!tokens)
+ return -1;
+
+ ret = cli_cmd_ingest (tree, tokens, cbk);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ if (tokens)
+ cli_cmd_tokens_destroy (tokens);
+
+ return ret;
+}
+