]> sourceware.org Git - lvm2.git/blob - tools/lvm.c
b0bbdd8f2cb15491af6275c8bb1933cfc848f894
[lvm2.git] / tools / lvm.c
1 /*
2 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
4 *
5 * This file is part of LVM2.
6 *
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.
10 *
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
14 */
15
16 #include "tools.h"
17
18 #include "lvm2cmdline.h"
19
20 int main(int argc, char **argv)
21 {
22 /* coverity[os_cmd_sink] intentionally passing argv */
23 return lvm2_main(argc, argv);
24 }
25
26 #if defined(READLINE_SUPPORT) || defined(EDITLINE_SUPPORT)
27
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
34 # endif
35 # elif defined(EDITLINE_SUPPORT)
36 # include <editline/readline.h>
37 # endif
38
39 static struct cmdline_context *_cmdline;
40
41 /* List matching commands */
42 static char *_list_cmds(const char *text, int state)
43 {
44 static int i = 0;
45 static size_t len = 0;
46
47 /* Initialise if this is a new completion attempt */
48 if (!state) {
49 i = 0;
50 len = strlen(text);
51 }
52
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);
57
58 return NULL;
59 }
60
61 /* List matching arguments */
62 static char *_list_args(const char *text, int state)
63 {
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;
68
69 /* Initialise if this is a new completion attempt */
70 if (!state) {
71 char *s = rl_line_buffer;
72 int j;
73
74 match_no = 0;
75 cname = NULL;
76 cna = NULL;
77 len = strlen(text);
78
79 /* Find start of first word in line buffer */
80 while (isspace(*s))
81 s++;
82
83 /* Look for word in list of command names */
84 for (j = 0; j < _cmdline->num_command_names; j++) {
85 const char *p;
86 char *q = s;
87
88 p = _cmdline->command_names[j].name;
89 while (*p == *q) {
90 p++;
91 q++;
92 }
93 if ((!*p) && *q == ' ') {
94 cname = _cmdline->command_names + j;
95 cna = _cmdline->command_names_args + j;
96 break;
97 }
98 }
99 }
100
101 if (!cname)
102 return NULL;
103
104 /* Short form arguments */
105 if (len < 3) {
106 while (match_no < cna->num_args) {
107 char s[3];
108 /* increase position for next iteration */
109 char c = _cmdline->opt_names[cna->valid_args[match_no++]].short_opt;
110 if (c) {
111 sprintf(s, "-%c", c);
112 if (!strncmp(text, s, len))
113 return strdup(s);
114 }
115 }
116 }
117
118 /* Long form arguments */
119 if (match_no < cna->num_args)
120 match_no = cna->num_args;
121
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))
126 return strdup(l);
127 }
128
129 return NULL;
130 }
131
132 /* Custom completion function */
133 static char **_completion(const char *text, int start_pos,
134 int end_pos __attribute__((unused)))
135 {
136 char **match_list = NULL;
137 int p = 0;
138
139 while (isspace((int) *(rl_line_buffer + p)))
140 p++;
141
142 /* First word should be one of our commands */
143 if (start_pos == p)
144 match_list = rl_completion_matches(text, _list_cmds);
145
146 else if (*text == '-')
147 match_list = rl_completion_matches(text, _list_args);
148 /* else other args */
149
150 /* No further completion */
151 rl_attempted_completion_over = 1;
152 return match_list;
153 }
154
155 static int _hist_file(char *buffer, size_t size)
156 {
157 char *e = getenv("HOME");
158
159 if (dm_snprintf(buffer, size, "%s%s.lvm_history", e ? :"", e ? "/" : "") < 0) {
160 log_error("$HOME/.lvm_history: path too long");
161 return 0;
162 }
163
164 return 1;
165 }
166
167 static void _read_history(struct cmd_context *cmd)
168 {
169 char hist_file[PATH_MAX];
170
171 if (!_hist_file(hist_file, sizeof(hist_file)))
172 return;
173
174 if (read_history(hist_file))
175 log_very_verbose("Couldn't read history from %s.", hist_file);
176
177 stifle_history(find_config_tree_int(cmd, shell_history_size_CFG, NULL));
178 }
179
180 static void _write_history(void)
181 {
182 char hist_file[PATH_MAX];
183
184 if (!_hist_file(hist_file, sizeof(hist_file)))
185 return;
186
187 if (write_history(hist_file))
188 log_very_verbose("Couldn't write history to %s.", hist_file);
189 }
190
191 static int _log_shell_command_status(struct cmd_context *cmd, int ret_code)
192 {
193 log_report_t log_state;
194
195 if (!cmd->cmd_report.log_rh)
196 return 1;
197
198 log_state = log_get_report_state();
199
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);
208 }
209
210 static void _discard_log_report_content(struct cmd_context *cmd)
211 {
212 if (cmd->cmd_report.log_rh)
213 dm_report_destroy_rows(cmd->cmd_report.log_rh);
214 }
215
216 int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
217 {
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;
222
223 rl_readline_name = "lvm";
224 rl_attempted_completion_function = (rl_completion_func_t *) _completion;
225
226 _read_history(cmd);
227 _cmdline = cmdline;
228
229 cmd->is_interactive = 1;
230
231 if (!report_format_init(cmd))
232 return_ECMD_FAILED;
233
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);
237
238 while (1) {
239 /*
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').
244 *
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.
247 */
248
249 report_reset_cmdlog_seqnum();
250 if (cmd->cmd_report.log_rh) {
251 /*
252 * If previous command was lastlog, reset log report selection to
253 * its original value as set by log/command_log_selection config setting.
254 */
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.");
258 }
259
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);
263
264 free(input);
265 input = readline("lvm> ");
266
267 /* EOF */
268 if (!input) {
269 _discard_log_report_content(cmd);
270 /* readline sends prompt to stdout */
271 printf("\n");
272 break;
273 }
274
275 /* empty line */
276 if (!*input) {
277 _discard_log_report_content(cmd);
278 continue;
279 }
280
281 log_set_report_object_name_and_id(input, NULL);
282
283 add_history(input);
284
285 for (i = 0; i < MAX_ARGS; i++)
286 args[i] = NULL;
287
288 argv = args;
289
290 if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
291 _discard_log_report_content(cmd);
292 log_error("Too many arguments, sorry.");
293 goto report_log;
294 }
295
296 if (!argc) {
297 _discard_log_report_content(cmd);
298 continue;
299 }
300
301 if (!strcmp(argv[0], "lvm")) {
302 argv++;
303 argc--;
304 }
305
306 if (!argc) {
307 _discard_log_report_content(cmd);
308 continue;
309 }
310
311 log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_CMD);
312 log_set_report_object_name_and_id(argv[0], NULL);
313
314 is_lastlog_cmd = !strcmp(argv[0], "lastlog");
315
316 if (!is_lastlog_cmd)
317 _discard_log_report_content(cmd);
318
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.");
323 break;
324 }
325
326 ret = lvm_run_command(cmd, argc, argv);
327 if (ret == ENO_SUCH_CMD)
328 log_error("No such command '%s'. Try 'help'.",
329 argv[0]);
330
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);
334 }
335 _write_history();
336
337 if (!is_lastlog_cmd)
338 _log_shell_command_status(cmd, ret);
339 report_log:
340 log_set_report(NULL);
341 dm_report_group_output_and_pop_all(cmd->cmd_report.report_group);
342
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.");
349 break;
350 }
351 }
352
353 log_restore_report_state(saved_log_report_state);
354 cmd->is_interactive = 0;
355
356 free(input);
357
358 if (cmd->cmd_report.report_group) {
359 if (!dm_report_group_destroy(cmd->cmd_report.report_group))
360 stack;
361 cmd->cmd_report.report_group = NULL;
362 }
363
364 if (cmd->cmd_report.log_rh) {
365 dm_report_free(cmd->cmd_report.log_rh);
366 cmd->cmd_report.report_group = NULL;
367 }
368
369 return 0;
370 }
371
372 #endif /* READLINE_SUPPORT || EDITLINE_SUPPORT */
This page took 0.058363 seconds and 6 git commands to generate.