root / qemu-io.c @ ddf5636d
History | View | Annotate | Download (11.6 kB)
1 |
/*
|
---|---|
2 |
* Command line utility to exercise the QEMU I/O path.
|
3 |
*
|
4 |
* Copyright (C) 2009 Red Hat, Inc.
|
5 |
* Copyright (c) 2003-2005 Silicon Graphics, Inc.
|
6 |
*
|
7 |
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
8 |
* See the COPYING file in the top-level directory.
|
9 |
*/
|
10 |
#include <sys/time.h> |
11 |
#include <sys/types.h> |
12 |
#include <stdarg.h> |
13 |
#include <stdio.h> |
14 |
#include <getopt.h> |
15 |
#include <libgen.h> |
16 |
|
17 |
#include "qemu-io.h" |
18 |
#include "qemu/main-loop.h" |
19 |
#include "qemu/option.h" |
20 |
#include "qemu/config-file.h" |
21 |
#include "qemu/readline.h" |
22 |
#include "block/block_int.h" |
23 |
#include "trace/control.h" |
24 |
|
25 |
#define CMD_NOFILE_OK 0x01 |
26 |
|
27 |
char *progname;
|
28 |
|
29 |
BlockDriverState *qemuio_bs; |
30 |
extern int qemuio_misalign; |
31 |
|
32 |
/* qemu-io commands passed using -c */
|
33 |
static int ncmdline; |
34 |
static char **cmdline; |
35 |
|
36 |
static ReadLineState *readline_state;
|
37 |
|
38 |
static int close_f(BlockDriverState *bs, int argc, char **argv) |
39 |
{ |
40 |
bdrv_unref(bs); |
41 |
qemuio_bs = NULL;
|
42 |
return 0; |
43 |
} |
44 |
|
45 |
static const cmdinfo_t close_cmd = { |
46 |
.name = "close",
|
47 |
.altname = "c",
|
48 |
.cfunc = close_f, |
49 |
.oneline = "close the current open file",
|
50 |
}; |
51 |
|
52 |
static int openfile(char *name, int flags, int growable, QDict *opts) |
53 |
{ |
54 |
Error *local_err = NULL;
|
55 |
|
56 |
if (qemuio_bs) {
|
57 |
fprintf(stderr, "file open already, try 'help close'\n");
|
58 |
return 1; |
59 |
} |
60 |
|
61 |
if (growable) {
|
62 |
if (bdrv_file_open(&qemuio_bs, name, NULL, opts, flags, &local_err)) { |
63 |
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
|
64 |
error_get_pretty(local_err)); |
65 |
error_free(local_err); |
66 |
return 1; |
67 |
} |
68 |
} else {
|
69 |
qemuio_bs = bdrv_new("hda");
|
70 |
|
71 |
if (bdrv_open(&qemuio_bs, name, NULL, opts, flags, NULL, &local_err) |
72 |
< 0)
|
73 |
{ |
74 |
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
|
75 |
error_get_pretty(local_err)); |
76 |
error_free(local_err); |
77 |
bdrv_unref(qemuio_bs); |
78 |
qemuio_bs = NULL;
|
79 |
return 1; |
80 |
} |
81 |
} |
82 |
|
83 |
return 0; |
84 |
} |
85 |
|
86 |
static void open_help(void) |
87 |
{ |
88 |
printf( |
89 |
"\n"
|
90 |
" opens a new file in the requested mode\n"
|
91 |
"\n"
|
92 |
" Example:\n"
|
93 |
" 'open -Cn /tmp/data' - creates/opens data file read-write and uncached\n"
|
94 |
"\n"
|
95 |
" Opens a file for subsequent use by all of the other qemu-io commands.\n"
|
96 |
" -r, -- open file read-only\n"
|
97 |
" -s, -- use snapshot file\n"
|
98 |
" -n, -- disable host cache\n"
|
99 |
" -g, -- allow file to grow (only applies to protocols)\n"
|
100 |
" -o, -- options to be given to the block driver"
|
101 |
"\n");
|
102 |
} |
103 |
|
104 |
static int open_f(BlockDriverState *bs, int argc, char **argv); |
105 |
|
106 |
static const cmdinfo_t open_cmd = { |
107 |
.name = "open",
|
108 |
.altname = "o",
|
109 |
.cfunc = open_f, |
110 |
.argmin = 1,
|
111 |
.argmax = -1,
|
112 |
.flags = CMD_NOFILE_OK, |
113 |
.args = "[-Crsn] [-o options] [path]",
|
114 |
.oneline = "open the file specified by path",
|
115 |
.help = open_help, |
116 |
}; |
117 |
|
118 |
static QemuOptsList empty_opts = {
|
119 |
.name = "drive",
|
120 |
.head = QTAILQ_HEAD_INITIALIZER(empty_opts.head), |
121 |
.desc = { |
122 |
/* no elements => accept any params */
|
123 |
{ /* end of list */ }
|
124 |
}, |
125 |
}; |
126 |
|
127 |
static int open_f(BlockDriverState *bs, int argc, char **argv) |
128 |
{ |
129 |
int flags = 0; |
130 |
int readonly = 0; |
131 |
int growable = 0; |
132 |
int c;
|
133 |
QemuOpts *qopts; |
134 |
QDict *opts = NULL;
|
135 |
|
136 |
while ((c = getopt(argc, argv, "snrgo:")) != EOF) { |
137 |
switch (c) {
|
138 |
case 's': |
139 |
flags |= BDRV_O_SNAPSHOT; |
140 |
break;
|
141 |
case 'n': |
142 |
flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; |
143 |
break;
|
144 |
case 'r': |
145 |
readonly = 1;
|
146 |
break;
|
147 |
case 'g': |
148 |
growable = 1;
|
149 |
break;
|
150 |
case 'o': |
151 |
qopts = qemu_opts_parse(&empty_opts, optarg, 0);
|
152 |
if (qopts == NULL) { |
153 |
printf("could not parse option list -- %s\n", optarg);
|
154 |
return 0; |
155 |
} |
156 |
opts = qemu_opts_to_qdict(qopts, opts); |
157 |
qemu_opts_del(qopts); |
158 |
break;
|
159 |
default:
|
160 |
return qemuio_command_usage(&open_cmd);
|
161 |
} |
162 |
} |
163 |
|
164 |
if (!readonly) {
|
165 |
flags |= BDRV_O_RDWR; |
166 |
} |
167 |
|
168 |
if (optind == argc - 1) { |
169 |
return openfile(argv[optind], flags, growable, opts);
|
170 |
} else if (optind == argc) { |
171 |
return openfile(NULL, flags, growable, opts); |
172 |
} else {
|
173 |
return qemuio_command_usage(&open_cmd);
|
174 |
} |
175 |
} |
176 |
|
177 |
static int quit_f(BlockDriverState *bs, int argc, char **argv) |
178 |
{ |
179 |
return 1; |
180 |
} |
181 |
|
182 |
static const cmdinfo_t quit_cmd = { |
183 |
.name = "quit",
|
184 |
.altname = "q",
|
185 |
.cfunc = quit_f, |
186 |
.argmin = -1,
|
187 |
.argmax = -1,
|
188 |
.flags = CMD_FLAG_GLOBAL, |
189 |
.oneline = "exit the program",
|
190 |
}; |
191 |
|
192 |
static void usage(const char *name) |
193 |
{ |
194 |
printf( |
195 |
"Usage: %s [-h] [-V] [-rsnm] [-c cmd] ... [file]\n"
|
196 |
"QEMU Disk exerciser\n"
|
197 |
"\n"
|
198 |
" -c, --cmd command to execute\n"
|
199 |
" -r, --read-only export read-only\n"
|
200 |
" -s, --snapshot use snapshot file\n"
|
201 |
" -n, --nocache disable host cache\n"
|
202 |
" -g, --growable allow file to grow (only applies to protocols)\n"
|
203 |
" -m, --misalign misalign allocations for O_DIRECT\n"
|
204 |
" -k, --native-aio use kernel AIO implementation (on Linux only)\n"
|
205 |
" -t, --cache=MODE use the given cache mode for the image\n"
|
206 |
" -T, --trace FILE enable trace events listed in the given file\n"
|
207 |
" -h, --help display this help and exit\n"
|
208 |
" -V, --version output version information and exit\n"
|
209 |
"\n",
|
210 |
name); |
211 |
} |
212 |
|
213 |
static char *get_prompt(void) |
214 |
{ |
215 |
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ]; |
216 |
|
217 |
if (!prompt[0]) { |
218 |
snprintf(prompt, sizeof(prompt), "%s> ", progname); |
219 |
} |
220 |
|
221 |
return prompt;
|
222 |
} |
223 |
|
224 |
static void GCC_FMT_ATTR(2, 3) readline_printf_func(void *opaque, |
225 |
const char *fmt, ...) |
226 |
{ |
227 |
va_list ap; |
228 |
va_start(ap, fmt); |
229 |
vprintf(fmt, ap); |
230 |
va_end(ap); |
231 |
} |
232 |
|
233 |
static void readline_flush_func(void *opaque) |
234 |
{ |
235 |
fflush(stdout); |
236 |
} |
237 |
|
238 |
static void readline_func(void *opaque, const char *str, void *readline_opaque) |
239 |
{ |
240 |
char **line = readline_opaque;
|
241 |
*line = g_strdup(str); |
242 |
} |
243 |
|
244 |
static void completion_match(const char *cmd, void *opaque) |
245 |
{ |
246 |
readline_add_completion(readline_state, cmd); |
247 |
} |
248 |
|
249 |
static void readline_completion_func(void *opaque, const char *str) |
250 |
{ |
251 |
readline_set_completion_index(readline_state, strlen(str)); |
252 |
qemuio_complete_command(str, completion_match, NULL);
|
253 |
} |
254 |
|
255 |
static char *fetchline_readline(void) |
256 |
{ |
257 |
char *line = NULL; |
258 |
|
259 |
readline_start(readline_state, get_prompt(), 0, readline_func, &line);
|
260 |
while (!line) {
|
261 |
int ch = getchar();
|
262 |
if (ch == EOF) { |
263 |
break;
|
264 |
} |
265 |
readline_handle_byte(readline_state, ch); |
266 |
} |
267 |
return line;
|
268 |
} |
269 |
|
270 |
#define MAXREADLINESZ 1024 |
271 |
static char *fetchline_fgets(void) |
272 |
{ |
273 |
char *p, *line = g_malloc(MAXREADLINESZ);
|
274 |
|
275 |
if (!fgets(line, MAXREADLINESZ, stdin)) {
|
276 |
g_free(line); |
277 |
return NULL; |
278 |
} |
279 |
|
280 |
p = line + strlen(line); |
281 |
if (p != line && p[-1] == '\n') { |
282 |
p[-1] = '\0'; |
283 |
} |
284 |
|
285 |
return line;
|
286 |
} |
287 |
|
288 |
static char *fetchline(void) |
289 |
{ |
290 |
if (readline_state) {
|
291 |
return fetchline_readline();
|
292 |
} else {
|
293 |
return fetchline_fgets();
|
294 |
} |
295 |
} |
296 |
|
297 |
static void prep_fetchline(void *opaque) |
298 |
{ |
299 |
int *fetchable = opaque;
|
300 |
|
301 |
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); |
302 |
*fetchable= 1;
|
303 |
} |
304 |
|
305 |
static void command_loop(void) |
306 |
{ |
307 |
int i, done = 0, fetchable = 0, prompted = 0; |
308 |
char *input;
|
309 |
|
310 |
for (i = 0; !done && i < ncmdline; i++) { |
311 |
done = qemuio_command(qemuio_bs, cmdline[i]); |
312 |
} |
313 |
if (cmdline) {
|
314 |
g_free(cmdline); |
315 |
return;
|
316 |
} |
317 |
|
318 |
while (!done) {
|
319 |
if (!prompted) {
|
320 |
printf("%s", get_prompt());
|
321 |
fflush(stdout); |
322 |
qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable);
|
323 |
prompted = 1;
|
324 |
} |
325 |
|
326 |
main_loop_wait(false);
|
327 |
|
328 |
if (!fetchable) {
|
329 |
continue;
|
330 |
} |
331 |
|
332 |
input = fetchline(); |
333 |
if (input == NULL) { |
334 |
break;
|
335 |
} |
336 |
done = qemuio_command(qemuio_bs, input); |
337 |
g_free(input); |
338 |
|
339 |
prompted = 0;
|
340 |
fetchable = 0;
|
341 |
} |
342 |
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); |
343 |
} |
344 |
|
345 |
static void add_user_command(char *optarg) |
346 |
{ |
347 |
cmdline = g_realloc(cmdline, ++ncmdline * sizeof(char *)); |
348 |
cmdline[ncmdline-1] = optarg;
|
349 |
} |
350 |
|
351 |
static void reenable_tty_echo(void) |
352 |
{ |
353 |
qemu_set_tty_echo(STDIN_FILENO, true);
|
354 |
} |
355 |
|
356 |
int main(int argc, char **argv) |
357 |
{ |
358 |
int readonly = 0; |
359 |
int growable = 0; |
360 |
const char *sopt = "hVc:d:rsnmgkt:T:"; |
361 |
const struct option lopt[] = { |
362 |
{ "help", 0, NULL, 'h' }, |
363 |
{ "version", 0, NULL, 'V' }, |
364 |
{ "offset", 1, NULL, 'o' }, |
365 |
{ "cmd", 1, NULL, 'c' }, |
366 |
{ "read-only", 0, NULL, 'r' }, |
367 |
{ "snapshot", 0, NULL, 's' }, |
368 |
{ "nocache", 0, NULL, 'n' }, |
369 |
{ "misalign", 0, NULL, 'm' }, |
370 |
{ "growable", 0, NULL, 'g' }, |
371 |
{ "native-aio", 0, NULL, 'k' }, |
372 |
{ "discard", 1, NULL, 'd' }, |
373 |
{ "cache", 1, NULL, 't' }, |
374 |
{ "trace", 1, NULL, 'T' }, |
375 |
{ NULL, 0, NULL, 0 } |
376 |
}; |
377 |
int c;
|
378 |
int opt_index = 0; |
379 |
int flags = BDRV_O_UNMAP;
|
380 |
|
381 |
#ifdef CONFIG_POSIX
|
382 |
signal(SIGPIPE, SIG_IGN); |
383 |
#endif
|
384 |
|
385 |
progname = basename(argv[0]);
|
386 |
|
387 |
while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { |
388 |
switch (c) {
|
389 |
case 's': |
390 |
flags |= BDRV_O_SNAPSHOT; |
391 |
break;
|
392 |
case 'n': |
393 |
flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; |
394 |
break;
|
395 |
case 'd': |
396 |
if (bdrv_parse_discard_flags(optarg, &flags) < 0) { |
397 |
error_report("Invalid discard option: %s", optarg);
|
398 |
exit(1);
|
399 |
} |
400 |
break;
|
401 |
case 'c': |
402 |
add_user_command(optarg); |
403 |
break;
|
404 |
case 'r': |
405 |
readonly = 1;
|
406 |
break;
|
407 |
case 'm': |
408 |
qemuio_misalign = 1;
|
409 |
break;
|
410 |
case 'g': |
411 |
growable = 1;
|
412 |
break;
|
413 |
case 'k': |
414 |
flags |= BDRV_O_NATIVE_AIO; |
415 |
break;
|
416 |
case 't': |
417 |
if (bdrv_parse_cache_flags(optarg, &flags) < 0) { |
418 |
error_report("Invalid cache option: %s", optarg);
|
419 |
exit(1);
|
420 |
} |
421 |
break;
|
422 |
case 'T': |
423 |
if (!trace_backend_init(optarg, NULL)) { |
424 |
exit(1); /* error message will have been printed */ |
425 |
} |
426 |
break;
|
427 |
case 'V': |
428 |
printf("%s version %s\n", progname, QEMU_VERSION);
|
429 |
exit(0);
|
430 |
case 'h': |
431 |
usage(progname); |
432 |
exit(0);
|
433 |
default:
|
434 |
usage(progname); |
435 |
exit(1);
|
436 |
} |
437 |
} |
438 |
|
439 |
if ((argc - optind) > 1) { |
440 |
usage(progname); |
441 |
exit(1);
|
442 |
} |
443 |
|
444 |
qemu_init_main_loop(); |
445 |
bdrv_init(); |
446 |
|
447 |
/* initialize commands */
|
448 |
qemuio_add_command(&quit_cmd); |
449 |
qemuio_add_command(&open_cmd); |
450 |
qemuio_add_command(&close_cmd); |
451 |
|
452 |
if (isatty(STDIN_FILENO)) {
|
453 |
readline_state = readline_init(readline_printf_func, |
454 |
readline_flush_func, |
455 |
NULL,
|
456 |
readline_completion_func); |
457 |
qemu_set_tty_echo(STDIN_FILENO, false);
|
458 |
atexit(reenable_tty_echo); |
459 |
} |
460 |
|
461 |
/* open the device */
|
462 |
if (!readonly) {
|
463 |
flags |= BDRV_O_RDWR; |
464 |
} |
465 |
|
466 |
if ((argc - optind) == 1) { |
467 |
openfile(argv[optind], flags, growable, NULL);
|
468 |
} |
469 |
command_loop(); |
470 |
|
471 |
/*
|
472 |
* Make sure all outstanding requests complete before the program exits.
|
473 |
*/
|
474 |
bdrv_drain_all(); |
475 |
|
476 |
if (qemuio_bs) {
|
477 |
bdrv_unref(qemuio_bs); |
478 |
} |
479 |
g_free(readline_state); |
480 |
return 0; |
481 |
} |