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_unref(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_unref(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_unref(qemuio_bs); 426 } 427 return 0; 428 } 429