Use a bare monitor to capture qemuio_command() printf output. Return it
as HumanReadableText.

Signed-off-by: Marc-André Lureau <[email protected]>
---
 block/monitor/block-hmp-cmds.c | 14 +++++---
 block/monitor/qmp-cmds.c       | 49 +++++++++++++++++++++++-----
 qapi/block.json                |  3 ++
 qemu-io-cmds.c                 | 74 ++++++++++++++++++++++--------------------
 system/qtest.c                 |  4 ++-
 tests/qemu-iotests/iotests.py  |  2 +-
 6 files changed, 95 insertions(+), 51 deletions(-)

diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
index 7bae4d425c6d..ed071c0f4144 100644
--- a/block/monitor/block-hmp-cmds.c
+++ b/block/monitor/block-hmp-cmds.c
@@ -543,12 +543,18 @@ void hmp_qemu_io(MonitorHMP *hmp, const QDict *qdict)
     bool qdev = qdict_get_try_bool(qdict, "qdev", false);
     const char *device = qdict_get_str(qdict, "device");
     const char *command = qdict_get_str(qdict, "command");
+    g_autoptr(HumanReadableText) out = NULL;
     Error *err = NULL;
 
-    qmp_x_qemu_io(qdev ? NULL : device,
-                  qdev ? device : NULL,
-                  command, &err);
-    hmp_handle_error(hmp, err);
+    out = qmp_x_qemu_io(qdev ? NULL : device,
+                        qdev ? device : NULL,
+                        command, &err);
+    if (hmp_handle_error(hmp, err)) {
+        return;
+    }
+
+    /* use stdout and not monitor_puts() for compatiblity reasons */
+    printf("%s", out->human_readable_text);
 }
 
 static void print_block_info(MonitorHMP *hmp, BlockInfo *info,
diff --git a/block/monitor/qmp-cmds.c b/block/monitor/qmp-cmds.c
index e5759d824f13..4ea4d5985833 100644
--- a/block/monitor/qmp-cmds.c
+++ b/block/monitor/qmp-cmds.c
@@ -5,44 +5,51 @@
 #include "block/block_int.h"
 #include "qapi/qapi-commands-block.h"
 #include "qapi/error.h"
+#include "qapi/type-helpers.h"
+#include "monitor/monitor.h"
 #include "qemu-io.h"
 
-void qmp_x_qemu_io(const char *device, const char *qdev,
-                   const char *command, Error **errp)
+HumanReadableText *qmp_x_qemu_io(const char *device, const char *qdev,
+                                 const char *command, Error **errp)
 {
+    ERRP_GUARD();
     BlockBackend *blk = NULL;
     BlockBackend *local_blk = NULL;
     BlockDriverState *bs = NULL;
-    int ret;
+    g_autoptr(Monitor) capture_mon = NULL;
+    g_autoptr(GString) str = NULL;
+    Monitor *old_mon = NULL;
+    HumanReadableText *ret = NULL;
+    int r;
 
     if (!device && !qdev) {
         error_setg(errp, "Must specify either device or qdev");
-        return;
+        return NULL;
     }
     if (qdev && device) {
         error_setg(errp, "Cannot specify both qdev and device");
-        return;
+        return NULL;
     }
 
     if (qdev) {
         blk = blk_by_qdev_id(qdev, errp);
         if (!blk) {
-            return;
+            return NULL;
         }
     } else {
         blk = blk_by_name(device);
         if (!blk) {
             bs = bdrv_lookup_bs(NULL, device, errp);
             if (!bs) {
-                return;
+                return NULL;
             }
         }
     }
 
     if (bs) {
         blk = local_blk = blk_new(bdrv_get_aio_context(bs), 0, BLK_PERM_ALL);
-        ret = blk_insert_bs(blk, bs, errp);
-        if (ret < 0) {
+        r = blk_insert_bs(blk, bs, errp);
+        if (r < 0) {
             goto fail;
         }
     }
@@ -72,8 +79,32 @@ void qmp_x_qemu_io(const char *device, const char *qdev,
      * extended, possibly resulting in a read-only guest device keeping write
      * permissions. Ugly, but it appears to be the lesser evil.
      */
+
+    /*
+     * qemuio_command() doesn't return structured output, so we need
+     * to capture the monitor output, use a bare monitor for that.
+     */
+    capture_mon = MONITOR(object_new(TYPE_MONITOR));
+    old_mon = monitor_set_cur(qemu_coroutine_self(), capture_mon);
+
     qemuio_command(blk, command, errp);
 
+    monitor_set_cur(qemu_coroutine_self(), old_mon);
+
+    if (*errp) {
+        goto fail;
+    }
+
+    str = monitor_steal_output(capture_mon);
+
+    /*
+     * monitor_puts_locked() adds \r before \n for terminal display;
+     * strip those from captured output
+     */
+    g_string_replace(str, "\r", "", 0);
+    ret = human_readable_text_from_str(str);
+
 fail:
     blk_unref(local_blk);
+    return ret;
 }
diff --git a/qapi/block.json b/qapi/block.json
index 012a9d73333b..da135f419386 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -618,6 +618,8 @@
 #
 # @command: the qemu-io command string to execute.
 #
+# Returns: qemu-io output as a string
+#
 # Features:
 #
 # @unstable: This command is for testing only.
@@ -635,5 +637,6 @@
   'data': { '*device': 'str',
             '*qdev': 'str',
             'command': 'str' },
+  'returns': 'HumanReadableText',
   'features': [ 'unstable' ],
   'allow-preconfig': true }
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index aa795fd87a14..28b810c25e58 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -17,6 +17,7 @@
 #include "block/block_int.h" /* for info_f() */
 #include "block/qapi.h"
 #include "qemu/error-report.h"
+#include "qemu/qemu-print.h"
 #include "qemu/main-loop.h"
 #include "qemu/option.h"
 #include "qemu/timer.h"
@@ -52,7 +53,7 @@ void qemuio_add_command(const cmdinfo_t *ci)
 
 void qemuio_command_usage(const cmdinfo_t *ci)
 {
-    printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
+    qemu_printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
 }
 
 static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct,
@@ -453,19 +454,19 @@ static void dump_buffer(const void *buffer, int64_t 
offset, int64_t len)
     for (i = 0, p = buffer; i < len; i += 16) {
         const uint8_t *s = p;
 
-        printf("%08" PRIx64 ":  ", offset + i);
+        qemu_printf("%08" PRIx64 ":  ", offset + i);
         for (j = 0; j < 16 && i + j < len; j++, p++) {
-            printf("%02x ", *p);
+            qemu_printf("%02x ", *p);
         }
-        printf(" ");
+        qemu_printf(" ");
         for (j = 0; j < 16 && i + j < len; j++, s++) {
             if (isalnum(*s)) {
-                printf("%c", *s);
+                qemu_printf("%c", *s);
             } else {
-                printf(".");
+                qemu_printf(".");
             }
         }
-        printf("\n");
+        qemu_printf("\n");
     }
 }
 
@@ -478,12 +479,12 @@ static void print_report(const char *op, struct timespec 
*t, int64_t offset,
     if (!Cflag) {
         cvtstr((double)total, s1, sizeof(s1));
         cvtstr(tdiv((double)total, *t), s2, sizeof(s2));
-        printf("%s %"PRId64"/%"PRId64" bytes at offset %" PRId64 "\n",
+        qemu_printf("%s %"PRId64"/%"PRId64" bytes at offset %" PRId64 "\n",
                op, total, count, offset);
-        printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n",
+        qemu_printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n",
                s1, cnt, ts, s2, tdiv((double)cnt, *t));
     } else {/* bytes,ops,time,bytes/sec,ops/sec */
-        printf("%"PRId64",%d,%s,%.3f,%.3f\n",
+        qemu_printf("%"PRId64",%d,%s,%.3f,%.3f\n",
             total, cnt, ts,
             tdiv((double)total, *t),
             tdiv((double)cnt, *t));
@@ -674,7 +675,7 @@ static int do_aio_writev(BlockBackend *blk, QEMUIOVector 
*qiov,
 
 static void read_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " reads a range of bytes from the given offset\n"
 "\n"
@@ -877,7 +878,7 @@ out:
 
 static void readv_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " reads a range of bytes from the given offset into multiple buffers\n"
 "\n"
@@ -1012,7 +1013,7 @@ out:
 
 static void write_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " writes a range of bytes from the given offset\n"
 "\n"
@@ -1239,7 +1240,7 @@ out:
 static void
 writev_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " writes a range of bytes from the given offset source from multiple buffers\n"
 "\n"
@@ -1449,7 +1450,7 @@ out:
 
 static void aio_read_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " asynchronously reads a range of bytes from the given offset\n"
 "\n"
@@ -1504,7 +1505,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char 
**argv, Error **errp)
             }
             break;
         case 'i':
-            printf("injecting invalid read request\n");
+            qemu_printf("injecting invalid read request\n");
             block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ);
             g_free(ctx);
             return 0;
@@ -1558,7 +1559,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char 
**argv, Error **errp)
 
 static void aio_write_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " asynchronously writes a range of bytes from the given offset source\n"
 " from multiple buffers\n"
@@ -1629,7 +1630,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char 
**argv, Error **errp)
             }
             break;
         case 'i':
-            printf("injecting invalid write request\n");
+            qemu_printf("injecting invalid write request\n");
             block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE);
             g_free(ctx);
             return 0;
@@ -1780,7 +1781,7 @@ static int zone_report_f(BlockBackend *blk, int argc, 
char **argv, Error **errp)
         error_setg(errp, "zone report failed: %s", strerror(-ret));
     } else {
         for (int i = 0; i < nr_zones; ++i) {
-            printf("start: 0x%" PRIx64 ", len 0x%" PRIx64 ", "
+            qemu_printf("start: 0x%" PRIx64 ", len 0x%" PRIx64 ", "
                    "cap"" 0x%" PRIx64 ", wptr 0x%" PRIx64 ", "
                    "zcond:%u, [type: %u]\n",
                     tosector(zones[i].start), tosector(zones[i].length),
@@ -1986,7 +1987,7 @@ static int zone_append_f(BlockBackend *blk, int argc, 
char **argv, Error **errp)
     }
 
     if (pflag) {
-        printf("After zap done, the append sector is 0x%" PRIx64 "\n",
+        qemu_printf("After zap done, the append sector is 0x%" PRIx64 "\n",
                tosector(offset));
     }
 
@@ -2072,7 +2073,7 @@ static int length_f(BlockBackend *blk, int argc, char 
**argv, Error **errp)
     }
 
     cvtstr(size, s1, sizeof(s1));
-    printf("%s\n", s1);
+    qemu_printf("%s\n", s1);
     return 0;
 }
 
@@ -2098,10 +2099,10 @@ static int info_f(BlockBackend *blk, int argc, char 
**argv, Error **errp)
     GRAPH_RDLOCK_GUARD_MAINLOOP();
 
     if (bs->drv && bs->drv->format_name) {
-        printf("format name: %s\n", bs->drv->format_name);
+        qemu_printf("format name: %s\n", bs->drv->format_name);
     }
     if (bs->drv && bs->drv->protocol_name) {
-        printf("format name: %s\n", bs->drv->protocol_name);
+        qemu_printf("format name: %s\n", bs->drv->protocol_name);
     }
 
     ret = bdrv_get_info(bs, &bdi);
@@ -2112,8 +2113,8 @@ static int info_f(BlockBackend *blk, int argc, char 
**argv, Error **errp)
     cvtstr(bdi.cluster_size, s1, sizeof(s1));
     cvtstr(bdi.vm_state_offset, s2, sizeof(s2));
 
-    printf("cluster size: %s\n", s1);
-    printf("vm state offset: %s\n", s2);
+    qemu_printf("cluster size: %s\n", s1);
+    qemu_printf("vm state offset: %s\n", s2);
 
     spec_info = bdrv_get_specific_info(bs, errp);
     if (*errp) {
@@ -2140,7 +2141,7 @@ static const cmdinfo_t info_cmd = {
 
 static void discard_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " discards a range of bytes from the given offset\n"
 "\n"
@@ -2230,7 +2231,7 @@ static int discard_f(BlockBackend *blk, int argc, char 
**argv, Error **errp)
 
 static void aio_discard_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " asynchronously discards a range of bytes from the given offset\n"
 "\n"
@@ -2388,7 +2389,7 @@ static int alloc_f(BlockBackend *blk, int argc, char 
**argv, Error **errp)
 
     cvtstr(start, s1, sizeof(s1));
 
-    printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n",
+    qemu_printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n",
            sum_alloc, count, s1);
     return 0;
 }
@@ -2462,8 +2463,9 @@ static int map_f(BlockBackend *blk, int argc, char 
**argv, Error **errp)
         retstr = ret ? "    allocated" : "not allocated";
         cvtstr(num, s1, sizeof(s1));
         cvtstr(offset, s2, sizeof(s2));
-        printf("%s (0x%" PRIx64 ") bytes %s at offset %s (0x%" PRIx64 ")\n",
-               s1, num, retstr, s2, offset);
+        qemu_printf("%s (0x%" PRIx64 ") bytes %s"
+                    " at offset %s (0x%" PRIx64 ")\n",
+                    s1, num, retstr, s2, offset);
 
         offset += num;
         bytes -= num;
@@ -2483,7 +2485,7 @@ static const cmdinfo_t map_cmd = {
 
 static void reopen_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " Changes the open options of an already opened image\n"
 "\n"
@@ -2729,7 +2731,7 @@ static const cmdinfo_t abort_cmd = {
 
 static void sigraise_help(void)
 {
-    printf(
+    qemu_printf(
 "\n"
 " raises the given signal\n"
 "\n"
@@ -2819,12 +2821,12 @@ static const cmdinfo_t sleep_cmd = {
 
 static void help_oneline(const char *cmd, const cmdinfo_t *ct)
 {
-    printf("%s ", cmd);
+    qemu_printf("%s ", cmd);
 
     if (ct->args) {
-        printf("%s ", ct->args);
+        qemu_printf("%s ", ct->args);
     }
-    printf("-- %s\n", ct->oneline);
+    qemu_printf("-- %s\n", ct->oneline);
 }
 
 static void help_onecmd(const char *cmd, const cmdinfo_t *ct)
@@ -2842,7 +2844,7 @@ static void help_all(void)
     for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
         help_oneline(ct->name, ct);
     }
-    printf("\nUse 'help commandname' for extended help.\n");
+    qemu_printf("\nUse 'help commandname' for extended help.\n");
 }
 
 static int help_f(BlockBackend *blk, int argc, char **argv, Error **errp)
diff --git a/system/qtest.c b/system/qtest.c
index 47c954838dd0..e07c7acf34fc 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -801,13 +801,15 @@ static void qtest_process_command(CharFrontend *chr, 
gchar **words)
     } else if (strcmp(words[0], "qemu-io") == 0) {
         const char *io_dev;
         g_autofree char *io_cmd = NULL;
+        /* ignore multi-line output for now */
+        g_autoptr(HumanReadableText) out = NULL;
         Error *err = NULL;
 
         g_assert(words[1] && words[2]);
         io_dev = words[1];
         io_cmd = g_strjoinv(" ", &words[2]);
 
-        qmp_x_qemu_io(io_dev, NULL, io_cmd, &err);
+        out = qmp_x_qemu_io(io_dev, NULL, io_cmd, &err);
         qtest_sendf(chr, err ? "FAIL %s\n" : "OK\n",
                     err ? error_get_pretty(err) : NULL);
         error_free(err);
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 7d5a77a85e58..445a22a8659a 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -965,7 +965,7 @@ def qmp_qemu_io(self, drive: str, cmd: str,
             res = self.qmp_log('x-qemu-io', **kwargs)
         else:
             res = self.qmp('x-qemu-io', **kwargs)
-        return res.get('error', {}).get('desc', '')
+        return res.get('return', {}).get('human-readable-text', '')
 
     def flatten_qmp_object(self, obj, output=None, basestr=''):
         if output is None:

-- 
2.56.0.rc0.29.g47ce80527c56


Reply via email to