/* Copyright (c) 2010-2011 Gluster, Inc. 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif #ifdef HAVE_MALLOC_H #include #endif #ifdef HAVE_MALLOC_STATS #ifdef DEBUG #include #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 "call-stub.h" #include #include "xdr-generic.h" extern int connected; /* using argp for command line parsing */ const char *argp_program_version = "" \ PACKAGE_NAME" "PACKAGE_VERSION" built on "__DATE__" "__TIME__ \ "\nRepository revision: " GLUSTERFS_REPOSITORY_REVISION "\n" \ "Copyright (c) 2006-2011 Gluster Inc. " \ "\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 ">"; struct rpc_clnt *global_rpc; rpc_clnt_prog_t *cli_rpc_prog; extern struct rpc_clnt_program cli_prog; 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 (); 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; /* frame_mem_pool size 112 * 64 */ pool->frame_mem_pool = mem_pool_new (call_frame_t, 32); if (!pool->frame_mem_pool) return -1; /* stack_mem_pool size 256 * 128 */ pool->stack_mem_pool = mem_pool_new (call_stack_t, 16); if (!pool->stack_mem_pool) return -1; ctx->stub_mem_pool = mem_pool_new (call_stub_t, 16); if (!ctx->stub_mem_pool) return -1; ctx->dict_pool = mem_pool_new (dict_t, 32); if (!ctx->dict_pool) return -1; ctx->dict_pair_pool = mem_pool_new (data_pair_t, 512); if (!ctx->dict_pair_pool) return -1; ctx->dict_data_pool = mem_pool_new (data_t, 512); if (!ctx->dict_data_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; 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 (struct cli_state *state) { char *log_file = state->log_file ? state->log_file : DEFAULT_CLI_LOG_FILE_DIRECTORY "/cli.log"; if (gf_log_init (log_file) == -1) { fprintf (stderr, "ERROR: failed to open logfile %s\n", log_file); return -1; } /* CLI should not have something to DEBUG after the release, hence defaulting to INFO loglevel */ gf_log_set_loglevel ((state->log_level == -1) ? GF_LOG_INFO : state->log_level); return 0; } int cli_submit_request (void *req, call_frame_t *frame, rpc_clnt_prog_t *prog, int procnum, struct iobref *iobref, xlator_t *this, fop_cbk_fn_t cbkfn, xdrproc_t xdrproc) { int ret = -1; int count = 0; struct iovec iov = {0, }; struct iobuf *iobuf = NULL; char new_iobref = 0; ssize_t xdr_size = 0; GF_ASSERT (this); if (req) { xdr_size = xdr_sizeof (xdrproc, req); iobuf = iobuf_get2 (this->ctx->iobuf_pool, xdr_size); 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 = iobuf_size (iobuf); /* Create the xdr payload */ ret = xdr_serialize_generic (iov, req, xdrproc); 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, NULL, 0, NULL, 0, NULL); ret = 0; out: if (new_iobref) iobref_unref (iobref); if (iobuf) iobuf_unref (iobuf); return ret; } int cli_rpc_notify (struct rpc_clnt *rpc, void *mydata, rpc_clnt_event_t event, void *data) { xlator_t *this = NULL; int ret = 0; this = mydata; switch (event) { case RPC_CLNT_CONNECT: { cli_cmd_broadcast_connected (); gf_log (this->name, GF_LOG_TRACE, "got RPC_CLNT_CONNECT"); break; } case RPC_CLNT_DISCONNECT: { gf_log (this->name, GF_LOG_TRACE, "got RPC_CLNT_DISCONNECT"); connected = 0; if (!global_state->prompt && global_state->await_connected) { ret = 1; cli_out ("Connection failed. Please check if gluster " "daemon is operational."); exit (ret); } break; } default: gf_log (this->name, GF_LOG_TRACE, "got some other RPC event %d", event); ret = 0; break; } return ret; } int cli_opt_parse (char *opt, struct cli_state *state) { char *oarg; if (strcmp (opt, "version") == 0) { puts (argp_program_version); exit (0); } if (strcmp (opt, "xml") == 0) { state->mode |= GLUSTER_MODE_XML; return 0; } oarg = strtail (opt, "mode="); if (oarg) { if (strcmp (oarg, "script") == 0) { state->mode |= GLUSTER_MODE_SCRIPT; return 0; } if (strcmp (oarg, "interactive") == 0) return 0; return -1; } oarg = strtail (opt, "remote-host="); if (oarg) { state->remote_host = oarg; return 0; } oarg = strtail (opt, "log-file="); if (oarg) { state->log_file = oarg; return 0; } oarg = strtail (opt, "log-level="); if (oarg) { state->log_level = glusterd_check_log_level(oarg); if (state->log_level == -1) return -1; return 0; } return -1; } int parse_cmdline (int argc, char *argv[], struct cli_state *state) { int ret = 0; int i = 0; int j = 0; char *opt = NULL; state->argc=argc-1; state->argv=&argv[1]; for (i = 0; i < state->argc; i++) { opt = strtail (state->argv[i], "--"); if (opt) { ret = cli_opt_parse (opt, state); if (ret == -1) { cli_out ("unrecognized option --%s", opt); return ret; } for (j = i; j < state->argc - 1; j++) state->argv[j] = state->argv[j + 1]; state->argc--; /* argv shifted, next check should be at i again */ i--; } } 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; state->remote_host = "localhost"; state->log_level = -1; tree = &state->tree; tree->state = state; ret = cli_cmd_tree_init (tree); return ret; } int cli_usage_out (const char *usage) { GF_ASSERT (usage); GF_ASSERT (usage[0] != '\0'); if (!usage || usage[0] == '\0') return -1; cli_out ("Usage: %s", usage); return 0; } int _cli_out (const char *fmt, ...) { struct cli_state *state = NULL; va_list ap; int ret = 0; 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 ret = vprintf (fmt, ap); printf ("\n"); va_end (ap); return ret; } struct rpc_clnt * cli_rpc_init (struct cli_state *state) { struct rpc_clnt *rpc = NULL; dict_t *options = NULL; int ret = -1; int port = CLI_GLUSTERD_PORT; xlator_t *this = NULL; this = THIS; cli_rpc_prog = &cli_prog; options = dict_new (); if (!options) goto out; ret = dict_set_str (options, "remote-host", state->remote_host); if (ret) goto out; if (state->remote_port) port = state->remote_port; ret = dict_set_int32 (options, "remote-port", port); if (ret) goto out; ret = dict_set_str (options, "transport.address-family", "inet/inet6"); if (ret) goto out; rpc = rpc_clnt_new (options, this->ctx, this->name, 16); if (!rpc) goto out; ret = rpc_clnt_register_notify (rpc, cli_rpc_notify, this); if (ret) { gf_log ("cli", GF_LOG_ERROR, "failed to register notify"); goto out; } rpc_clnt_start (rpc); out: if (ret) { if (rpc) rpc_clnt_unref (rpc); rpc = NULL; } return rpc; } cli_local_t * cli_local_get () { cli_local_t *local = NULL; local = GF_CALLOC (1, sizeof (*local), cli_mt_cli_local_t); return local; } void cli_local_wipe (cli_local_t *local) { if (local) { if (local->get_vol.volname) GF_FREE (local->get_vol.volname); if (local->dict) dict_unref (local->dict); GF_FREE (local); } return; } /* If the path exists use realpath(3) to handle extra slashes and to resolve * symlinks else strip the extra slashes in the path and return */ int cli_canonicalize_path (char *path) { struct stat sb = {0}; int ret = -1; char *tmppath = NULL; char *dir = NULL; char *tmpstr = NULL; int path_len = 0; if (!path) return ret; ret = stat (path, &sb); if (ret == -1) { /* Strip the extra slashes and return */ tmppath = gf_strdup (path); if (tmppath == NULL) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Out of memory."); goto out; } bzero (path, strlen(path)); path[0] = '/'; dir = strtok_r(tmppath, "/", &tmpstr); while (dir) { strncpy ((path + path_len + 1), dir, strlen(dir)); path_len = strlen (path); dir = strtok_r(NULL, "/", &tmpstr); if (dir) strncpy((path + path_len), "/", 1); } if (path_len == 0) path[1] = '\0'; else path[path_len] = '\0'; ret = 0; goto out; } else { tmppath = gf_strdup(path); if (tmppath == NULL) { ret = -1; gf_log ("cli", GF_LOG_ERROR, "Out of memory."); goto out; } if (realpath (tmppath, path) == NULL) { cli_out ("Path manipulation failed: %s", strerror(errno)); gf_log ("cli", GF_LOG_ERROR, "Path manipulation " "failed: %s", strerror(errno)); ret = -1; goto out; } ret = 0; } out: if (tmppath) GF_FREE(tmppath); return ret; } 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 (&state); if (ret) goto out; global_rpc = cli_rpc_init (&state); if (!global_rpc) goto out; ret = cli_cmds_register (&state); if (ret) goto out; ret = cli_cmd_cond_init (); if (ret) goto out; ret = cli_input_init (&state); if (ret) goto out; ret = event_dispatch (ctx->event_pool); out: // glusterfs_ctx_destroy (ctx); return ret; } void cli_print_line (int len) { GF_ASSERT (len > 0); while (len--) printf ("-"); printf ("\n"); }