qemu-patch-raspberry4/hmp.c
Daniel P. Berrange d59ce6f344 migration: add reporting of errors for outgoing migration
Currently if an application initiates an outgoing migration,
it may or may not, get an error reported back on failure. If
the error occurs synchronously to the 'migrate' command
execution, the client app will see the error message. This
is the case for DNS lookup failures. If the error occurs
asynchronously to the monitor command though, the error
will be thrown away and the client left guessing about
what went wrong. This is the case for failure to connect
to the TCP server (eg due to wrong port, or firewall
rules, or other similar errors).

In the future we'll be adding more scope for errors to
happen asynchronously with the TLS protocol handshake.
TLS errors are hard to diagnose even when they are well
reported, so discarding errors entirely will make it
impossible to debug TLS connection problems.

Management apps which do migration are already using
'query-migrate' / 'info migrate' to check up on progress
of background migration operations and to see their end
status. This is a fine place to also include the error
message when things go wrong.

This patch thus adds an 'error-desc' field to the
MigrationInfo struct, which will be populated when
the 'status' is set to 'failed':

(qemu) migrate -d tcp:localhost:9001
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off events: off x-postcopy-ram: off
Migration status: failed (Error connecting to socket: Connection refused)
total time: 0 milliseconds

In the HMP, when doing non-detached migration, it is
also possible to display this error message directly
to the app.

(qemu) migrate tcp:localhost:9001
Error connecting to socket: Connection refused

Or with QMP

  {
    "execute": "query-migrate",
    "arguments": {}
  }
  {
    "return": {
      "status": "failed",
      "error-desc": "address resolution failed for myhost:9000: No address associated with hostname"
    }
  }

Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <1461751518-12128-11-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-05-26 11:31:30 +05:30

2395 lines
72 KiB
C

/*
* Human Monitor Interface
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "hmp.h"
#include "net/net.h"
#include "net/eth.h"
#include "sysemu/char.h"
#include "sysemu/block-backend.h"
#include "qemu/option.h"
#include "qemu/timer.h"
#include "qmp-commands.h"
#include "qemu/sockets.h"
#include "monitor/monitor.h"
#include "monitor/qdev.h"
#include "qapi/opts-visitor.h"
#include "qapi/qmp/qerror.h"
#include "qapi/string-output-visitor.h"
#include "qapi/util.h"
#include "qapi-visit.h"
#include "qom/object_interfaces.h"
#include "ui/console.h"
#include "block/qapi.h"
#include "qemu-io.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#ifdef CONFIG_SPICE
#include <spice/enums.h>
#endif
static void hmp_handle_error(Monitor *mon, Error **errp)
{
assert(errp);
if (*errp) {
error_report_err(*errp);
}
}
void hmp_info_name(Monitor *mon, const QDict *qdict)
{
NameInfo *info;
info = qmp_query_name(NULL);
if (info->has_name) {
monitor_printf(mon, "%s\n", info->name);
}
qapi_free_NameInfo(info);
}
void hmp_info_version(Monitor *mon, const QDict *qdict)
{
VersionInfo *info;
info = qmp_query_version(NULL);
monitor_printf(mon, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n",
info->qemu->major, info->qemu->minor, info->qemu->micro,
info->package);
qapi_free_VersionInfo(info);
}
void hmp_info_kvm(Monitor *mon, const QDict *qdict)
{
KvmInfo *info;
info = qmp_query_kvm(NULL);
monitor_printf(mon, "kvm support: ");
if (info->present) {
monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled");
} else {
monitor_printf(mon, "not compiled\n");
}
qapi_free_KvmInfo(info);
}
void hmp_info_status(Monitor *mon, const QDict *qdict)
{
StatusInfo *info;
info = qmp_query_status(NULL);
monitor_printf(mon, "VM status: %s%s",
info->running ? "running" : "paused",
info->singlestep ? " (single step mode)" : "");
if (!info->running && info->status != RUN_STATE_PAUSED) {
monitor_printf(mon, " (%s)", RunState_lookup[info->status]);
}
monitor_printf(mon, "\n");
qapi_free_StatusInfo(info);
}
void hmp_info_uuid(Monitor *mon, const QDict *qdict)
{
UuidInfo *info;
info = qmp_query_uuid(NULL);
monitor_printf(mon, "%s\n", info->UUID);
qapi_free_UuidInfo(info);
}
void hmp_info_chardev(Monitor *mon, const QDict *qdict)
{
ChardevInfoList *char_info, *info;
char_info = qmp_query_chardev(NULL);
for (info = char_info; info; info = info->next) {
monitor_printf(mon, "%s: filename=%s\n", info->value->label,
info->value->filename);
}
qapi_free_ChardevInfoList(char_info);
}
void hmp_info_mice(Monitor *mon, const QDict *qdict)
{
MouseInfoList *mice_list, *mouse;
mice_list = qmp_query_mice(NULL);
if (!mice_list) {
monitor_printf(mon, "No mouse devices connected\n");
return;
}
for (mouse = mice_list; mouse; mouse = mouse->next) {
monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n",
mouse->value->current ? '*' : ' ',
mouse->value->index, mouse->value->name,
mouse->value->absolute ? " (absolute)" : "");
}
qapi_free_MouseInfoList(mice_list);
}
void hmp_info_migrate(Monitor *mon, const QDict *qdict)
{
MigrationInfo *info;
MigrationCapabilityStatusList *caps, *cap;
info = qmp_query_migrate(NULL);
caps = qmp_query_migrate_capabilities(NULL);
/* do not display parameters during setup */
if (info->has_status && caps) {
monitor_printf(mon, "capabilities: ");
for (cap = caps; cap; cap = cap->next) {
monitor_printf(mon, "%s: %s ",
MigrationCapability_lookup[cap->value->capability],
cap->value->state ? "on" : "off");
}
monitor_printf(mon, "\n");
}
if (info->has_status) {
monitor_printf(mon, "Migration status: %s",
MigrationStatus_lookup[info->status]);
if (info->status == MIGRATION_STATUS_FAILED &&
info->has_error_desc) {
monitor_printf(mon, " (%s)\n", info->error_desc);
} else {
monitor_printf(mon, "\n");
}
monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
info->total_time);
if (info->has_expected_downtime) {
monitor_printf(mon, "expected downtime: %" PRIu64 " milliseconds\n",
info->expected_downtime);
}
if (info->has_downtime) {
monitor_printf(mon, "downtime: %" PRIu64 " milliseconds\n",
info->downtime);
}
if (info->has_setup_time) {
monitor_printf(mon, "setup: %" PRIu64 " milliseconds\n",
info->setup_time);
}
}
if (info->has_ram) {
monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n",
info->ram->transferred >> 10);
monitor_printf(mon, "throughput: %0.2f mbps\n",
info->ram->mbps);
monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n",
info->ram->remaining >> 10);
monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n",
info->ram->total >> 10);
monitor_printf(mon, "duplicate: %" PRIu64 " pages\n",
info->ram->duplicate);
monitor_printf(mon, "skipped: %" PRIu64 " pages\n",
info->ram->skipped);
monitor_printf(mon, "normal: %" PRIu64 " pages\n",
info->ram->normal);
monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n",
info->ram->normal_bytes >> 10);
monitor_printf(mon, "dirty sync count: %" PRIu64 "\n",
info->ram->dirty_sync_count);
if (info->ram->dirty_pages_rate) {
monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n",
info->ram->dirty_pages_rate);
}
}
if (info->has_disk) {
monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n",
info->disk->transferred >> 10);
monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n",
info->disk->remaining >> 10);
monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n",
info->disk->total >> 10);
}
if (info->has_xbzrle_cache) {
monitor_printf(mon, "cache size: %" PRIu64 " bytes\n",
info->xbzrle_cache->cache_size);
monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n",
info->xbzrle_cache->bytes >> 10);
monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n",
info->xbzrle_cache->pages);
monitor_printf(mon, "xbzrle cache miss: %" PRIu64 "\n",
info->xbzrle_cache->cache_miss);
monitor_printf(mon, "xbzrle cache miss rate: %0.2f\n",
info->xbzrle_cache->cache_miss_rate);
monitor_printf(mon, "xbzrle overflow : %" PRIu64 "\n",
info->xbzrle_cache->overflow);
}
if (info->has_cpu_throttle_percentage) {
monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n",
info->cpu_throttle_percentage);
}
qapi_free_MigrationInfo(info);
qapi_free_MigrationCapabilityStatusList(caps);
}
void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict)
{
MigrationCapabilityStatusList *caps, *cap;
caps = qmp_query_migrate_capabilities(NULL);
if (caps) {
monitor_printf(mon, "capabilities: ");
for (cap = caps; cap; cap = cap->next) {
monitor_printf(mon, "%s: %s ",
MigrationCapability_lookup[cap->value->capability],
cap->value->state ? "on" : "off");
}
monitor_printf(mon, "\n");
}
qapi_free_MigrationCapabilityStatusList(caps);
}
void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
{
MigrationParameters *params;
params = qmp_query_migrate_parameters(NULL);
if (params) {
monitor_printf(mon, "parameters:");
monitor_printf(mon, " %s: %" PRId64,
MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_LEVEL],
params->compress_level);
monitor_printf(mon, " %s: %" PRId64,
MigrationParameter_lookup[MIGRATION_PARAMETER_COMPRESS_THREADS],
params->compress_threads);
monitor_printf(mon, " %s: %" PRId64,
MigrationParameter_lookup[MIGRATION_PARAMETER_DECOMPRESS_THREADS],
params->decompress_threads);
monitor_printf(mon, " %s: %" PRId64,
MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL],
params->cpu_throttle_initial);
monitor_printf(mon, " %s: %" PRId64,
MigrationParameter_lookup[MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT],
params->cpu_throttle_increment);
monitor_printf(mon, "\n");
}
qapi_free_MigrationParameters(params);
}
void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "xbzrel cache size: %" PRId64 " kbytes\n",
qmp_query_migrate_cache_size(NULL) >> 10);
}
void hmp_info_cpus(Monitor *mon, const QDict *qdict)
{
CpuInfoList *cpu_list, *cpu;
cpu_list = qmp_query_cpus(NULL);
for (cpu = cpu_list; cpu; cpu = cpu->next) {
int active = ' ';
if (cpu->value->CPU == monitor_get_cpu_index()) {
active = '*';
}
monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU);
switch (cpu->value->arch) {
case CPU_INFO_ARCH_X86:
monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86.pc);
break;
case CPU_INFO_ARCH_PPC:
monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc.nip);
break;
case CPU_INFO_ARCH_SPARC:
monitor_printf(mon, " pc=0x%016" PRIx64,
cpu->value->u.q_sparc.pc);
monitor_printf(mon, " npc=0x%016" PRIx64,
cpu->value->u.q_sparc.npc);
break;
case CPU_INFO_ARCH_MIPS:
monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.q_mips.PC);
break;
case CPU_INFO_ARCH_TRICORE:
monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore.PC);
break;
default:
break;
}
if (cpu->value->halted) {
monitor_printf(mon, " (halted)");
}
monitor_printf(mon, " thread_id=%" PRId64 "\n", cpu->value->thread_id);
}
qapi_free_CpuInfoList(cpu_list);
}
static void print_block_info(Monitor *mon, BlockInfo *info,
BlockDeviceInfo *inserted, bool verbose)
{
ImageInfo *image_info;
assert(!info || !info->has_inserted || info->inserted == inserted);
if (info) {
monitor_printf(mon, "%s", info->device);
if (inserted && inserted->has_node_name) {
monitor_printf(mon, " (%s)", inserted->node_name);
}
} else {
assert(inserted);
monitor_printf(mon, "%s",
inserted->has_node_name
? inserted->node_name
: "<anonymous>");
}
if (inserted) {
monitor_printf(mon, ": %s (%s%s%s)\n",
inserted->file,
inserted->drv,
inserted->ro ? ", read-only" : "",
inserted->encrypted ? ", encrypted" : "");
} else {
monitor_printf(mon, ": [not inserted]\n");
}
if (info) {
if (info->has_io_status && info->io_status != BLOCK_DEVICE_IO_STATUS_OK) {
monitor_printf(mon, " I/O status: %s\n",
BlockDeviceIoStatus_lookup[info->io_status]);
}
if (info->removable) {
monitor_printf(mon, " Removable device: %slocked, tray %s\n",
info->locked ? "" : "not ",
info->tray_open ? "open" : "closed");
}
}
if (!inserted) {
return;
}
monitor_printf(mon, " Cache mode: %s%s%s\n",
inserted->cache->writeback ? "writeback" : "writethrough",
inserted->cache->direct ? ", direct" : "",
inserted->cache->no_flush ? ", ignore flushes" : "");
if (inserted->has_backing_file) {
monitor_printf(mon,
" Backing file: %s "
"(chain depth: %" PRId64 ")\n",
inserted->backing_file,
inserted->backing_file_depth);
}
if (inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) {
monitor_printf(mon, " Detect zeroes: %s\n",
BlockdevDetectZeroesOptions_lookup[inserted->detect_zeroes]);
}
if (inserted->bps || inserted->bps_rd || inserted->bps_wr ||
inserted->iops || inserted->iops_rd || inserted->iops_wr)
{
monitor_printf(mon, " I/O throttling: bps=%" PRId64
" bps_rd=%" PRId64 " bps_wr=%" PRId64
" bps_max=%" PRId64
" bps_rd_max=%" PRId64
" bps_wr_max=%" PRId64
" iops=%" PRId64 " iops_rd=%" PRId64
" iops_wr=%" PRId64
" iops_max=%" PRId64
" iops_rd_max=%" PRId64
" iops_wr_max=%" PRId64
" iops_size=%" PRId64
" group=%s\n",
inserted->bps,
inserted->bps_rd,
inserted->bps_wr,
inserted->bps_max,
inserted->bps_rd_max,
inserted->bps_wr_max,
inserted->iops,
inserted->iops_rd,
inserted->iops_wr,
inserted->iops_max,
inserted->iops_rd_max,
inserted->iops_wr_max,
inserted->iops_size,
inserted->group);
}
if (verbose) {
monitor_printf(mon, "\nImages:\n");
image_info = inserted->image;
while (1) {
bdrv_image_info_dump((fprintf_function)monitor_printf,
mon, image_info);
if (image_info->has_backing_image) {
image_info = image_info->backing_image;
} else {
break;
}
}
}
}
void hmp_info_block(Monitor *mon, const QDict *qdict)
{
BlockInfoList *block_list, *info;
BlockDeviceInfoList *blockdev_list, *blockdev;
const char *device = qdict_get_try_str(qdict, "device");
bool verbose = qdict_get_try_bool(qdict, "verbose", false);
bool nodes = qdict_get_try_bool(qdict, "nodes", false);
bool printed = false;
/* Print BlockBackend information */
if (!nodes) {
block_list = qmp_query_block(NULL);
} else {
block_list = NULL;
}
for (info = block_list; info; info = info->next) {
if (device && strcmp(device, info->value->device)) {
continue;
}
if (info != block_list) {
monitor_printf(mon, "\n");
}
print_block_info(mon, info->value, info->value->has_inserted
? info->value->inserted : NULL,
verbose);
printed = true;
}
qapi_free_BlockInfoList(block_list);
if ((!device && !nodes) || printed) {
return;
}
/* Print node information */
blockdev_list = qmp_query_named_block_nodes(NULL);
for (blockdev = blockdev_list; blockdev; blockdev = blockdev->next) {
assert(blockdev->value->has_node_name);
if (device && strcmp(device, blockdev->value->node_name)) {
continue;
}
if (blockdev != blockdev_list) {
monitor_printf(mon, "\n");
}
print_block_info(mon, NULL, blockdev->value, verbose);
}
qapi_free_BlockDeviceInfoList(blockdev_list);
}
void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
{
BlockStatsList *stats_list, *stats;
stats_list = qmp_query_blockstats(false, false, NULL);
for (stats = stats_list; stats; stats = stats->next) {
if (!stats->value->has_device) {
continue;
}
monitor_printf(mon, "%s:", stats->value->device);
monitor_printf(mon, " rd_bytes=%" PRId64
" wr_bytes=%" PRId64
" rd_operations=%" PRId64
" wr_operations=%" PRId64
" flush_operations=%" PRId64
" wr_total_time_ns=%" PRId64
" rd_total_time_ns=%" PRId64
" flush_total_time_ns=%" PRId64
" rd_merged=%" PRId64
" wr_merged=%" PRId64
" idle_time_ns=%" PRId64
"\n",
stats->value->stats->rd_bytes,
stats->value->stats->wr_bytes,
stats->value->stats->rd_operations,
stats->value->stats->wr_operations,
stats->value->stats->flush_operations,
stats->value->stats->wr_total_time_ns,
stats->value->stats->rd_total_time_ns,
stats->value->stats->flush_total_time_ns,
stats->value->stats->rd_merged,
stats->value->stats->wr_merged,
stats->value->stats->idle_time_ns);
}
qapi_free_BlockStatsList(stats_list);
}
void hmp_info_vnc(Monitor *mon, const QDict *qdict)
{
VncInfo *info;
Error *err = NULL;
VncClientInfoList *client;
info = qmp_query_vnc(&err);
if (err) {
error_report_err(err);
return;
}
if (!info->enabled) {
monitor_printf(mon, "Server: disabled\n");
goto out;
}
monitor_printf(mon, "Server:\n");
if (info->has_host && info->has_service) {
monitor_printf(mon, " address: %s:%s\n", info->host, info->service);
}
if (info->has_auth) {
monitor_printf(mon, " auth: %s\n", info->auth);
}
if (!info->has_clients || info->clients == NULL) {
monitor_printf(mon, "Client: none\n");
} else {
for (client = info->clients; client; client = client->next) {
monitor_printf(mon, "Client:\n");
monitor_printf(mon, " address: %s:%s\n",
client->value->host,
client->value->service);
monitor_printf(mon, " x509_dname: %s\n",
client->value->x509_dname ?
client->value->x509_dname : "none");
monitor_printf(mon, " username: %s\n",
client->value->has_sasl_username ?
client->value->sasl_username : "none");
}
}
out:
qapi_free_VncInfo(info);
}
#ifdef CONFIG_SPICE
void hmp_info_spice(Monitor *mon, const QDict *qdict)
{
SpiceChannelList *chan;
SpiceInfo *info;
const char *channel_name;
const char * const channel_names[] = {
[SPICE_CHANNEL_MAIN] = "main",
[SPICE_CHANNEL_DISPLAY] = "display",
[SPICE_CHANNEL_INPUTS] = "inputs",
[SPICE_CHANNEL_CURSOR] = "cursor",
[SPICE_CHANNEL_PLAYBACK] = "playback",
[SPICE_CHANNEL_RECORD] = "record",
[SPICE_CHANNEL_TUNNEL] = "tunnel",
[SPICE_CHANNEL_SMARTCARD] = "smartcard",
[SPICE_CHANNEL_USBREDIR] = "usbredir",
[SPICE_CHANNEL_PORT] = "port",
#if 0
/* minimum spice-protocol is 0.12.3, webdav was added in 0.12.7,
* no easy way to #ifdef (SPICE_CHANNEL_* is a enum). Disable
* as quick fix for build failures with older versions. */
[SPICE_CHANNEL_WEBDAV] = "webdav",
#endif
};
info = qmp_query_spice(NULL);
if (!info->enabled) {
monitor_printf(mon, "Server: disabled\n");
goto out;
}
monitor_printf(mon, "Server:\n");
if (info->has_port) {
monitor_printf(mon, " address: %s:%" PRId64 "\n",
info->host, info->port);
}
if (info->has_tls_port) {
monitor_printf(mon, " address: %s:%" PRId64 " [tls]\n",
info->host, info->tls_port);
}
monitor_printf(mon, " migrated: %s\n",
info->migrated ? "true" : "false");
monitor_printf(mon, " auth: %s\n", info->auth);
monitor_printf(mon, " compiled: %s\n", info->compiled_version);
monitor_printf(mon, " mouse-mode: %s\n",
SpiceQueryMouseMode_lookup[info->mouse_mode]);
if (!info->has_channels || info->channels == NULL) {
monitor_printf(mon, "Channels: none\n");
} else {
for (chan = info->channels; chan; chan = chan->next) {
monitor_printf(mon, "Channel:\n");
monitor_printf(mon, " address: %s:%s%s\n",
chan->value->host, chan->value->port,
chan->value->tls ? " [tls]" : "");
monitor_printf(mon, " session: %" PRId64 "\n",
chan->value->connection_id);
monitor_printf(mon, " channel: %" PRId64 ":%" PRId64 "\n",
chan->value->channel_type, chan->value->channel_id);
channel_name = "unknown";
if (chan->value->channel_type > 0 &&
chan->value->channel_type < ARRAY_SIZE(channel_names) &&
channel_names[chan->value->channel_type]) {
channel_name = channel_names[chan->value->channel_type];
}
monitor_printf(mon, " channel name: %s\n", channel_name);
}
}
out:
qapi_free_SpiceInfo(info);
}
#endif
void hmp_info_balloon(Monitor *mon, const QDict *qdict)
{
BalloonInfo *info;
Error *err = NULL;
info = qmp_query_balloon(&err);
if (err) {
error_report_err(err);
return;
}
monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20);
qapi_free_BalloonInfo(info);
}
static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev)
{
PciMemoryRegionList *region;
monitor_printf(mon, " Bus %2" PRId64 ", ", dev->bus);
monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n",
dev->slot, dev->function);
monitor_printf(mon, " ");
if (dev->class_info->has_desc) {
monitor_printf(mon, "%s", dev->class_info->desc);
} else {
monitor_printf(mon, "Class %04" PRId64, dev->class_info->q_class);
}
monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n",
dev->id->vendor, dev->id->device);
if (dev->has_irq) {
monitor_printf(mon, " IRQ %" PRId64 ".\n", dev->irq);
}
if (dev->has_pci_bridge) {
monitor_printf(mon, " BUS %" PRId64 ".\n",
dev->pci_bridge->bus->number);
monitor_printf(mon, " secondary bus %" PRId64 ".\n",
dev->pci_bridge->bus->secondary);
monitor_printf(mon, " subordinate bus %" PRId64 ".\n",
dev->pci_bridge->bus->subordinate);
monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n",
dev->pci_bridge->bus->io_range->base,
dev->pci_bridge->bus->io_range->limit);
monitor_printf(mon,
" memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n",
dev->pci_bridge->bus->memory_range->base,
dev->pci_bridge->bus->memory_range->limit);
monitor_printf(mon, " prefetchable memory range "
"[0x%08"PRIx64", 0x%08"PRIx64"]\n",
dev->pci_bridge->bus->prefetchable_range->base,
dev->pci_bridge->bus->prefetchable_range->limit);
}
for (region = dev->regions; region; region = region->next) {
uint64_t addr, size;
addr = region->value->address;
size = region->value->size;
monitor_printf(mon, " BAR%" PRId64 ": ", region->value->bar);
if (!strcmp(region->value->type, "io")) {
monitor_printf(mon, "I/O at 0x%04" PRIx64
" [0x%04" PRIx64 "].\n",
addr, addr + size - 1);
} else {
monitor_printf(mon, "%d bit%s memory at 0x%08" PRIx64
" [0x%08" PRIx64 "].\n",
region->value->mem_type_64 ? 64 : 32,
region->value->prefetch ? " prefetchable" : "",
addr, addr + size - 1);
}
}
monitor_printf(mon, " id \"%s\"\n", dev->qdev_id);
if (dev->has_pci_bridge) {
if (dev->pci_bridge->has_devices) {
PciDeviceInfoList *cdev;
for (cdev = dev->pci_bridge->devices; cdev; cdev = cdev->next) {
hmp_info_pci_device(mon, cdev->value);
}
}
}
}
void hmp_info_pci(Monitor *mon, const QDict *qdict)
{
PciInfoList *info_list, *info;
Error *err = NULL;
info_list = qmp_query_pci(&err);
if (err) {
monitor_printf(mon, "PCI devices not supported\n");
error_free(err);
return;
}
for (info = info_list; info; info = info->next) {
PciDeviceInfoList *dev;
for (dev = info->value->devices; dev; dev = dev->next) {
hmp_info_pci_device(mon, dev->value);
}
}
qapi_free_PciInfoList(info_list);
}
void hmp_info_block_jobs(Monitor *mon, const QDict *qdict)
{
BlockJobInfoList *list;
Error *err = NULL;
list = qmp_query_block_jobs(&err);
assert(!err);
if (!list) {
monitor_printf(mon, "No active jobs\n");
return;
}
while (list) {
if (strcmp(list->value->type, "stream") == 0) {
monitor_printf(mon, "Streaming device %s: Completed %" PRId64
" of %" PRId64 " bytes, speed limit %" PRId64
" bytes/s\n",
list->value->device,
list->value->offset,
list->value->len,
list->value->speed);
} else {
monitor_printf(mon, "Type %s, device %s: Completed %" PRId64
" of %" PRId64 " bytes, speed limit %" PRId64
" bytes/s\n",
list->value->type,
list->value->device,
list->value->offset,
list->value->len,
list->value->speed);
}
list = list->next;
}
qapi_free_BlockJobInfoList(list);
}
void hmp_info_tpm(Monitor *mon, const QDict *qdict)
{
TPMInfoList *info_list, *info;
Error *err = NULL;
unsigned int c = 0;
TPMPassthroughOptions *tpo;
info_list = qmp_query_tpm(&err);
if (err) {
monitor_printf(mon, "TPM device not supported\n");
error_free(err);
return;
}
if (info_list) {
monitor_printf(mon, "TPM device:\n");
}
for (info = info_list; info; info = info->next) {
TPMInfo *ti = info->value;
monitor_printf(mon, " tpm%d: model=%s\n",
c, TpmModel_lookup[ti->model]);
monitor_printf(mon, " \\ %s: type=%s",
ti->id, TpmTypeOptionsKind_lookup[ti->options->type]);
switch (ti->options->type) {
case TPM_TYPE_OPTIONS_KIND_PASSTHROUGH:
tpo = ti->options->u.passthrough.data;
monitor_printf(mon, "%s%s%s%s",
tpo->has_path ? ",path=" : "",
tpo->has_path ? tpo->path : "",
tpo->has_cancel_path ? ",cancel-path=" : "",
tpo->has_cancel_path ? tpo->cancel_path : "");
break;
case TPM_TYPE_OPTIONS_KIND__MAX:
break;
}
monitor_printf(mon, "\n");
c++;
}
qapi_free_TPMInfoList(info_list);
}
void hmp_quit(Monitor *mon, const QDict *qdict)
{
monitor_suspend(mon);
qmp_quit(NULL);
}
void hmp_stop(Monitor *mon, const QDict *qdict)
{
qmp_stop(NULL);
}
void hmp_system_reset(Monitor *mon, const QDict *qdict)
{
qmp_system_reset(NULL);
}
void hmp_system_powerdown(Monitor *mon, const QDict *qdict)
{
qmp_system_powerdown(NULL);
}
void hmp_cpu(Monitor *mon, const QDict *qdict)
{
int64_t cpu_index;
/* XXX: drop the monitor_set_cpu() usage when all HMP commands that
use it are converted to the QAPI */
cpu_index = qdict_get_int(qdict, "index");
if (monitor_set_cpu(cpu_index) < 0) {
monitor_printf(mon, "invalid CPU index\n");
}
}
void hmp_memsave(Monitor *mon, const QDict *qdict)
{
uint32_t size = qdict_get_int(qdict, "size");
const char *filename = qdict_get_str(qdict, "filename");
uint64_t addr = qdict_get_int(qdict, "val");
Error *err = NULL;
qmp_memsave(addr, size, filename, true, monitor_get_cpu_index(), &err);
hmp_handle_error(mon, &err);
}
void hmp_pmemsave(Monitor *mon, const QDict *qdict)
{
uint32_t size = qdict_get_int(qdict, "size");
const char *filename = qdict_get_str(qdict, "filename");
uint64_t addr = qdict_get_int(qdict, "val");
Error *err = NULL;
qmp_pmemsave(addr, size, filename, &err);
hmp_handle_error(mon, &err);
}
void hmp_ringbuf_write(Monitor *mon, const QDict *qdict)
{
const char *chardev = qdict_get_str(qdict, "device");
const char *data = qdict_get_str(qdict, "data");
Error *err = NULL;
qmp_ringbuf_write(chardev, data, false, 0, &err);
hmp_handle_error(mon, &err);
}
void hmp_ringbuf_read(Monitor *mon, const QDict *qdict)
{
uint32_t size = qdict_get_int(qdict, "size");
const char *chardev = qdict_get_str(qdict, "device");
char *data;
Error *err = NULL;
int i;
data = qmp_ringbuf_read(chardev, size, false, 0, &err);
if (err) {
error_report_err(err);
return;
}
for (i = 0; data[i]; i++) {
unsigned char ch = data[i];
if (ch == '\\') {
monitor_printf(mon, "\\\\");
} else if ((ch < 0x20 && ch != '\n' && ch != '\t') || ch == 0x7F) {
monitor_printf(mon, "\\u%04X", ch);
} else {
monitor_printf(mon, "%c", ch);
}
}
monitor_printf(mon, "\n");
g_free(data);
}
static void hmp_cont_cb(void *opaque, int err)
{
if (!err) {
qmp_cont(NULL);
}
}
static bool key_is_missing(const BlockInfo *bdev)
{
return (bdev->inserted && bdev->inserted->encryption_key_missing);
}
void hmp_cont(Monitor *mon, const QDict *qdict)
{
BlockInfoList *bdev_list, *bdev;
Error *err = NULL;
bdev_list = qmp_query_block(NULL);
for (bdev = bdev_list; bdev; bdev = bdev->next) {
if (key_is_missing(bdev->value)) {
monitor_read_block_device_key(mon, bdev->value->device,
hmp_cont_cb, NULL);
goto out;
}
}
qmp_cont(&err);
hmp_handle_error(mon, &err);
out:
qapi_free_BlockInfoList(bdev_list);
}
void hmp_system_wakeup(Monitor *mon, const QDict *qdict)
{
qmp_system_wakeup(NULL);
}
void hmp_nmi(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
qmp_inject_nmi(&err);
hmp_handle_error(mon, &err);
}
void hmp_set_link(Monitor *mon, const QDict *qdict)
{
const char *name = qdict_get_str(qdict, "name");
bool up = qdict_get_bool(qdict, "up");
Error *err = NULL;
qmp_set_link(name, up, &err);
hmp_handle_error(mon, &err);
}
void hmp_block_passwd(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *password = qdict_get_str(qdict, "password");
Error *err = NULL;
qmp_block_passwd(true, device, false, NULL, password, &err);
hmp_handle_error(mon, &err);
}
void hmp_balloon(Monitor *mon, const QDict *qdict)
{
int64_t value = qdict_get_int(qdict, "value");
Error *err = NULL;
qmp_balloon(value, &err);
if (err) {
error_report_err(err);
}
}
void hmp_block_resize(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
int64_t size = qdict_get_int(qdict, "size");
Error *err = NULL;
qmp_block_resize(true, device, false, NULL, size, &err);
hmp_handle_error(mon, &err);
}
void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *filename = qdict_get_str(qdict, "target");
const char *format = qdict_get_try_str(qdict, "format");
bool reuse = qdict_get_try_bool(qdict, "reuse", false);
bool full = qdict_get_try_bool(qdict, "full", false);
enum NewImageMode mode;
Error *err = NULL;
if (!filename) {
error_setg(&err, QERR_MISSING_PARAMETER, "target");
hmp_handle_error(mon, &err);
return;
}
if (reuse) {
mode = NEW_IMAGE_MODE_EXISTING;
} else {
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
qmp_drive_mirror(device, filename, !!format, format,
false, NULL, false, NULL,
full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
true, mode, false, 0, false, 0, false, 0,
false, 0, false, 0, false, true, &err);
hmp_handle_error(mon, &err);
}
void hmp_drive_backup(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *filename = qdict_get_str(qdict, "target");
const char *format = qdict_get_try_str(qdict, "format");
bool reuse = qdict_get_try_bool(qdict, "reuse", false);
bool full = qdict_get_try_bool(qdict, "full", false);
enum NewImageMode mode;
Error *err = NULL;
if (!filename) {
error_setg(&err, QERR_MISSING_PARAMETER, "target");
hmp_handle_error(mon, &err);
return;
}
if (reuse) {
mode = NEW_IMAGE_MODE_EXISTING;
} else {
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
qmp_drive_backup(device, filename, !!format, format,
full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
true, mode, false, 0, false, NULL,
false, 0, false, 0, &err);
hmp_handle_error(mon, &err);
}
void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *filename = qdict_get_try_str(qdict, "snapshot-file");
const char *format = qdict_get_try_str(qdict, "format");
bool reuse = qdict_get_try_bool(qdict, "reuse", false);
enum NewImageMode mode;
Error *err = NULL;
if (!filename) {
/* In the future, if 'snapshot-file' is not specified, the snapshot
will be taken internally. Today it's actually required. */
error_setg(&err, QERR_MISSING_PARAMETER, "snapshot-file");
hmp_handle_error(mon, &err);
return;
}
mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
qmp_blockdev_snapshot_sync(true, device, false, NULL,
filename, false, NULL,
!!format, format,
true, mode, &err);
hmp_handle_error(mon, &err);
}
void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *name = qdict_get_str(qdict, "name");
Error *err = NULL;
qmp_blockdev_snapshot_internal_sync(device, name, &err);
hmp_handle_error(mon, &err);
}
void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *name = qdict_get_str(qdict, "name");
const char *id = qdict_get_try_str(qdict, "id");
Error *err = NULL;
qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id,
true, name, &err);
hmp_handle_error(mon, &err);
}
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
{
qmp_migrate_cancel(NULL);
}
void hmp_migrate_incoming(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
const char *uri = qdict_get_str(qdict, "uri");
qmp_migrate_incoming(uri, &err);
hmp_handle_error(mon, &err);
}
void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict)
{
double value = qdict_get_double(qdict, "value");
qmp_migrate_set_downtime(value, NULL);
}
void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict)
{
int64_t value = qdict_get_int(qdict, "value");
Error *err = NULL;
qmp_migrate_set_cache_size(value, &err);
if (err) {
error_report_err(err);
return;
}
}
void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
{
int64_t value = qdict_get_int(qdict, "value");
qmp_migrate_set_speed(value, NULL);
}
void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict)
{
const char *cap = qdict_get_str(qdict, "capability");
bool state = qdict_get_bool(qdict, "state");
Error *err = NULL;
MigrationCapabilityStatusList *caps = g_malloc0(sizeof(*caps));
int i;
for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
caps->value = g_malloc0(sizeof(*caps->value));
caps->value->capability = i;
caps->value->state = state;
caps->next = NULL;
qmp_migrate_set_capabilities(caps, &err);
break;
}
}
if (i == MIGRATION_CAPABILITY__MAX) {
error_setg(&err, QERR_INVALID_PARAMETER, cap);
}
qapi_free_MigrationCapabilityStatusList(caps);
if (err) {
error_report_err(err);
}
}
void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
{
const char *param = qdict_get_str(qdict, "parameter");
int value = qdict_get_int(qdict, "value");
Error *err = NULL;
bool has_compress_level = false;
bool has_compress_threads = false;
bool has_decompress_threads = false;
bool has_cpu_throttle_initial = false;
bool has_cpu_throttle_increment = false;
int i;
for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) {
if (strcmp(param, MigrationParameter_lookup[i]) == 0) {
switch (i) {
case MIGRATION_PARAMETER_COMPRESS_LEVEL:
has_compress_level = true;
break;
case MIGRATION_PARAMETER_COMPRESS_THREADS:
has_compress_threads = true;
break;
case MIGRATION_PARAMETER_DECOMPRESS_THREADS:
has_decompress_threads = true;
break;
case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL:
has_cpu_throttle_initial = true;
break;
case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT:
has_cpu_throttle_increment = true;
break;
}
qmp_migrate_set_parameters(has_compress_level, value,
has_compress_threads, value,
has_decompress_threads, value,
has_cpu_throttle_initial, value,
has_cpu_throttle_increment, value,
&err);
break;
}
}
if (i == MIGRATION_PARAMETER__MAX) {
error_setg(&err, QERR_INVALID_PARAMETER, param);
}
if (err) {
error_report_err(err);
}
}
void hmp_client_migrate_info(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
const char *protocol = qdict_get_str(qdict, "protocol");
const char *hostname = qdict_get_str(qdict, "hostname");
bool has_port = qdict_haskey(qdict, "port");
int port = qdict_get_try_int(qdict, "port", -1);
bool has_tls_port = qdict_haskey(qdict, "tls-port");
int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
const char *cert_subject = qdict_get_try_str(qdict, "cert-subject");
qmp_client_migrate_info(protocol, hostname,
has_port, port, has_tls_port, tls_port,
!!cert_subject, cert_subject, &err);
hmp_handle_error(mon, &err);
}
void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
qmp_migrate_start_postcopy(&err);
hmp_handle_error(mon, &err);
}
void hmp_set_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *password = qdict_get_str(qdict, "password");
const char *connected = qdict_get_try_str(qdict, "connected");
Error *err = NULL;
qmp_set_password(protocol, password, !!connected, connected, &err);
hmp_handle_error(mon, &err);
}
void hmp_expire_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *whenstr = qdict_get_str(qdict, "time");
Error *err = NULL;
qmp_expire_password(protocol, whenstr, &err);
hmp_handle_error(mon, &err);
}
void hmp_eject(Monitor *mon, const QDict *qdict)
{
bool force = qdict_get_try_bool(qdict, "force", false);
const char *device = qdict_get_str(qdict, "device");
Error *err = NULL;
qmp_eject(device, true, force, &err);
hmp_handle_error(mon, &err);
}
static void hmp_change_read_arg(void *opaque, const char *password,
void *readline_opaque)
{
qmp_change_vnc_password(password, NULL);
monitor_read_command(opaque, 1);
}
void hmp_change(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *target = qdict_get_str(qdict, "target");
const char *arg = qdict_get_try_str(qdict, "arg");
const char *read_only = qdict_get_try_str(qdict, "read-only-mode");
BlockdevChangeReadOnlyMode read_only_mode = 0;
Error *err = NULL;
if (strcmp(device, "vnc") == 0) {
if (read_only) {
monitor_printf(mon,
"Parameter 'read-only-mode' is invalid for VNC\n");
return;
}
if (strcmp(target, "passwd") == 0 ||
strcmp(target, "password") == 0) {
if (!arg) {
monitor_read_password(mon, hmp_change_read_arg, NULL);
return;
}
}
qmp_change("vnc", target, !!arg, arg, &err);
} else {
if (read_only) {
read_only_mode =
qapi_enum_parse(BlockdevChangeReadOnlyMode_lookup,
read_only, BLOCKDEV_CHANGE_READ_ONLY_MODE__MAX,
BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, &err);
if (err) {
hmp_handle_error(mon, &err);
return;
}
}
qmp_blockdev_change_medium(device, target, !!arg, arg,
!!read_only, read_only_mode, &err);
if (err &&
error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) {
error_free(err);
monitor_read_block_device_key(mon, device, NULL, NULL);
return;
}
}
hmp_handle_error(mon, &err);
}
void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
qmp_block_set_io_throttle(qdict_get_str(qdict, "device"),
qdict_get_int(qdict, "bps"),
qdict_get_int(qdict, "bps_rd"),
qdict_get_int(qdict, "bps_wr"),
qdict_get_int(qdict, "iops"),
qdict_get_int(qdict, "iops_rd"),
qdict_get_int(qdict, "iops_wr"),
false, /* no burst max via HMP */
0,
false,
0,
false,
0,
false,
0,
false,
0,
false,
0,
false, /* no burst length via HMP */
0,
false,
0,
false,
0,
false,
0,
false,
0,
false,
0,
false, /* No default I/O size */
0,
false,
NULL, &err);
hmp_handle_error(mon, &err);
}
void hmp_block_stream(Monitor *mon, const QDict *qdict)
{
Error *error = NULL;
const char *device = qdict_get_str(qdict, "device");
const char *base = qdict_get_try_str(qdict, "base");
int64_t speed = qdict_get_try_int(qdict, "speed", 0);
qmp_block_stream(device, base != NULL, base, false, NULL,
qdict_haskey(qdict, "speed"), speed,
true, BLOCKDEV_ON_ERROR_REPORT, &error);
hmp_handle_error(mon, &error);
}
void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict)
{
Error *error = NULL;
const char *device = qdict_get_str(qdict, "device");
int64_t value = qdict_get_int(qdict, "speed");
qmp_block_job_set_speed(device, value, &error);
hmp_handle_error(mon, &error);
}
void hmp_block_job_cancel(Monitor *mon, const QDict *qdict)
{
Error *error = NULL;
const char *device = qdict_get_str(qdict, "device");
bool force = qdict_get_try_bool(qdict, "force", false);
qmp_block_job_cancel(device, true, force, &error);
hmp_handle_error(mon, &error);
}
void hmp_block_job_pause(Monitor *mon, const QDict *qdict)
{
Error *error = NULL;
const char *device = qdict_get_str(qdict, "device");
qmp_block_job_pause(device, &error);
hmp_handle_error(mon, &error);
}
void hmp_block_job_resume(Monitor *mon, const QDict *qdict)
{
Error *error = NULL;
const char *device = qdict_get_str(qdict, "device");
qmp_block_job_resume(device, &error);
hmp_handle_error(mon, &error);
}
void hmp_block_job_complete(Monitor *mon, const QDict *qdict)
{
Error *error = NULL;
const char *device = qdict_get_str(qdict, "device");
qmp_block_job_complete(device, &error);
hmp_handle_error(mon, &error);
}
typedef struct HMPMigrationStatus
{
QEMUTimer *timer;
Monitor *mon;
bool is_block_migration;
} HMPMigrationStatus;
static void hmp_migrate_status_cb(void *opaque)
{
HMPMigrationStatus *status = opaque;
MigrationInfo *info;
info = qmp_query_migrate(NULL);
if (!info->has_status || info->status == MIGRATION_STATUS_ACTIVE ||
info->status == MIGRATION_STATUS_SETUP) {
if (info->has_disk) {
int progress;
if (info->disk->remaining) {
progress = info->disk->transferred * 100 / info->disk->total;
} else {
progress = 100;
}
monitor_printf(status->mon, "Completed %d %%\r", progress);
monitor_flush(status->mon);
}
timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
} else {
if (status->is_block_migration) {
monitor_printf(status->mon, "\n");
}
if (info->has_error_desc) {
error_report("%s", info->error_desc);
}
monitor_resume(status->mon);
timer_del(status->timer);
g_free(status);
}
qapi_free_MigrationInfo(info);
}
void hmp_migrate(Monitor *mon, const QDict *qdict)
{
bool detach = qdict_get_try_bool(qdict, "detach", false);
bool blk = qdict_get_try_bool(qdict, "blk", false);
bool inc = qdict_get_try_bool(qdict, "inc", false);
const char *uri = qdict_get_str(qdict, "uri");
Error *err = NULL;
qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, &err);
if (err) {
error_report_err(err);
return;
}
if (!detach) {
HMPMigrationStatus *status;
if (monitor_suspend(mon) < 0) {
monitor_printf(mon, "terminal does not allow synchronous "
"migration, continuing detached\n");
return;
}
status = g_malloc0(sizeof(*status));
status->mon = mon;
status->is_block_migration = blk || inc;
status->timer = timer_new_ms(QEMU_CLOCK_REALTIME, hmp_migrate_status_cb,
status);
timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
}
}
void hmp_device_add(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
qmp_device_add((QDict *)qdict, NULL, &err);
hmp_handle_error(mon, &err);
}
void hmp_device_del(Monitor *mon, const QDict *qdict)
{
const char *id = qdict_get_str(qdict, "id");
Error *err = NULL;
qmp_device_del(id, &err);
hmp_handle_error(mon, &err);
}
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
bool paging = qdict_get_try_bool(qdict, "paging", false);
bool zlib = qdict_get_try_bool(qdict, "zlib", false);
bool lzo = qdict_get_try_bool(qdict, "lzo", false);
bool snappy = qdict_get_try_bool(qdict, "snappy", false);
const char *file = qdict_get_str(qdict, "filename");
bool has_begin = qdict_haskey(qdict, "begin");
bool has_length = qdict_haskey(qdict, "length");
bool has_detach = qdict_haskey(qdict, "detach");
int64_t begin = 0;
int64_t length = 0;
bool detach = false;
enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
char *prot;
if (zlib + lzo + snappy > 1) {
error_setg(&err, "only one of '-z|-l|-s' can be set");
hmp_handle_error(mon, &err);
return;
}
if (zlib) {
dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB;
}
if (lzo) {
dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO;
}
if (snappy) {
dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY;
}
if (has_begin) {
begin = qdict_get_int(qdict, "begin");
}
if (has_length) {
length = qdict_get_int(qdict, "length");
}
if (has_detach) {
detach = qdict_get_bool(qdict, "detach");
}
prot = g_strconcat("file:", file, NULL);
qmp_dump_guest_memory(paging, prot, true, detach, has_begin, begin,
has_length, length, true, dump_format, &err);
hmp_handle_error(mon, &err);
g_free(prot);
}
void hmp_netdev_add(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
QemuOpts *opts;
opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict, &err);
if (err) {
goto out;
}
netdev_add(opts, &err);
if (err) {
qemu_opts_del(opts);
}
out:
hmp_handle_error(mon, &err);
}
void hmp_netdev_del(Monitor *mon, const QDict *qdict)
{
const char *id = qdict_get_str(qdict, "id");
Error *err = NULL;
qmp_netdev_del(id, &err);
hmp_handle_error(mon, &err);
}
void hmp_object_add(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
QemuOpts *opts;
OptsVisitor *ov;
Object *obj = NULL;
opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
if (err) {
hmp_handle_error(mon, &err);
return;
}
ov = opts_visitor_new(opts);
obj = user_creatable_add(qdict, opts_get_visitor(ov), &err);
opts_visitor_cleanup(ov);
qemu_opts_del(opts);
if (err) {
hmp_handle_error(mon, &err);
}
if (obj) {
object_unref(obj);
}
}
void hmp_getfd(Monitor *mon, const QDict *qdict)
{
const char *fdname = qdict_get_str(qdict, "fdname");
Error *err = NULL;
qmp_getfd(fdname, &err);
hmp_handle_error(mon, &err);
}
void hmp_closefd(Monitor *mon, const QDict *qdict)
{
const char *fdname = qdict_get_str(qdict, "fdname");
Error *err = NULL;
qmp_closefd(fdname, &err);
hmp_handle_error(mon, &err);
}
void hmp_sendkey(Monitor *mon, const QDict *qdict)
{
const char *keys = qdict_get_str(qdict, "keys");
KeyValueList *keylist, *head = NULL, *tmp = NULL;
int has_hold_time = qdict_haskey(qdict, "hold-time");
int hold_time = qdict_get_try_int(qdict, "hold-time", -1);
Error *err = NULL;
char *separator;
int keyname_len;
while (1) {
separator = strchr(keys, '-');
keyname_len = separator ? separator - keys : strlen(keys);
/* Be compatible with old interface, convert user inputted "<" */
if (keys[0] == '<' && keyname_len == 1) {
keys = "less";
keyname_len = 4;
}
keylist = g_malloc0(sizeof(*keylist));
keylist->value = g_malloc0(sizeof(*keylist->value));
if (!head) {
head = keylist;
}
if (tmp) {
tmp->next = keylist;
}
tmp = keylist;
if (strstart(keys, "0x", NULL)) {
char *endp;
int value = strtoul(keys, &endp, 0);
assert(endp <= keys + keyname_len);
if (endp != keys + keyname_len) {
goto err_out;
}
keylist->value->type = KEY_VALUE_KIND_NUMBER;
keylist->value->u.number.data = value;
} else {
int idx = index_from_key(keys, keyname_len);
if (idx == Q_KEY_CODE__MAX) {
goto err_out;
}
keylist->value->type = KEY_VALUE_KIND_QCODE;
keylist->value->u.qcode.data = idx;
}
if (!separator) {
break;
}
keys = separator + 1;
}
qmp_send_key(head, has_hold_time, hold_time, &err);
hmp_handle_error(mon, &err);
out:
qapi_free_KeyValueList(head);
return;
err_out:
monitor_printf(mon, "invalid parameter: %.*s\n", keyname_len, keys);
goto out;
}
void hmp_screendump(Monitor *mon, const QDict *qdict)
{
const char *filename = qdict_get_str(qdict, "filename");
Error *err = NULL;
qmp_screendump(filename, &err);
hmp_handle_error(mon, &err);
}
void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
{
const char *uri = qdict_get_str(qdict, "uri");
bool writable = qdict_get_try_bool(qdict, "writable", false);
bool all = qdict_get_try_bool(qdict, "all", false);
Error *local_err = NULL;
BlockInfoList *block_list, *info;
SocketAddress *addr;
if (writable && !all) {
error_setg(&local_err, "-w only valid together with -a");
goto exit;
}
/* First check if the address is valid and start the server. */
addr = socket_parse(uri, &local_err);
if (local_err != NULL) {
goto exit;
}
qmp_nbd_server_start(addr, false, NULL, &local_err);
qapi_free_SocketAddress(addr);
if (local_err != NULL) {
goto exit;
}
if (!all) {
return;
}
/* Then try adding all block devices. If one fails, close all and
* exit.
*/
block_list = qmp_query_block(NULL);
for (info = block_list; info; info = info->next) {
if (!info->value->has_inserted) {
continue;
}
qmp_nbd_server_add(info->value->device, true, writable, &local_err);
if (local_err != NULL) {
qmp_nbd_server_stop(NULL);
break;
}
}
qapi_free_BlockInfoList(block_list);
exit:
hmp_handle_error(mon, &local_err);
}
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
bool writable = qdict_get_try_bool(qdict, "writable", false);
Error *local_err = NULL;
qmp_nbd_server_add(device, true, writable, &local_err);
if (local_err != NULL) {
hmp_handle_error(mon, &local_err);
}
}
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
qmp_nbd_server_stop(&err);
hmp_handle_error(mon, &err);
}
void hmp_cpu_add(Monitor *mon, const QDict *qdict)
{
int cpuid;
Error *err = NULL;
cpuid = qdict_get_int(qdict, "id");
qmp_cpu_add(cpuid, &err);
hmp_handle_error(mon, &err);
}
void hmp_chardev_add(Monitor *mon, const QDict *qdict)
{
const char *args = qdict_get_str(qdict, "args");
Error *err = NULL;
QemuOpts *opts;
opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, true);
if (opts == NULL) {
error_setg(&err, "Parsing chardev args failed");
} else {
qemu_chr_new_from_opts(opts, NULL, &err);
}
hmp_handle_error(mon, &err);
}
void hmp_chardev_remove(Monitor *mon, const QDict *qdict)
{
Error *local_err = NULL;
qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err);
hmp_handle_error(mon, &local_err);
}
void hmp_qemu_io(Monitor *mon, const QDict *qdict)
{
BlockBackend *blk;
const char* device = qdict_get_str(qdict, "device");
const char* command = qdict_get_str(qdict, "command");
Error *err = NULL;
blk = blk_by_name(device);
if (blk) {
qemuio_command(blk, command);
} else {
error_set(&err, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", device);
}
hmp_handle_error(mon, &err);
}
void hmp_object_del(Monitor *mon, const QDict *qdict)
{
const char *id = qdict_get_str(qdict, "id");
Error *err = NULL;
user_creatable_del(id, &err);
hmp_handle_error(mon, &err);
}
void hmp_info_memdev(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
MemdevList *memdev_list = qmp_query_memdev(&err);
MemdevList *m = memdev_list;
StringOutputVisitor *ov;
char *str;
int i = 0;
while (m) {
ov = string_output_visitor_new(false);
visit_type_uint16List(string_output_get_visitor(ov), NULL,
&m->value->host_nodes, NULL);
monitor_printf(mon, "memory backend: %d\n", i);
monitor_printf(mon, " size: %" PRId64 "\n", m->value->size);
monitor_printf(mon, " merge: %s\n",
m->value->merge ? "true" : "false");
monitor_printf(mon, " dump: %s\n",
m->value->dump ? "true" : "false");
monitor_printf(mon, " prealloc: %s\n",
m->value->prealloc ? "true" : "false");
monitor_printf(mon, " policy: %s\n",
HostMemPolicy_lookup[m->value->policy]);
str = string_output_get_string(ov);
monitor_printf(mon, " host nodes: %s\n", str);
g_free(str);
string_output_visitor_cleanup(ov);
m = m->next;
i++;
}
monitor_printf(mon, "\n");
qapi_free_MemdevList(memdev_list);
}
void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
MemoryDeviceInfoList *info_list = qmp_query_memory_devices(&err);
MemoryDeviceInfoList *info;
MemoryDeviceInfo *value;
PCDIMMDeviceInfo *di;
for (info = info_list; info; info = info->next) {
value = info->value;
if (value) {
switch (value->type) {
case MEMORY_DEVICE_INFO_KIND_DIMM:
di = value->u.dimm.data;
monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
MemoryDeviceInfoKind_lookup[value->type],
di->id ? di->id : "");
monitor_printf(mon, " addr: 0x%" PRIx64 "\n", di->addr);
monitor_printf(mon, " slot: %" PRId64 "\n", di->slot);
monitor_printf(mon, " node: %" PRId64 "\n", di->node);
monitor_printf(mon, " size: %" PRIu64 "\n", di->size);
monitor_printf(mon, " memdev: %s\n", di->memdev);
monitor_printf(mon, " hotplugged: %s\n",
di->hotplugged ? "true" : "false");
monitor_printf(mon, " hotpluggable: %s\n",
di->hotpluggable ? "true" : "false");
break;
default:
break;
}
}
}
qapi_free_MemoryDeviceInfoList(info_list);
}
void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
{
IOThreadInfoList *info_list = qmp_query_iothreads(NULL);
IOThreadInfoList *info;
for (info = info_list; info; info = info->next) {
monitor_printf(mon, "%s: thread_id=%" PRId64 "\n",
info->value->id, info->value->thread_id);
}
qapi_free_IOThreadInfoList(info_list);
}
void hmp_qom_list(Monitor *mon, const QDict *qdict)
{
const char *path = qdict_get_try_str(qdict, "path");
ObjectPropertyInfoList *list;
Error *err = NULL;
if (path == NULL) {
monitor_printf(mon, "/\n");
return;
}
list = qmp_qom_list(path, &err);
if (err == NULL) {
ObjectPropertyInfoList *start = list;
while (list != NULL) {
ObjectPropertyInfo *value = list->value;
monitor_printf(mon, "%s (%s)\n",
value->name, value->type);
list = list->next;
}
qapi_free_ObjectPropertyInfoList(start);
}
hmp_handle_error(mon, &err);
}
void hmp_qom_set(Monitor *mon, const QDict *qdict)
{
const char *path = qdict_get_str(qdict, "path");
const char *property = qdict_get_str(qdict, "property");
const char *value = qdict_get_str(qdict, "value");
Error *err = NULL;
bool ambiguous = false;
Object *obj;
obj = object_resolve_path(path, &ambiguous);
if (obj == NULL) {
error_set(&err, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", path);
} else {
if (ambiguous) {
monitor_printf(mon, "Warning: Path '%s' is ambiguous\n", path);
}
object_property_parse(obj, value, property, &err);
}
hmp_handle_error(mon, &err);
}
void hmp_rocker(Monitor *mon, const QDict *qdict)
{
const char *name = qdict_get_str(qdict, "name");
RockerSwitch *rocker;
Error *err = NULL;
rocker = qmp_query_rocker(name, &err);
if (err != NULL) {
hmp_handle_error(mon, &err);
return;
}
monitor_printf(mon, "name: %s\n", rocker->name);
monitor_printf(mon, "id: 0x%" PRIx64 "\n", rocker->id);
monitor_printf(mon, "ports: %d\n", rocker->ports);
qapi_free_RockerSwitch(rocker);
}
void hmp_rocker_ports(Monitor *mon, const QDict *qdict)
{
RockerPortList *list, *port;
const char *name = qdict_get_str(qdict, "name");
Error *err = NULL;
list = qmp_query_rocker_ports(name, &err);
if (err != NULL) {
hmp_handle_error(mon, &err);
return;
}
monitor_printf(mon, " ena/ speed/ auto\n");
monitor_printf(mon, " port link duplex neg?\n");
for (port = list; port; port = port->next) {
monitor_printf(mon, "%10s %-4s %-3s %2s %-3s\n",
port->value->name,
port->value->enabled ? port->value->link_up ?
"up" : "down" : "!ena",
port->value->speed == 10000 ? "10G" : "??",
port->value->duplex ? "FD" : "HD",
port->value->autoneg ? "Yes" : "No");
}
qapi_free_RockerPortList(list);
}
void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict)
{
RockerOfDpaFlowList *list, *info;
const char *name = qdict_get_str(qdict, "name");
uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1);
Error *err = NULL;
list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err);
if (err != NULL) {
hmp_handle_error(mon, &err);
return;
}
monitor_printf(mon, "prio tbl hits key(mask) --> actions\n");
for (info = list; info; info = info->next) {
RockerOfDpaFlow *flow = info->value;
RockerOfDpaFlowKey *key = flow->key;
RockerOfDpaFlowMask *mask = flow->mask;
RockerOfDpaFlowAction *action = flow->action;
if (flow->hits) {
monitor_printf(mon, "%-4d %-3d %-4" PRIu64,
key->priority, key->tbl_id, flow->hits);
} else {
monitor_printf(mon, "%-4d %-3d ",
key->priority, key->tbl_id);
}
if (key->has_in_pport) {
monitor_printf(mon, " pport %d", key->in_pport);
if (mask->has_in_pport) {
monitor_printf(mon, "(0x%x)", mask->in_pport);
}
}
if (key->has_vlan_id) {
monitor_printf(mon, " vlan %d",
key->vlan_id & VLAN_VID_MASK);
if (mask->has_vlan_id) {
monitor_printf(mon, "(0x%x)", mask->vlan_id);
}
}
if (key->has_tunnel_id) {
monitor_printf(mon, " tunnel %d", key->tunnel_id);
if (mask->has_tunnel_id) {
monitor_printf(mon, "(0x%x)", mask->tunnel_id);
}
}
if (key->has_eth_type) {
switch (key->eth_type) {
case 0x0806:
monitor_printf(mon, " ARP");
break;
case 0x0800:
monitor_printf(mon, " IP");
break;
case 0x86dd:
monitor_printf(mon, " IPv6");
break;
case 0x8809:
monitor_printf(mon, " LACP");
break;
case 0x88cc:
monitor_printf(mon, " LLDP");
break;
default:
monitor_printf(mon, " eth type 0x%04x", key->eth_type);
break;
}
}
if (key->has_eth_src) {
if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) &&
(mask->has_eth_src) &&
(strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) {
monitor_printf(mon, " src <any mcast/bcast>");
} else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) &&
(mask->has_eth_src) &&
(strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) {
monitor_printf(mon, " src <any ucast>");
} else {
monitor_printf(mon, " src %s", key->eth_src);
if (mask->has_eth_src) {
monitor_printf(mon, "(%s)", mask->eth_src);
}
}
}
if (key->has_eth_dst) {
if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) &&
(mask->has_eth_dst) &&
(strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) {
monitor_printf(mon, " dst <any mcast/bcast>");
} else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) &&
(mask->has_eth_dst) &&
(strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) {
monitor_printf(mon, " dst <any ucast>");
} else {
monitor_printf(mon, " dst %s", key->eth_dst);
if (mask->has_eth_dst) {
monitor_printf(mon, "(%s)", mask->eth_dst);
}
}
}
if (key->has_ip_proto) {
monitor_printf(mon, " proto %d", key->ip_proto);
if (mask->has_ip_proto) {
monitor_printf(mon, "(0x%x)", mask->ip_proto);
}
}
if (key->has_ip_tos) {
monitor_printf(mon, " TOS %d", key->ip_tos);
if (mask->has_ip_tos) {
monitor_printf(mon, "(0x%x)", mask->ip_tos);
}
}
if (key->has_ip_dst) {
monitor_printf(mon, " dst %s", key->ip_dst);
}
if (action->has_goto_tbl || action->has_group_id ||
action->has_new_vlan_id) {
monitor_printf(mon, " -->");
}
if (action->has_new_vlan_id) {
monitor_printf(mon, " apply new vlan %d",
ntohs(action->new_vlan_id));
}
if (action->has_group_id) {
monitor_printf(mon, " write group 0x%08x", action->group_id);
}
if (action->has_goto_tbl) {
monitor_printf(mon, " goto tbl %d", action->goto_tbl);
}
monitor_printf(mon, "\n");
}
qapi_free_RockerOfDpaFlowList(list);
}
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict)
{
RockerOfDpaGroupList *list, *g;
const char *name = qdict_get_str(qdict, "name");
uint8_t type = qdict_get_try_int(qdict, "type", 9);
Error *err = NULL;
bool set = false;
list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err);
if (err != NULL) {
hmp_handle_error(mon, &err);
return;
}
monitor_printf(mon, "id (decode) --> buckets\n");
for (g = list; g; g = g->next) {
RockerOfDpaGroup *group = g->value;
monitor_printf(mon, "0x%08x", group->id);
monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" :
group->type == 1 ? "L2 rewrite" :
group->type == 2 ? "L3 unicast" :
group->type == 3 ? "L2 multicast" :
group->type == 4 ? "L2 flood" :
group->type == 5 ? "L3 interface" :
group->type == 6 ? "L3 multicast" :
group->type == 7 ? "L3 ECMP" :
group->type == 8 ? "L2 overlay" :
"unknown");
if (group->has_vlan_id) {
monitor_printf(mon, " vlan %d", group->vlan_id);
}
if (group->has_pport) {
monitor_printf(mon, " pport %d", group->pport);
}
if (group->has_index) {
monitor_printf(mon, " index %d", group->index);
}
monitor_printf(mon, ") -->");
if (group->has_set_vlan_id && group->set_vlan_id) {
set = true;
monitor_printf(mon, " set vlan %d",
group->set_vlan_id & VLAN_VID_MASK);
}
if (group->has_set_eth_src) {
if (!set) {
set = true;
monitor_printf(mon, " set");
}
monitor_printf(mon, " src %s", group->set_eth_src);
}
if (group->has_set_eth_dst) {
if (!set) {
set = true;
monitor_printf(mon, " set");
}
monitor_printf(mon, " dst %s", group->set_eth_dst);
}
set = false;
if (group->has_ttl_check && group->ttl_check) {
monitor_printf(mon, " check TTL");
}
if (group->has_group_id && group->group_id) {
monitor_printf(mon, " group id 0x%08x", group->group_id);
}
if (group->has_pop_vlan && group->pop_vlan) {
monitor_printf(mon, " pop vlan");
}
if (group->has_out_pport) {
monitor_printf(mon, " out pport %d", group->out_pport);
}
if (group->has_group_ids) {
struct uint32List *id;
monitor_printf(mon, " groups [");
for (id = group->group_ids; id; id = id->next) {
monitor_printf(mon, "0x%08x", id->value);
if (id->next) {
monitor_printf(mon, ",");
}
}
monitor_printf(mon, "]");
}
monitor_printf(mon, "\n");
}
qapi_free_RockerOfDpaGroupList(list);
}
void hmp_info_dump(Monitor *mon, const QDict *qdict)
{
DumpQueryResult *result = qmp_query_dump(NULL);
assert(result && result->status < DUMP_STATUS__MAX);
monitor_printf(mon, "Status: %s\n", DumpStatus_lookup[result->status]);
if (result->status == DUMP_STATUS_ACTIVE) {
float percent = 0;
assert(result->total != 0);
percent = 100.0 * result->completed / result->total;
monitor_printf(mon, "Finished: %.2f %%\n", percent);
}
qapi_free_DumpQueryResult(result);
}