2 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
5 * This file is part of LVM2.
7 * This copyrighted material is made available to anyone wishing to use,
8 * modify, copy, or redistribute it subject to the terms and conditions
9 * of the GNU General Public License v.2.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software Foundation,
13 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 #include "lvm2cmdline.h"
20 int main(int argc
, char **argv
)
22 /* coverity[os_cmd_sink] intentionally passing argv */
23 return lvm2_main(argc
, argv
);
26 #if defined(READLINE_SUPPORT) || defined(EDITLINE_SUPPORT)
28 # ifdef READLINE_SUPPORT
29 # include <readline/readline.h>
30 # include <readline/history.h>
31 # ifndef HAVE_RL_COMPLETION_MATCHES
32 # define rl_completion_matches(a, b) completion_matches((char *)a, b)
33 # define rl_completion_func_t CPPFunction
35 # elif defined(EDITLINE_SUPPORT)
36 # include <editline/readline.h>
39 static struct cmdline_context
*_cmdline
;
41 /* List matching commands */
42 static char *_list_cmds(const char *text
, int state
)
45 static size_t len
= 0;
47 /* Initialise if this is a new completion attempt */
53 for (;i
< _cmdline
->num_command_names
;++i
)
54 if (!strncmp(text
, _cmdline
->command_names
[i
].name
, len
))
55 /* increase position for next iteration */
56 return strdup(_cmdline
->command_names
[i
++].name
);
61 /* List matching arguments */
62 static char *_list_args(const char *text
, int state
)
64 static int match_no
= 0;
65 static size_t len
= 0;
66 static const struct command_name
*cname
;
67 static const struct command_name_args
*cna
;
69 /* Initialise if this is a new completion attempt */
71 char *s
= rl_line_buffer
;
79 /* Find start of first word in line buffer */
83 /* Look for word in list of command names */
84 for (j
= 0; j
< _cmdline
->num_command_names
; j
++) {
88 p
= _cmdline
->command_names
[j
].name
;
93 if ((!*p
) && *q
== ' ') {
94 cname
= _cmdline
->command_names
+ j
;
95 cna
= _cmdline
->command_names_args
+ j
;
104 /* Short form arguments */
106 while (match_no
< cna
->num_args
) {
108 /* increase position for next iteration */
109 char c
= _cmdline
->opt_names
[cna
->valid_args
[match_no
++]].short_opt
;
111 sprintf(s
, "-%c", c
);
112 if (!strncmp(text
, s
, len
))
118 /* Long form arguments */
119 if (match_no
< cna
->num_args
)
120 match_no
= cna
->num_args
;
122 while ((match_no
- cna
->num_args
) < cna
->num_args
) {
123 /* increase position for next iteration */
124 const char *l
= _cmdline
->opt_names
[cna
->valid_args
[match_no
++ - cna
->num_args
]].long_opt
;
125 if (*(l
+ 2) && !strncmp(text
, l
, len
))
132 /* Custom completion function */
133 static char **_completion(const char *text
, int start_pos
,
134 int end_pos
__attribute__((unused
)))
136 char **match_list
= NULL
;
139 while (isspace((int) *(rl_line_buffer
+ p
)))
142 /* First word should be one of our commands */
144 match_list
= rl_completion_matches(text
, _list_cmds
);
146 else if (*text
== '-')
147 match_list
= rl_completion_matches(text
, _list_args
);
148 /* else other args */
150 /* No further completion */
151 rl_attempted_completion_over
= 1;
155 static int _hist_file(char *buffer
, size_t size
)
157 char *e
= getenv("HOME");
159 if (dm_snprintf(buffer
, size
, "%s%s.lvm_history", e
? :"", e
? "/" : "") < 0) {
160 log_error("$HOME/.lvm_history: path too long");
167 static void _read_history(struct cmd_context
*cmd
)
169 char hist_file
[PATH_MAX
];
171 if (!_hist_file(hist_file
, sizeof(hist_file
)))
174 if (read_history(hist_file
))
175 log_very_verbose("Couldn't read history from %s.", hist_file
);
177 stifle_history(find_config_tree_int(cmd
, shell_history_size_CFG
, NULL
));
180 static void _write_history(void)
182 char hist_file
[PATH_MAX
];
184 if (!_hist_file(hist_file
, sizeof(hist_file
)))
187 if (write_history(hist_file
))
188 log_very_verbose("Couldn't write history to %s.", hist_file
);
191 static int _log_shell_command_status(struct cmd_context
*cmd
, int ret_code
)
193 log_report_t log_state
;
195 if (!cmd
->cmd_report
.log_rh
)
198 log_state
= log_get_report_state();
200 return report_cmdlog(cmd
->cmd_report
.log_rh
, REPORT_OBJECT_CMDLOG_NAME
,
201 log_get_report_context_name(log_state
.context
),
202 log_get_report_object_type_name(log_state
.object_type
),
203 log_state
.object_name
, log_state
.object_id
,
204 log_state
.object_group
, log_state
.object_group_id
,
205 ret_code
== ECMD_PROCESSED
? REPORT_OBJECT_CMDLOG_SUCCESS
206 : REPORT_OBJECT_CMDLOG_FAILURE
,
207 stored_errno(), ret_code
);
210 static void _discard_log_report_content(struct cmd_context
*cmd
)
212 if (cmd
->cmd_report
.log_rh
)
213 dm_report_destroy_rows(cmd
->cmd_report
.log_rh
);
216 int lvm_shell(struct cmd_context
*cmd
, struct cmdline_context
*cmdline
)
218 log_report_t saved_log_report_state
= log_get_report_state();
219 char *orig_command_log_selection
= NULL
;
220 int is_lastlog_cmd
= 0, argc
, ret
, i
;
221 char *input
= NULL
, *args
[MAX_ARGS
], **argv
;
223 rl_readline_name
= "lvm";
224 rl_attempted_completion_function
= (rl_completion_func_t
*) _completion
;
229 cmd
->is_interactive
= 1;
231 if (!report_format_init(cmd
))
234 orig_command_log_selection
= dm_pool_strdup(cmd
->libmem
, find_config_tree_str(cmd
, log_command_log_selection_CFG
, NULL
));
235 log_set_report_context(LOG_REPORT_CONTEXT_SHELL
);
236 log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_PRE_CMD
);
240 * Note: If we need to output the log report before we get to the dm_report_group_output_and_pop_all
241 * at the end of this loop, like hitting a failure situation before we execute the command itself,
242 * don't forget to directly call dm_report_group_output_and_pop_all, otherwise no log message will
243 * appear on output (for output formats other than 'basic').
245 * Obviously, you can't output the 'log report' if the error is in initializing or setting
246 * the report itself. In this case, we can only return an error code, but no message.
249 report_reset_cmdlog_seqnum();
250 if (cmd
->cmd_report
.log_rh
) {
252 * If previous command was lastlog, reset log report selection to
253 * its original value as set by log/command_log_selection config setting.
255 if (is_lastlog_cmd
&&
256 !dm_report_set_selection(cmd
->cmd_report
.log_rh
, orig_command_log_selection
))
257 log_error("Failed to reset log report selection.");
260 log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_PRE_CMD
);
261 log_set_report(cmd
->cmd_report
.log_rh
);
262 log_set_report_object_name_and_id(NULL
, NULL
);
265 input
= readline("lvm> ");
269 _discard_log_report_content(cmd
);
270 /* readline sends prompt to stdout */
277 _discard_log_report_content(cmd
);
281 log_set_report_object_name_and_id(input
, NULL
);
285 for (i
= 0; i
< MAX_ARGS
; i
++)
290 if (lvm_split(input
, &argc
, argv
, MAX_ARGS
) == MAX_ARGS
) {
291 _discard_log_report_content(cmd
);
292 log_error("Too many arguments, sorry.");
297 _discard_log_report_content(cmd
);
301 if (!strcmp(argv
[0], "lvm")) {
307 _discard_log_report_content(cmd
);
311 log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_CMD
);
312 log_set_report_object_name_and_id(argv
[0], NULL
);
314 is_lastlog_cmd
= !strcmp(argv
[0], "lastlog");
317 _discard_log_report_content(cmd
);
319 if (!strcmp(argv
[0], "quit") || !strcmp(argv
[0], "exit")) {
320 _discard_log_report_content(cmd
);
321 remove_history(history_length
- 1);
322 log_error("Exiting.");
326 ret
= lvm_run_command(cmd
, argc
, argv
);
327 if (ret
== ENO_SUCH_CMD
)
328 log_error("No such command '%s'. Try 'help'.",
331 if ((ret
!= ECMD_PROCESSED
) && !error_message_produced()) {
332 log_debug(INTERNAL_ERROR
"Failed command did not use log_error");
333 log_error("Command failed with status code %d.", ret
);
338 _log_shell_command_status(cmd
, ret
);
340 log_set_report(NULL
);
341 dm_report_group_output_and_pop_all(cmd
->cmd_report
.report_group
);
343 if (cmd
->cmd_report
.log_rh
&&
344 !(dm_report_group_push(cmd
->cmd_report
.report_group
,
345 cmd
->cmd_report
.log_rh
,
346 (void *) cmd
->cmd_report
.log_name
))) {
347 log_set_report(NULL
);
348 log_error("Failed to add log report.");
353 log_restore_report_state(saved_log_report_state
);
354 cmd
->is_interactive
= 0;
358 if (cmd
->cmd_report
.report_group
) {
359 if (!dm_report_group_destroy(cmd
->cmd_report
.report_group
))
361 cmd
->cmd_report
.report_group
= NULL
;
364 if (cmd
->cmd_report
.log_rh
) {
365 dm_report_free(cmd
->cmd_report
.log_rh
);
366 cmd
->cmd_report
.report_group
= NULL
;
372 #endif /* READLINE_SUPPORT || EDITLINE_SUPPORT */