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