/* Copyright (c) 2010-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include #include "cli.h" #include "cli-cmd.h" #include "cli-mem-types.h" #include "protocol-common.h" #include static int cmd_done; static int cmd_sent; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t conn = PTHREAD_COND_INITIALIZER; static pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER; int cli_op_ret = 0; static gf_boolean_t connected = _gf_false; static unsigned cli_cmd_needs_connection(struct cli_cmd_word *word) { if (!strcasecmp("quit", word->word)) return 0; if (!strcasecmp("help", word->word)) return 0; if (!strcasecmp("getwd", word->word)) return 1; if (!strcasecmp("exit", word->word)) return 0; return cli_default_conn_timeout; } int cli_cmd_status_reset(void) { int ret = 0; ret = cli_cmd_lock(); { if (ret == 0) { cmd_sent = 0; cmd_done = 0; } } ret = cli_cmd_unlock(); return ret; } int cli_cmd_sent_status_get(int *status) { int ret = 0; GF_ASSERT(status); ret = cli_cmd_lock(); { if (ret == 0) *status = cmd_sent; } ret = cli_cmd_unlock(); return ret; } 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; if (!argc) return 0; 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); usage(); return -1; } if (!word->cbkfn) { cli_out("unrecognized command\n"); usage(); return -1; } if (strcmp(word->word, "help") == 0) goto callback; state->await_connected = cli_cmd_needs_connection(word); ret = cli_cmd_await_connected(state->await_connected); if (ret) { cli_out( "Connection failed. Please check if gluster " "daemon is operational."); gf_log("", GF_LOG_INFO, "Exiting with: %d", ret); exit(ret); } callback: ret = word->cbkfn(state, word, (const char **)argv, argc); (void)cli_cmd_status_reset(); 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: 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; ret = cli_cmd_system_register(state); if (ret) goto out; ret = cli_cmd_misc_register(state); if (ret) goto out; ret = cli_cmd_snapshot_register(state); if (ret) goto out; ret = cli_cmd_global_register(state); if (ret) goto out; out: return ret; } int cli_cmd_lock() { pthread_mutex_lock(&cond_mutex); return 0; } int cli_cmd_unlock() { pthread_mutex_unlock(&cond_mutex); return 0; } static void seconds_from_now(unsigned secs, struct timespec *ts) { struct timeval tv = { 0, }; gettimeofday(&tv, NULL); ts->tv_sec = tv.tv_sec + secs; ts->tv_nsec = tv.tv_usec * 1000; } int cli_cmd_await_response(unsigned time) { struct timespec ts = { 0, }; int ret = 0; cli_op_ret = -1; seconds_from_now(time, &ts); while (!cmd_done && !ret) { ret = pthread_cond_timedwait(&cond, &cond_mutex, &ts); } if (!cmd_done) { if (ret == ETIMEDOUT) cli_out("Error : Request timed out"); else cli_out("Error : Command returned with error code:%d", ret); } cmd_done = 0; return cli_op_ret; } /* This function must be called _only_ after all actions associated with * command processing is complete. Otherwise, gluster process may exit before * reporting results to stdout/stderr. */ int cli_cmd_broadcast_response(int32_t status) { pthread_mutex_lock(&cond_mutex); { if (!cmd_sent) goto out; cmd_done = 1; cli_op_ret = status; pthread_cond_broadcast(&cond); } out: pthread_mutex_unlock(&cond_mutex); return 0; } int32_t cli_cmd_await_connected(unsigned conn_timo) { int32_t ret = 0; struct timespec ts = { 0, }; if (!conn_timo) return 0; pthread_mutex_lock(&conn_mutex); { seconds_from_now(conn_timo, &ts); while (!connected && !ret) { ret = pthread_cond_timedwait(&conn, &conn_mutex, &ts); } } pthread_mutex_unlock(&conn_mutex); return ret; } int32_t cli_cmd_broadcast_connected(gf_boolean_t status) { pthread_mutex_lock(&conn_mutex); { connected = status; pthread_cond_broadcast(&conn); } pthread_mutex_unlock(&conn_mutex); return 0; } gf_boolean_t cli_cmd_connected(void) { gf_boolean_t status; pthread_mutex_lock(&conn_mutex); { status = connected; } pthread_mutex_unlock(&conn_mutex); return status; } int cli_cmd_submit(struct rpc_clnt *rpc, 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; unsigned timeout = 0; if ((GLUSTER_CLI_PROFILE_VOLUME == procnum) || (GLUSTER_CLI_HEAL_VOLUME == procnum) || (GLUSTER_CLI_GANESHA == procnum)) timeout = cli_ten_minutes_timeout; else timeout = cli_default_conn_timeout; cli_cmd_lock(); cmd_sent = 0; ret = cli_submit_request(rpc, req, frame, prog, procnum, NULL, this, cbkfn, xdrproc); if (!ret) { cmd_sent = 1; ret = cli_cmd_await_response(timeout); } cli_cmd_unlock(); gf_log("cli", GF_LOG_DEBUG, "Returning %d", ret); return ret; } int cli_cmd_pattern_cmp(void *a, void *b) { struct cli_cmd *ia = NULL; struct cli_cmd *ib = NULL; int ret = 0; ia = a; ib = b; if (strcmp(ia->pattern, ib->pattern) > 0) ret = 1; else if (strcmp(ia->pattern, ib->pattern) < 0) ret = -1; else ret = 0; return ret; } void cli_cmd_sort(struct cli_cmd *cmd, int count) { gf_array_insertionsort(cmd, 1, count - 2, sizeof(struct cli_cmd), cli_cmd_pattern_cmp); }