/* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU General Public License v.2. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "tools.h" #include "lvm2cmdline.h" int main(int argc, char **argv) { /* coverity[os_cmd_sink] intentionally passing argv */ return lvm2_main(argc, argv); } #if defined(READLINE_SUPPORT) || defined(EDITLINE_SUPPORT) # ifdef READLINE_SUPPORT # include # include # ifndef HAVE_RL_COMPLETION_MATCHES # define rl_completion_matches(a, b) completion_matches((char *)a, b) # define rl_completion_func_t CPPFunction # endif # elif defined(EDITLINE_SUPPORT) # include # endif static struct cmdline_context *_cmdline; /* List matching commands */ static char *_list_cmds(const char *text, int state) { static int i = 0; static size_t len = 0; /* Initialise if this is a new completion attempt */ if (!state) { i = 0; len = strlen(text); } for (;i < _cmdline->num_command_names;++i) if (!strncmp(text, _cmdline->command_names[i].name, len)) return strdup(_cmdline->command_names[i].name); return NULL; } /* List matching arguments */ static char *_list_args(const char *text, int state) { static int match_no = 0; static size_t len = 0; static const struct command_name *cname; static const struct command_name_args *cna; /* Initialise if this is a new completion attempt */ if (!state) { char *s = rl_line_buffer; int j; match_no = 0; cname = NULL; cna = NULL; len = strlen(text); /* Find start of first word in line buffer */ while (isspace(*s)) s++; /* Look for word in list of command names */ for (j = 0; j < _cmdline->num_command_names; j++) { const char *p; char *q = s; p = _cmdline->command_names[j].name; while (*p == *q) { p++; q++; } if ((!*p) && *q == ' ') { cname = _cmdline->command_names + j; cna = _cmdline->command_names_args + j; break; } } } if (!cname) return NULL; /* Short form arguments */ if (len < 3) { for (;match_no < cna->num_args; ++match_no) { char s[3]; char c = (_cmdline->opt_names + cna->valid_args[match_no])->short_opt; if (c) { sprintf(s, "-%c", c); if (!strncmp(text, s, len)) return strdup(s); } } } /* Long form arguments */ if (match_no < cna->num_args) match_no = cna->num_args; for (;match_no - cna->num_args < cna->num_args; ++match_no) { const char *l = (_cmdline->opt_names + cna->valid_args[match_no - cna->num_args])->long_opt; if (*(l + 2) && !strncmp(text, l, len)) return strdup(l); } return NULL; } /* Custom completion function */ static char **_completion(const char *text, int start_pos, int end_pos __attribute__((unused))) { char **match_list = NULL; int p = 0; while (isspace((int) *(rl_line_buffer + p))) p++; /* First word should be one of our commands */ if (start_pos == p) match_list = rl_completion_matches(text, _list_cmds); else if (*text == '-') match_list = rl_completion_matches(text, _list_args); /* else other args */ /* No further completion */ rl_attempted_completion_over = 1; return match_list; } static int _hist_file(char *buffer, size_t size) { char *e = getenv("HOME"); if (dm_snprintf(buffer, size, "%s%s.lvm_history", e ? :"", e ? "/" : "") < 0) { log_error("$HOME/.lvm_history: path too long"); return 0; } return 1; } static void _read_history(struct cmd_context *cmd) { char hist_file[PATH_MAX]; if (!_hist_file(hist_file, sizeof(hist_file))) return; if (read_history(hist_file)) log_very_verbose("Couldn't read history from %s.", hist_file); stifle_history(find_config_tree_int(cmd, shell_history_size_CFG, NULL)); } static void _write_history(void) { char hist_file[PATH_MAX]; if (!_hist_file(hist_file, sizeof(hist_file))) return; if (write_history(hist_file)) log_very_verbose("Couldn't write history to %s.", hist_file); } static int _log_shell_command_status(struct cmd_context *cmd, int ret_code) { log_report_t log_state; if (!cmd->cmd_report.log_rh) return 1; log_state = log_get_report_state(); return report_cmdlog(cmd->cmd_report.log_rh, REPORT_OBJECT_CMDLOG_NAME, log_get_report_context_name(log_state.context), log_get_report_object_type_name(log_state.object_type), log_state.object_name, log_state.object_id, log_state.object_group, log_state.object_group_id, ret_code == ECMD_PROCESSED ? REPORT_OBJECT_CMDLOG_SUCCESS : REPORT_OBJECT_CMDLOG_FAILURE, stored_errno(), ret_code); } static void _discard_log_report_content(struct cmd_context *cmd) { if (cmd->cmd_report.log_rh) dm_report_destroy_rows(cmd->cmd_report.log_rh); } int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline) { log_report_t saved_log_report_state = log_get_report_state(); char *orig_command_log_selection = NULL; int is_lastlog_cmd = 0, argc, ret, i; char *input = NULL, *args[MAX_ARGS], **argv; rl_readline_name = "lvm"; rl_attempted_completion_function = (rl_completion_func_t *) _completion; _read_history(cmd); _cmdline = cmdline; cmd->is_interactive = 1; if (!report_format_init(cmd)) return_ECMD_FAILED; orig_command_log_selection = dm_pool_strdup(cmd->libmem, find_config_tree_str(cmd, log_command_log_selection_CFG, NULL)); log_set_report_context(LOG_REPORT_CONTEXT_SHELL); log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_PRE_CMD); while (1) { /* * Note: If we need to output the log report before we get to the dm_report_group_output_and_pop_all * at the end of this loop, like hitting a failure situation before we execute the command itself, * don't forget to directly call dm_report_group_output_and_pop_all, otherwise no log meesage will * appear on output (for output formats other than 'basic'). * * Obviously, you can't output the 'log report' if the error is in initializing or setting * the report itself. In this case, we can only return an error code, but no message. */ report_reset_cmdlog_seqnum(); if (cmd->cmd_report.log_rh) { /* * If previous command was lastlog, reset log report selection to * its original value as set by log/command_log_selection config setting. */ if (is_lastlog_cmd && !dm_report_set_selection(cmd->cmd_report.log_rh, orig_command_log_selection)) log_error("Failed to reset log report selection."); } log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_PRE_CMD); log_set_report(cmd->cmd_report.log_rh); log_set_report_object_name_and_id(NULL, NULL); free(input); input = readline("lvm> "); /* EOF */ if (!input) { _discard_log_report_content(cmd); /* readline sends prompt to stdout */ printf("\n"); break; } /* empty line */ if (!*input) { _discard_log_report_content(cmd); continue; } log_set_report_object_name_and_id(input, NULL); add_history(input); for (i = 0; i < MAX_ARGS; i++) args[i] = NULL; argv = args; if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) { _discard_log_report_content(cmd); log_error("Too many arguments, sorry."); goto report_log; } if (!argc) { _discard_log_report_content(cmd); continue; } if (!strcmp(argv[0], "lvm")) { argv++; argc--; } if (!argc) { _discard_log_report_content(cmd); continue; } log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_CMD); log_set_report_object_name_and_id(argv[0], NULL); is_lastlog_cmd = !strcmp(argv[0], "lastlog"); if (!is_lastlog_cmd) _discard_log_report_content(cmd); if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) { _discard_log_report_content(cmd); remove_history(history_length - 1); log_error("Exiting."); break; } ret = lvm_run_command(cmd, argc, argv); if (ret == ENO_SUCH_CMD) log_error("No such command '%s'. Try 'help'.", argv[0]); if ((ret != ECMD_PROCESSED) && !error_message_produced()) { log_debug(INTERNAL_ERROR "Failed command did not use log_error"); log_error("Command failed with status code %d.", ret); } _write_history(); if (!is_lastlog_cmd) _log_shell_command_status(cmd, ret); report_log: log_set_report(NULL); dm_report_group_output_and_pop_all(cmd->cmd_report.report_group); if (cmd->cmd_report.log_rh && !(dm_report_group_push(cmd->cmd_report.report_group, cmd->cmd_report.log_rh, (void *) cmd->cmd_report.log_name))) { log_set_report(NULL); log_error("Failed to add log report."); break; } } log_restore_report_state(saved_log_report_state); cmd->is_interactive = 0; free(input); if (cmd->cmd_report.report_group) { if (!dm_report_group_destroy(cmd->cmd_report.report_group)) stack; cmd->cmd_report.report_group = NULL; } if (cmd->cmd_report.log_rh) { dm_report_free(cmd->cmd_report.log_rh); cmd->cmd_report.report_group = NULL; } return 0; } #endif /* READLINE_SUPPORT || EDITLINE_SUPPORT */