xref: /openbmc/qemu/qemu-io.c (revision 988717b46b6424907618cb845ace9d69062703af)
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  
11  #include "qemu/osdep.h"
12  #include <getopt.h>
13  #include <libgen.h>
14  #ifndef _WIN32
15  #include <termios.h>
16  #endif
17  
18  #include "qemu-common.h"
19  #include "qapi/error.h"
20  #include "qemu-io.h"
21  #include "qemu/error-report.h"
22  #include "qemu/main-loop.h"
23  #include "qemu/module.h"
24  #include "qemu/option.h"
25  #include "qemu/config-file.h"
26  #include "qemu/readline.h"
27  #include "qemu/log.h"
28  #include "qapi/qmp/qstring.h"
29  #include "qapi/qmp/qdict.h"
30  #include "qom/object_interfaces.h"
31  #include "sysemu/block-backend.h"
32  #include "block/block_int.h"
33  #include "trace/control.h"
34  #include "crypto/init.h"
35  #include "qemu-version.h"
36  
37  #define CMD_NOFILE_OK   0x01
38  
39  static BlockBackend *qemuio_blk;
40  static bool quit_qemu_io;
41  
42  /* qemu-io commands passed using -c */
43  static int ncmdline;
44  static char **cmdline;
45  static bool imageOpts;
46  
47  static ReadLineState *readline_state;
48  
49  static int ttyEOF;
50  
51  static int get_eof_char(void)
52  {
53  #ifdef _WIN32
54      return 0x4; /* Ctrl-D */
55  #else
56      struct termios tty;
57      if (tcgetattr(STDIN_FILENO, &tty) != 0) {
58          if (errno == ENOTTY) {
59              return 0x0; /* just expect read() == 0 */
60          } else {
61              return 0x4; /* Ctrl-D */
62          }
63      }
64  
65      return tty.c_cc[VEOF];
66  #endif
67  }
68  
69  static int close_f(BlockBackend *blk, int argc, char **argv)
70  {
71      blk_unref(qemuio_blk);
72      qemuio_blk = NULL;
73      return 0;
74  }
75  
76  static const cmdinfo_t close_cmd = {
77      .name       = "close",
78      .altname    = "c",
79      .cfunc      = close_f,
80      .oneline    = "close the current open file",
81  };
82  
83  static int openfile(char *name, int flags, bool writethrough, bool force_share,
84                      QDict *opts)
85  {
86      Error *local_err = NULL;
87  
88      if (qemuio_blk) {
89          error_report("file open already, try 'help close'");
90          qobject_unref(opts);
91          return 1;
92      }
93  
94      if (force_share) {
95          if (!opts) {
96              opts = qdict_new();
97          }
98          if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE)
99              && strcmp(qdict_get_str(opts, BDRV_OPT_FORCE_SHARE), "on")) {
100              error_report("-U conflicts with image options");
101              qobject_unref(opts);
102              return 1;
103          }
104          qdict_put_str(opts, BDRV_OPT_FORCE_SHARE, "on");
105      }
106      qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err);
107      if (!qemuio_blk) {
108          error_reportf_err(local_err, "can't open%s%s: ",
109                            name ? " device " : "", name ?: "");
110          return 1;
111      }
112  
113      blk_set_enable_write_cache(qemuio_blk, !writethrough);
114  
115      return 0;
116  }
117  
118  static void open_help(void)
119  {
120      printf(
121  "\n"
122  " opens a new file in the requested mode\n"
123  "\n"
124  " Example:\n"
125  " 'open -n -o driver=raw /tmp/data' - opens raw data file read-write, uncached\n"
126  "\n"
127  " Opens a file for subsequent use by all of the other qemu-io commands.\n"
128  " -r, -- open file read-only\n"
129  " -s, -- use snapshot file\n"
130  " -C, -- use copy-on-read\n"
131  " -n, -- disable host cache, short for -t none\n"
132  " -U, -- force shared permissions\n"
133  " -k, -- use kernel AIO implementation (Linux only, prefer use of -i)\n"
134  " -i, -- use AIO mode (threads, native or io_uring)\n"
135  " -t, -- use the given cache mode for the image\n"
136  " -d, -- use the given discard mode for the image\n"
137  " -o, -- options to be given to the block driver"
138  "\n");
139  }
140  
141  static int open_f(BlockBackend *blk, int argc, char **argv);
142  
143  static const cmdinfo_t open_cmd = {
144      .name       = "open",
145      .altname    = "o",
146      .cfunc      = open_f,
147      .argmin     = 1,
148      .argmax     = -1,
149      .flags      = CMD_NOFILE_OK,
150      .args       = "[-rsCnkU] [-t cache] [-d discard] [-o options] [path]",
151      .oneline    = "open the file specified by path",
152      .help       = open_help,
153  };
154  
155  static QemuOptsList empty_opts = {
156      .name = "drive",
157      .merge_lists = true,
158      .head = QTAILQ_HEAD_INITIALIZER(empty_opts.head),
159      .desc = {
160          /* no elements => accept any params */
161          { /* end of list */ }
162      },
163  };
164  
165  static int open_f(BlockBackend *blk, int argc, char **argv)
166  {
167      int flags = BDRV_O_UNMAP;
168      int readonly = 0;
169      bool writethrough = true;
170      int c;
171      int ret;
172      QemuOpts *qopts;
173      QDict *opts;
174      bool force_share = false;
175  
176      while ((c = getopt(argc, argv, "snCro:ki:t:d:U")) != -1) {
177          switch (c) {
178          case 's':
179              flags |= BDRV_O_SNAPSHOT;
180              break;
181          case 'n':
182              flags |= BDRV_O_NOCACHE;
183              writethrough = false;
184              break;
185          case 'C':
186              flags |= BDRV_O_COPY_ON_READ;
187              break;
188          case 'r':
189              readonly = 1;
190              break;
191          case 'k':
192              flags |= BDRV_O_NATIVE_AIO;
193              break;
194          case 't':
195              if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) {
196                  error_report("Invalid cache option: %s", optarg);
197                  qemu_opts_reset(&empty_opts);
198                  return -EINVAL;
199              }
200              break;
201          case 'd':
202              if (bdrv_parse_discard_flags(optarg, &flags) < 0) {
203                  error_report("Invalid discard option: %s", optarg);
204                  qemu_opts_reset(&empty_opts);
205                  return -EINVAL;
206              }
207              break;
208          case 'i':
209              if (bdrv_parse_aio(optarg, &flags) < 0) {
210                  error_report("Invalid aio option: %s", optarg);
211                  qemu_opts_reset(&empty_opts);
212                  return -EINVAL;
213              }
214              break;
215          case 'o':
216              if (imageOpts) {
217                  printf("--image-opts and 'open -o' are mutually exclusive\n");
218                  qemu_opts_reset(&empty_opts);
219                  return -EINVAL;
220              }
221              if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) {
222                  qemu_opts_reset(&empty_opts);
223                  return -EINVAL;
224              }
225              break;
226          case 'U':
227              force_share = true;
228              break;
229          default:
230              qemu_opts_reset(&empty_opts);
231              qemuio_command_usage(&open_cmd);
232              return -EINVAL;
233          }
234      }
235  
236      if (!readonly) {
237          flags |= BDRV_O_RDWR;
238      }
239  
240      if (imageOpts && (optind == argc - 1)) {
241          if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) {
242              qemu_opts_reset(&empty_opts);
243              return -EINVAL;
244          }
245          optind++;
246      }
247  
248      qopts = qemu_opts_find(&empty_opts, NULL);
249      opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
250      qemu_opts_reset(&empty_opts);
251  
252      if (optind == argc - 1) {
253          ret = openfile(argv[optind], flags, writethrough, force_share, opts);
254      } else if (optind == argc) {
255          ret = openfile(NULL, flags, writethrough, force_share, opts);
256      } else {
257          qobject_unref(opts);
258          qemuio_command_usage(&open_cmd);
259          return -EINVAL;
260      }
261  
262      if (ret) {
263          return -EINVAL;
264      }
265  
266      return 0;
267  }
268  
269  static int quit_f(BlockBackend *blk, int argc, char **argv)
270  {
271      quit_qemu_io = true;
272      return 0;
273  }
274  
275  static const cmdinfo_t quit_cmd = {
276      .name       = "quit",
277      .altname    = "q",
278      .cfunc      = quit_f,
279      .argmin     = -1,
280      .argmax     = -1,
281      .flags      = CMD_FLAG_GLOBAL,
282      .oneline    = "exit the program",
283  };
284  
285  static void usage(const char *name)
286  {
287      printf(
288  "Usage: %s [OPTIONS]... [-c STRING]... [file]\n"
289  "QEMU Disk exerciser\n"
290  "\n"
291  "  --object OBJECTDEF   define an object such as 'secret' for\n"
292  "                       passwords and/or encryption keys\n"
293  "  --image-opts         treat file as option string\n"
294  "  -c, --cmd STRING     execute command with its arguments\n"
295  "                       from the given string\n"
296  "  -f, --format FMT     specifies the block driver to use\n"
297  "  -r, --read-only      export read-only\n"
298  "  -s, --snapshot       use snapshot file\n"
299  "  -n, --nocache        disable host cache, short for -t none\n"
300  "  -C, --copy-on-read   enable copy-on-read\n"
301  "  -m, --misalign       misalign allocations for O_DIRECT\n"
302  "  -k, --native-aio     use kernel AIO implementation\n"
303  "                       (Linux only, prefer use of -i)\n"
304  "  -i, --aio=MODE       use AIO mode (threads, native or io_uring)\n"
305  "  -t, --cache=MODE     use the given cache mode for the image\n"
306  "  -d, --discard=MODE   use the given discard mode for the image\n"
307  "  -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
308  "                       specify tracing options\n"
309  "                       see qemu-img(1) man page for full description\n"
310  "  -U, --force-share    force shared permissions\n"
311  "  -h, --help           display this help and exit\n"
312  "  -V, --version        output version information and exit\n"
313  "\n"
314  "See '%s -c help' for information on available commands.\n"
315  "\n"
316  QEMU_HELP_BOTTOM "\n",
317      name, name);
318  }
319  
320  static char *get_prompt(void)
321  {
322      static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
323  
324      if (!prompt[0]) {
325          snprintf(prompt, sizeof(prompt), "%s> ", error_get_progname());
326      }
327  
328      return prompt;
329  }
330  
331  static void GCC_FMT_ATTR(2, 3) readline_printf_func(void *opaque,
332                                                      const char *fmt, ...)
333  {
334      va_list ap;
335      va_start(ap, fmt);
336      vprintf(fmt, ap);
337      va_end(ap);
338  }
339  
340  static void readline_flush_func(void *opaque)
341  {
342      fflush(stdout);
343  }
344  
345  static void readline_func(void *opaque, const char *str, void *readline_opaque)
346  {
347      char **line = readline_opaque;
348      *line = g_strdup(str);
349  }
350  
351  static void completion_match(const char *cmd, void *opaque)
352  {
353      readline_add_completion(readline_state, cmd);
354  }
355  
356  static void readline_completion_func(void *opaque, const char *str)
357  {
358      readline_set_completion_index(readline_state, strlen(str));
359      qemuio_complete_command(str, completion_match, NULL);
360  }
361  
362  static char *fetchline_readline(void)
363  {
364      char *line = NULL;
365  
366      readline_start(readline_state, get_prompt(), 0, readline_func, &line);
367      while (!line) {
368          int ch = getchar();
369          if (ttyEOF != 0x0 && ch == ttyEOF) {
370              printf("\n");
371              break;
372          }
373          readline_handle_byte(readline_state, ch);
374      }
375      return line;
376  }
377  
378  #define MAXREADLINESZ 1024
379  static char *fetchline_fgets(void)
380  {
381      char *p, *line = g_malloc(MAXREADLINESZ);
382  
383      if (!fgets(line, MAXREADLINESZ, stdin)) {
384          g_free(line);
385          return NULL;
386      }
387  
388      p = line + strlen(line);
389      if (p != line && p[-1] == '\n') {
390          p[-1] = '\0';
391      }
392  
393      return line;
394  }
395  
396  static char *fetchline(void)
397  {
398      if (readline_state) {
399          return fetchline_readline();
400      } else {
401          return fetchline_fgets();
402      }
403  }
404  
405  static void prep_fetchline(void *opaque)
406  {
407      int *fetchable = opaque;
408  
409      qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
410      *fetchable= 1;
411  }
412  
413  static int command_loop(void)
414  {
415      int i, fetchable = 0, prompted = 0;
416      int ret, last_error = 0;
417      char *input;
418  
419      for (i = 0; !quit_qemu_io && i < ncmdline; i++) {
420          ret = qemuio_command(qemuio_blk, cmdline[i]);
421          if (ret < 0) {
422              last_error = ret;
423          }
424      }
425      if (cmdline) {
426          g_free(cmdline);
427          return last_error;
428      }
429  
430      while (!quit_qemu_io) {
431          if (!prompted) {
432              printf("%s", get_prompt());
433              fflush(stdout);
434              qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable);
435              prompted = 1;
436          }
437  
438          main_loop_wait(false);
439  
440          if (!fetchable) {
441              continue;
442          }
443  
444          input = fetchline();
445          if (input == NULL) {
446              break;
447          }
448          ret = qemuio_command(qemuio_blk, input);
449          g_free(input);
450  
451          if (ret < 0) {
452              last_error = ret;
453          }
454  
455          prompted = 0;
456          fetchable = 0;
457      }
458      qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
459  
460      return last_error;
461  }
462  
463  static void add_user_command(char *optarg)
464  {
465      cmdline = g_renew(char *, cmdline, ++ncmdline);
466      cmdline[ncmdline-1] = optarg;
467  }
468  
469  static void reenable_tty_echo(void)
470  {
471      qemu_set_tty_echo(STDIN_FILENO, true);
472  }
473  
474  enum {
475      OPTION_OBJECT = 256,
476      OPTION_IMAGE_OPTS = 257,
477  };
478  
479  static QemuOptsList qemu_object_opts = {
480      .name = "object",
481      .implied_opt_name = "qom-type",
482      .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
483      .desc = {
484          { }
485      },
486  };
487  
488  static bool qemu_io_object_print_help(const char *type, QemuOpts *opts)
489  {
490      if (user_creatable_print_help(type, opts)) {
491          exit(0);
492      }
493      return true;
494  }
495  
496  static QemuOptsList file_opts = {
497      .name = "file",
498      .implied_opt_name = "file",
499      .head = QTAILQ_HEAD_INITIALIZER(file_opts.head),
500      .desc = {
501          /* no elements => accept any params */
502          { /* end of list */ }
503      },
504  };
505  
506  int main(int argc, char **argv)
507  {
508      int readonly = 0;
509      const char *sopt = "hVc:d:f:rsnCmki:t:T:U";
510      const struct option lopt[] = {
511          { "help", no_argument, NULL, 'h' },
512          { "version", no_argument, NULL, 'V' },
513          { "cmd", required_argument, NULL, 'c' },
514          { "format", required_argument, NULL, 'f' },
515          { "read-only", no_argument, NULL, 'r' },
516          { "snapshot", no_argument, NULL, 's' },
517          { "nocache", no_argument, NULL, 'n' },
518          { "copy-on-read", no_argument, NULL, 'C' },
519          { "misalign", no_argument, NULL, 'm' },
520          { "native-aio", no_argument, NULL, 'k' },
521          { "aio", required_argument, NULL, 'i' },
522          { "discard", required_argument, NULL, 'd' },
523          { "cache", required_argument, NULL, 't' },
524          { "trace", required_argument, NULL, 'T' },
525          { "object", required_argument, NULL, OPTION_OBJECT },
526          { "image-opts", no_argument, NULL, OPTION_IMAGE_OPTS },
527          { "force-share", no_argument, 0, 'U'},
528          { NULL, 0, NULL, 0 }
529      };
530      int c;
531      int opt_index = 0;
532      int flags = BDRV_O_UNMAP;
533      int ret;
534      bool writethrough = true;
535      Error *local_error = NULL;
536      QDict *opts = NULL;
537      const char *format = NULL;
538      char *trace_file = NULL;
539      bool force_share = false;
540  
541  #ifdef CONFIG_POSIX
542      signal(SIGPIPE, SIG_IGN);
543  #endif
544  
545      error_init(argv[0]);
546      module_call_init(MODULE_INIT_TRACE);
547      qemu_init_exec_dir(argv[0]);
548  
549      qcrypto_init(&error_fatal);
550  
551      module_call_init(MODULE_INIT_QOM);
552      qemu_add_opts(&qemu_object_opts);
553      qemu_add_opts(&qemu_trace_opts);
554      bdrv_init();
555  
556      while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
557          switch (c) {
558          case 's':
559              flags |= BDRV_O_SNAPSHOT;
560              break;
561          case 'n':
562              flags |= BDRV_O_NOCACHE;
563              writethrough = false;
564              break;
565          case 'C':
566              flags |= BDRV_O_COPY_ON_READ;
567              break;
568          case 'd':
569              if (bdrv_parse_discard_flags(optarg, &flags) < 0) {
570                  error_report("Invalid discard option: %s", optarg);
571                  exit(1);
572              }
573              break;
574          case 'f':
575              format = optarg;
576              break;
577          case 'c':
578              add_user_command(optarg);
579              break;
580          case 'r':
581              readonly = 1;
582              break;
583          case 'm':
584              qemuio_misalign = true;
585              break;
586          case 'k':
587              flags |= BDRV_O_NATIVE_AIO;
588              break;
589          case 'i':
590              if (bdrv_parse_aio(optarg, &flags) < 0) {
591                  error_report("Invalid aio option: %s", optarg);
592                  exit(1);
593              }
594              break;
595          case 't':
596              if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) {
597                  error_report("Invalid cache option: %s", optarg);
598                  exit(1);
599              }
600              break;
601          case 'T':
602              g_free(trace_file);
603              trace_file = trace_opt_parse(optarg);
604              break;
605          case 'V':
606              printf("%s version " QEMU_FULL_VERSION "\n"
607                     QEMU_COPYRIGHT "\n", error_get_progname());
608              exit(0);
609          case 'h':
610              usage(error_get_progname());
611              exit(0);
612          case 'U':
613              force_share = true;
614              break;
615          case OPTION_OBJECT: {
616              QemuOpts *qopts;
617              qopts = qemu_opts_parse_noisily(&qemu_object_opts,
618                                              optarg, true);
619              if (!qopts) {
620                  exit(1);
621              }
622          }   break;
623          case OPTION_IMAGE_OPTS:
624              imageOpts = true;
625              break;
626          default:
627              usage(error_get_progname());
628              exit(1);
629          }
630      }
631  
632      if ((argc - optind) > 1) {
633          usage(error_get_progname());
634          exit(1);
635      }
636  
637      if (format && imageOpts) {
638          error_report("--image-opts and -f are mutually exclusive");
639          exit(1);
640      }
641  
642      if (qemu_init_main_loop(&local_error)) {
643          error_report_err(local_error);
644          exit(1);
645      }
646  
647      qemu_opts_foreach(&qemu_object_opts,
648                        user_creatable_add_opts_foreach,
649                        qemu_io_object_print_help, &error_fatal);
650  
651      if (!trace_init_backends()) {
652          exit(1);
653      }
654      trace_init_file(trace_file);
655      qemu_set_log(LOG_TRACE);
656  
657      /* initialize commands */
658      qemuio_add_command(&quit_cmd);
659      qemuio_add_command(&open_cmd);
660      qemuio_add_command(&close_cmd);
661  
662      if (isatty(STDIN_FILENO)) {
663          ttyEOF = get_eof_char();
664          readline_state = readline_init(readline_printf_func,
665                                         readline_flush_func,
666                                         NULL,
667                                         readline_completion_func);
668          qemu_set_tty_echo(STDIN_FILENO, false);
669          atexit(reenable_tty_echo);
670      }
671  
672      /* open the device */
673      if (!readonly) {
674          flags |= BDRV_O_RDWR;
675      }
676  
677      if ((argc - optind) == 1) {
678          if (imageOpts) {
679              QemuOpts *qopts = NULL;
680              qopts = qemu_opts_parse_noisily(&file_opts, argv[optind], false);
681              if (!qopts) {
682                  exit(1);
683              }
684              opts = qemu_opts_to_qdict(qopts, NULL);
685              if (openfile(NULL, flags, writethrough, force_share, opts)) {
686                  exit(1);
687              }
688          } else {
689              if (format) {
690                  opts = qdict_new();
691                  qdict_put_str(opts, "driver", format);
692              }
693              if (openfile(argv[optind], flags, writethrough,
694                           force_share, opts)) {
695                  exit(1);
696              }
697          }
698      }
699      ret = command_loop();
700  
701      /*
702       * Make sure all outstanding requests complete before the program exits.
703       */
704      bdrv_drain_all();
705  
706      blk_unref(qemuio_blk);
707      g_free(readline_state);
708  
709      if (ret < 0) {
710          return 1;
711      } else {
712          return 0;
713      }
714  }
715