root / qemu-io.c @ 6a1751b7
History | View | Annotate | Download (9.9 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 "block/block_int.h" |
20 |
#include "trace/control.h" |
21 |
|
22 |
#define CMD_NOFILE_OK 0x01 |
23 |
|
24 |
char *progname;
|
25 |
|
26 |
BlockDriverState *qemuio_bs; |
27 |
extern int qemuio_misalign; |
28 |
|
29 |
/* qemu-io commands passed using -c */
|
30 |
static int ncmdline; |
31 |
static char **cmdline; |
32 |
|
33 |
static int close_f(BlockDriverState *bs, int argc, char **argv) |
34 |
{ |
35 |
bdrv_delete(bs); |
36 |
qemuio_bs = NULL;
|
37 |
return 0; |
38 |
} |
39 |
|
40 |
static const cmdinfo_t close_cmd = { |
41 |
.name = "close",
|
42 |
.altname = "c",
|
43 |
.cfunc = close_f, |
44 |
.oneline = "close the current open file",
|
45 |
}; |
46 |
|
47 |
static int openfile(char *name, int flags, int growable) |
48 |
{ |
49 |
if (qemuio_bs) {
|
50 |
fprintf(stderr, "file open already, try 'help close'\n");
|
51 |
return 1; |
52 |
} |
53 |
|
54 |
if (growable) {
|
55 |
if (bdrv_file_open(&qemuio_bs, name, NULL, flags)) { |
56 |
fprintf(stderr, "%s: can't open device %s\n", progname, name);
|
57 |
return 1; |
58 |
} |
59 |
} else {
|
60 |
qemuio_bs = bdrv_new("hda");
|
61 |
|
62 |
if (bdrv_open(qemuio_bs, name, NULL, flags, NULL) < 0) { |
63 |
fprintf(stderr, "%s: can't open device %s\n", progname, name);
|
64 |
bdrv_delete(qemuio_bs); |
65 |
qemuio_bs = NULL;
|
66 |
return 1; |
67 |
} |
68 |
} |
69 |
|
70 |
return 0; |
71 |
} |
72 |
|
73 |
static void open_help(void) |
74 |
{ |
75 |
printf( |
76 |
"\n"
|
77 |
" opens a new file in the requested mode\n"
|
78 |
"\n"
|
79 |
" Example:\n"
|
80 |
" 'open -Cn /tmp/data' - creates/opens data file read-write and uncached\n"
|
81 |
"\n"
|
82 |
" Opens a file for subsequent use by all of the other qemu-io commands.\n"
|
83 |
" -r, -- open file read-only\n"
|
84 |
" -s, -- use snapshot file\n"
|
85 |
" -n, -- disable host cache\n"
|
86 |
" -g, -- allow file to grow (only applies to protocols)"
|
87 |
"\n");
|
88 |
} |
89 |
|
90 |
static int open_f(BlockDriverState *bs, int argc, char **argv); |
91 |
|
92 |
static const cmdinfo_t open_cmd = { |
93 |
.name = "open",
|
94 |
.altname = "o",
|
95 |
.cfunc = open_f, |
96 |
.argmin = 1,
|
97 |
.argmax = -1,
|
98 |
.flags = CMD_NOFILE_OK, |
99 |
.args = "[-Crsn] [path]",
|
100 |
.oneline = "open the file specified by path",
|
101 |
.help = open_help, |
102 |
}; |
103 |
|
104 |
static int open_f(BlockDriverState *bs, int argc, char **argv) |
105 |
{ |
106 |
int flags = 0; |
107 |
int readonly = 0; |
108 |
int growable = 0; |
109 |
int c;
|
110 |
|
111 |
while ((c = getopt(argc, argv, "snrg")) != EOF) { |
112 |
switch (c) {
|
113 |
case 's': |
114 |
flags |= BDRV_O_SNAPSHOT; |
115 |
break;
|
116 |
case 'n': |
117 |
flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; |
118 |
break;
|
119 |
case 'r': |
120 |
readonly = 1;
|
121 |
break;
|
122 |
case 'g': |
123 |
growable = 1;
|
124 |
break;
|
125 |
default:
|
126 |
return qemuio_command_usage(&open_cmd);
|
127 |
} |
128 |
} |
129 |
|
130 |
if (!readonly) {
|
131 |
flags |= BDRV_O_RDWR; |
132 |
} |
133 |
|
134 |
if (optind != argc - 1) { |
135 |
return qemuio_command_usage(&open_cmd);
|
136 |
} |
137 |
|
138 |
return openfile(argv[optind], flags, growable);
|
139 |
} |
140 |
|
141 |
static int quit_f(BlockDriverState *bs, int argc, char **argv) |
142 |
{ |
143 |
return 1; |
144 |
} |
145 |
|
146 |
static const cmdinfo_t quit_cmd = { |
147 |
.name = "quit",
|
148 |
.altname = "q",
|
149 |
.cfunc = quit_f, |
150 |
.argmin = -1,
|
151 |
.argmax = -1,
|
152 |
.flags = CMD_FLAG_GLOBAL, |
153 |
.oneline = "exit the program",
|
154 |
}; |
155 |
|
156 |
static void usage(const char *name) |
157 |
{ |
158 |
printf( |
159 |
"Usage: %s [-h] [-V] [-rsnm] [-c cmd] ... [file]\n"
|
160 |
"QEMU Disk exerciser\n"
|
161 |
"\n"
|
162 |
" -c, --cmd command to execute\n"
|
163 |
" -r, --read-only export read-only\n"
|
164 |
" -s, --snapshot use snapshot file\n"
|
165 |
" -n, --nocache disable host cache\n"
|
166 |
" -g, --growable allow file to grow (only applies to protocols)\n"
|
167 |
" -m, --misalign misalign allocations for O_DIRECT\n"
|
168 |
" -k, --native-aio use kernel AIO implementation (on Linux only)\n"
|
169 |
" -t, --cache=MODE use the given cache mode for the image\n"
|
170 |
" -T, --trace FILE enable trace events listed in the given file\n"
|
171 |
" -h, --help display this help and exit\n"
|
172 |
" -V, --version output version information and exit\n"
|
173 |
"\n",
|
174 |
name); |
175 |
} |
176 |
|
177 |
|
178 |
#if defined(ENABLE_READLINE)
|
179 |
# include <readline/history.h> |
180 |
# include <readline/readline.h> |
181 |
#elif defined(ENABLE_EDITLINE)
|
182 |
# include <histedit.h> |
183 |
#endif
|
184 |
|
185 |
static char *get_prompt(void) |
186 |
{ |
187 |
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ]; |
188 |
|
189 |
if (!prompt[0]) { |
190 |
snprintf(prompt, sizeof(prompt), "%s> ", progname); |
191 |
} |
192 |
|
193 |
return prompt;
|
194 |
} |
195 |
|
196 |
#if defined(ENABLE_READLINE)
|
197 |
static char *fetchline(void) |
198 |
{ |
199 |
char *line = readline(get_prompt());
|
200 |
if (line && *line) {
|
201 |
add_history(line); |
202 |
} |
203 |
return line;
|
204 |
} |
205 |
#elif defined(ENABLE_EDITLINE)
|
206 |
static char *el_get_prompt(EditLine *e) |
207 |
{ |
208 |
return get_prompt();
|
209 |
} |
210 |
|
211 |
static char *fetchline(void) |
212 |
{ |
213 |
static EditLine *el;
|
214 |
static History *hist;
|
215 |
HistEvent hevent; |
216 |
char *line;
|
217 |
int count;
|
218 |
|
219 |
if (!el) {
|
220 |
hist = history_init(); |
221 |
history(hist, &hevent, H_SETSIZE, 100);
|
222 |
el = el_init(progname, stdin, stdout, stderr); |
223 |
el_source(el, NULL);
|
224 |
el_set(el, EL_SIGNAL, 1);
|
225 |
el_set(el, EL_PROMPT, el_get_prompt); |
226 |
el_set(el, EL_HIST, history, (const char *)hist); |
227 |
} |
228 |
line = strdup(el_gets(el, &count)); |
229 |
if (line) {
|
230 |
if (count > 0) { |
231 |
line[count-1] = '\0'; |
232 |
} |
233 |
if (*line) {
|
234 |
history(hist, &hevent, H_ENTER, line); |
235 |
} |
236 |
} |
237 |
return line;
|
238 |
} |
239 |
#else
|
240 |
# define MAXREADLINESZ 1024 |
241 |
static char *fetchline(void) |
242 |
{ |
243 |
char *p, *line = g_malloc(MAXREADLINESZ);
|
244 |
|
245 |
if (!fgets(line, MAXREADLINESZ, stdin)) {
|
246 |
g_free(line); |
247 |
return NULL; |
248 |
} |
249 |
|
250 |
p = line + strlen(line); |
251 |
if (p != line && p[-1] == '\n') { |
252 |
p[-1] = '\0'; |
253 |
} |
254 |
|
255 |
return line;
|
256 |
} |
257 |
#endif
|
258 |
|
259 |
static void prep_fetchline(void *opaque) |
260 |
{ |
261 |
int *fetchable = opaque;
|
262 |
|
263 |
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); |
264 |
*fetchable= 1;
|
265 |
} |
266 |
|
267 |
static void command_loop(void) |
268 |
{ |
269 |
int i, done = 0, fetchable = 0, prompted = 0; |
270 |
char *input;
|
271 |
|
272 |
for (i = 0; !done && i < ncmdline; i++) { |
273 |
done = qemuio_command(qemuio_bs, cmdline[i]); |
274 |
} |
275 |
if (cmdline) {
|
276 |
g_free(cmdline); |
277 |
return;
|
278 |
} |
279 |
|
280 |
while (!done) {
|
281 |
if (!prompted) {
|
282 |
printf("%s", get_prompt());
|
283 |
fflush(stdout); |
284 |
qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable);
|
285 |
prompted = 1;
|
286 |
} |
287 |
|
288 |
main_loop_wait(false);
|
289 |
|
290 |
if (!fetchable) {
|
291 |
continue;
|
292 |
} |
293 |
|
294 |
input = fetchline(); |
295 |
if (input == NULL) { |
296 |
break;
|
297 |
} |
298 |
done = qemuio_command(qemuio_bs, input); |
299 |
g_free(input); |
300 |
|
301 |
prompted = 0;
|
302 |
fetchable = 0;
|
303 |
} |
304 |
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); |
305 |
} |
306 |
|
307 |
static void add_user_command(char *optarg) |
308 |
{ |
309 |
cmdline = g_realloc(cmdline, ++ncmdline * sizeof(char *)); |
310 |
cmdline[ncmdline-1] = optarg;
|
311 |
} |
312 |
|
313 |
int main(int argc, char **argv) |
314 |
{ |
315 |
int readonly = 0; |
316 |
int growable = 0; |
317 |
const char *sopt = "hVc:d:rsnmgkt:T:"; |
318 |
const struct option lopt[] = { |
319 |
{ "help", 0, NULL, 'h' }, |
320 |
{ "version", 0, NULL, 'V' }, |
321 |
{ "offset", 1, NULL, 'o' }, |
322 |
{ "cmd", 1, NULL, 'c' }, |
323 |
{ "read-only", 0, NULL, 'r' }, |
324 |
{ "snapshot", 0, NULL, 's' }, |
325 |
{ "nocache", 0, NULL, 'n' }, |
326 |
{ "misalign", 0, NULL, 'm' }, |
327 |
{ "growable", 0, NULL, 'g' }, |
328 |
{ "native-aio", 0, NULL, 'k' }, |
329 |
{ "discard", 1, NULL, 'd' }, |
330 |
{ "cache", 1, NULL, 't' }, |
331 |
{ "trace", 1, NULL, 'T' }, |
332 |
{ NULL, 0, NULL, 0 } |
333 |
}; |
334 |
int c;
|
335 |
int opt_index = 0; |
336 |
int flags = BDRV_O_UNMAP;
|
337 |
|
338 |
#ifdef CONFIG_POSIX
|
339 |
signal(SIGPIPE, SIG_IGN); |
340 |
#endif
|
341 |
|
342 |
progname = basename(argv[0]);
|
343 |
|
344 |
while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { |
345 |
switch (c) {
|
346 |
case 's': |
347 |
flags |= BDRV_O_SNAPSHOT; |
348 |
break;
|
349 |
case 'n': |
350 |
flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; |
351 |
break;
|
352 |
case 'd': |
353 |
if (bdrv_parse_discard_flags(optarg, &flags) < 0) { |
354 |
error_report("Invalid discard option: %s", optarg);
|
355 |
exit(1);
|
356 |
} |
357 |
break;
|
358 |
case 'c': |
359 |
add_user_command(optarg); |
360 |
break;
|
361 |
case 'r': |
362 |
readonly = 1;
|
363 |
break;
|
364 |
case 'm': |
365 |
qemuio_misalign = 1;
|
366 |
break;
|
367 |
case 'g': |
368 |
growable = 1;
|
369 |
break;
|
370 |
case 'k': |
371 |
flags |= BDRV_O_NATIVE_AIO; |
372 |
break;
|
373 |
case 't': |
374 |
if (bdrv_parse_cache_flags(optarg, &flags) < 0) { |
375 |
error_report("Invalid cache option: %s", optarg);
|
376 |
exit(1);
|
377 |
} |
378 |
break;
|
379 |
case 'T': |
380 |
if (!trace_backend_init(optarg, NULL)) { |
381 |
exit(1); /* error message will have been printed */ |
382 |
} |
383 |
break;
|
384 |
case 'V': |
385 |
printf("%s version %s\n", progname, QEMU_VERSION);
|
386 |
exit(0);
|
387 |
case 'h': |
388 |
usage(progname); |
389 |
exit(0);
|
390 |
default:
|
391 |
usage(progname); |
392 |
exit(1);
|
393 |
} |
394 |
} |
395 |
|
396 |
if ((argc - optind) > 1) { |
397 |
usage(progname); |
398 |
exit(1);
|
399 |
} |
400 |
|
401 |
qemu_init_main_loop(); |
402 |
bdrv_init(); |
403 |
|
404 |
/* initialize commands */
|
405 |
qemuio_add_command(&quit_cmd); |
406 |
qemuio_add_command(&open_cmd); |
407 |
qemuio_add_command(&close_cmd); |
408 |
|
409 |
/* open the device */
|
410 |
if (!readonly) {
|
411 |
flags |= BDRV_O_RDWR; |
412 |
} |
413 |
|
414 |
if ((argc - optind) == 1) { |
415 |
openfile(argv[optind], flags, growable); |
416 |
} |
417 |
command_loop(); |
418 |
|
419 |
/*
|
420 |
* Make sure all outstanding requests complete before the program exits.
|
421 |
*/
|
422 |
bdrv_drain_all(); |
423 |
|
424 |
if (qemuio_bs) {
|
425 |
bdrv_delete(qemuio_bs); |
426 |
} |
427 |
return 0; |
428 |
} |