mirror of
git://sourceware.org/git/lvm2.git
synced 2025-01-03 05:18:29 +03:00
1fde4bf4d0
With patches that will follow, this will make it possible to widen log report coverage when commands are executed from lvm shell so the amount of messages that may end up in stderr/stdout instead of log report are minimized.
295 lines
6.5 KiB
C
295 lines
6.5 KiB
C
/*
|
|
* 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)
|
|
{
|
|
return lvm2_main(argc, argv);
|
|
}
|
|
|
|
#ifdef READLINE_SUPPORT
|
|
|
|
# include <readline/readline.h>
|
|
# include <readline/history.h>
|
|
# ifndef HAVE_RL_COMPLETION_MATCHES
|
|
# define rl_completion_matches(a, b) completion_matches((char *)a, b)
|
|
# define rl_completion_func_t CPPFunction
|
|
# 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);
|
|
}
|
|
|
|
while (i < _cmdline->num_commands)
|
|
if (!strncmp(text, _cmdline->commands[i++].name, len))
|
|
return strdup(_cmdline->commands[i - 1].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 struct command *com;
|
|
|
|
/* Initialise if this is a new completion attempt */
|
|
if (!state) {
|
|
char *s = rl_line_buffer;
|
|
int j;
|
|
|
|
match_no = 0;
|
|
com = NULL;
|
|
len = strlen(text);
|
|
|
|
/* Find start of first word in line buffer */
|
|
while (isspace(*s))
|
|
s++;
|
|
|
|
/* Look for word in list of commands */
|
|
for (j = 0; j < _cmdline->num_commands; j++) {
|
|
const char *p;
|
|
char *q = s;
|
|
|
|
p = _cmdline->commands[j].name;
|
|
while (*p == *q) {
|
|
p++;
|
|
q++;
|
|
}
|
|
if ((!*p) && *q == ' ') {
|
|
com = _cmdline->commands + j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!com)
|
|
return NULL;
|
|
|
|
/* Short form arguments */
|
|
if (len < 3) {
|
|
while (match_no < com->num_args) {
|
|
char s[3];
|
|
char c;
|
|
if (!(c = (_cmdline->arg_props +
|
|
com->valid_args[match_no++])->short_arg))
|
|
continue;
|
|
|
|
sprintf(s, "-%c", c);
|
|
if (!strncmp(text, s, len))
|
|
return strdup(s);
|
|
}
|
|
}
|
|
|
|
/* Long form arguments */
|
|
if (match_no < com->num_args)
|
|
match_no = com->num_args;
|
|
|
|
while (match_no - com->num_args < com->num_args) {
|
|
const char *l;
|
|
l = (_cmdline->arg_props +
|
|
com->valid_args[match_no++ - com->num_args])->long_arg;
|
|
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/.lvm_history", 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);
|
|
}
|
|
|
|
int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
|
|
{
|
|
log_report_t saved_log_report_state = log_get_report_state();
|
|
int is_lastlog_cmd, argc, ret;
|
|
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;
|
|
log_set_report_context(LOG_REPORT_CONTEXT_SHELL);
|
|
log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_CMD);
|
|
|
|
while (1) {
|
|
log_set_report_object_name_and_id(NULL, NULL);
|
|
free(input);
|
|
input = readline("lvm> ");
|
|
|
|
/* EOF */
|
|
if (!input) {
|
|
/* readline sends prompt to stdout */
|
|
printf("\n");
|
|
break;
|
|
}
|
|
|
|
/* empty line */
|
|
if (!*input)
|
|
continue;
|
|
|
|
add_history(input);
|
|
|
|
argv = args;
|
|
|
|
if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
|
|
log_error("Too many arguments, sorry.");
|
|
continue;
|
|
}
|
|
|
|
if (!argc)
|
|
continue;
|
|
|
|
if (!strcmp(argv[0], "lvm")) {
|
|
argv++;
|
|
argc--;
|
|
}
|
|
|
|
if (!argc)
|
|
continue;
|
|
|
|
log_set_report_object_name_and_id(argv[0], NULL);
|
|
|
|
if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
|
|
remove_history(history_length - 1);
|
|
log_error("Exiting.");
|
|
break;
|
|
}
|
|
|
|
is_lastlog_cmd = !strcmp(argv[0], "lastlog");
|
|
|
|
if (cmd->cmd_report.log_rh && !is_lastlog_cmd) {
|
|
/* drop old log report */
|
|
dm_report_free(cmd->cmd_report.log_rh);
|
|
cmd->cmd_report.log_rh = NULL;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
log_restore_report_state(saved_log_report_state);
|
|
cmd->is_interactive = 0;
|
|
|
|
free(input);
|
|
return 0;
|
|
}
|
|
|
|
#endif /* READLINE_SUPPORT */
|