mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-27 13:30:52 +00:00
0a73336d96
The vhost-user & colo code is poking at the QemuOpts instance in the CharDriverState struct, not realizing that it is valid for this to be NULL. e.g. the following crash shows a codepath where it will be NULL: Program terminated with signal SIGSEGV, Segmentation fault. #0 0x000055baf6ab4adc in qemu_opt_foreach (opts=0x0, func=0x55baf696b650 <net_vhost_chardev_opts>, opaque=0x7ffc51368c00, errp=0x7ffc51368e48) at util/qemu-option.c:617 617 QTAILQ_FOREACH(opt, &opts->head, next) { [Current thread is 1 (Thread 0x7f1d4970bb40 (LWP 6603))] (gdb) bt #0 0x000055baf6ab4adc in qemu_opt_foreach (opts=0x0, func=0x55baf696b650 <net_vhost_chardev_opts>, opaque=0x7ffc51368c00, errp=0x7ffc51368e48) at util/qemu-option.c:617 #1 0x000055baf696b7da in net_vhost_parse_chardev (opts=0x55baf8ff9260, errp=0x7ffc51368e48) at net/vhost-user.c:314 #2 0x000055baf696b985 in net_init_vhost_user (netdev=0x55baf8ff9250, name=0x55baf879d270 "hostnet2", peer=0x0, errp=0x7ffc51368e48) at net/vhost-user.c:360 #3 0x000055baf6960216 in net_client_init1 (object=0x55baf8ff9250, is_netdev=true, errp=0x7ffc51368e48) at net/net.c:1051 #4 0x000055baf6960518 in net_client_init (opts=0x55baf776e7e0, is_netdev=true, errp=0x7ffc51368f00) at net/net.c:1108 #5 0x000055baf696083f in netdev_add (opts=0x55baf776e7e0, errp=0x7ffc51368f00) at net/net.c:1186 #6 0x000055baf69608c7 in qmp_netdev_add (qdict=0x55baf7afaf60, ret=0x7ffc51368f50, errp=0x7ffc51368f48) at net/net.c:1205 #7 0x000055baf6622135 in handle_qmp_command (parser=0x55baf77fb590, tokens=0x7f1d24011960) at /path/to/qemu.git/monitor.c:3978 #8 0x000055baf6a9d099 in json_message_process_token (lexer=0x55baf77fb598, input=0x55baf75acd20, type=JSON_RCURLY, x=113, y=19) at qobject/json-streamer.c:105 #9 0x000055baf6abf7aa in json_lexer_feed_char (lexer=0x55baf77fb598, ch=125 '}', flush=false) at qobject/json-lexer.c:319 #10 0x000055baf6abf8f2 in json_lexer_feed (lexer=0x55baf77fb598, buffer=0x7ffc51369170 "}R\204\367\272U", size=1) at qobject/json-lexer.c:369 #11 0x000055baf6a9d13c in json_message_parser_feed (parser=0x55baf77fb590, buffer=0x7ffc51369170 "}R\204\367\272U", size=1) at qobject/json-streamer.c:124 #12 0x000055baf66221f7 in monitor_qmp_read (opaque=0x55baf77fb530, buf=0x7ffc51369170 "}R\204\367\272U", size=1) at /path/to/qemu.git/monitor.c:3994 #13 0x000055baf6757014 in qemu_chr_be_write_impl (s=0x55baf7610a40, buf=0x7ffc51369170 "}R\204\367\272U", len=1) at qemu-char.c:387 #14 0x000055baf6757076 in qemu_chr_be_write (s=0x55baf7610a40, buf=0x7ffc51369170 "}R\204\367\272U", len=1) at qemu-char.c:399 #15 0x000055baf675b3b0 in tcp_chr_read (chan=0x55baf90244b0, cond=G_IO_IN, opaque=0x55baf7610a40) at qemu-char.c:2927 #16 0x000055baf6a5d655 in qio_channel_fd_source_dispatch (source=0x55baf7610df0, callback=0x55baf675b25a <tcp_chr_read>, user_data=0x55baf7610a40) at io/channel-watch.c:84 #17 0x00007f1d3e80cbbd in g_main_context_dispatch () from /usr/lib64/libglib-2.0.so.0 #18 0x000055baf69d3720 in glib_pollfds_poll () at main-loop.c:213 #19 0x000055baf69d37fd in os_host_main_loop_wait (timeout=126000000) at main-loop.c:258 #20 0x000055baf69d38ad in main_loop_wait (nonblocking=0) at main-loop.c:506 #21 0x000055baf676587b in main_loop () at vl.c:1908 #22 0x000055baf676d3bf in main (argc=101, argv=0x7ffc5136a6c8, envp=0x7ffc5136a9f8) at vl.c:4604 (gdb) p opts $1 = (QemuOpts *) 0x0 The crash occurred when attaching vhost-user net via QMP: { "execute": "chardev-add", "arguments": { "id": "charnet2", "backend": { "type": "socket", "data": { "addr": { "type": "unix", "data": { "path": "/var/run/openvswitch/vhost-user1" } }, "wait": false, "server": false } } }, "id": "libvirt-19" } { "return": { }, "id": "libvirt-19" } { "execute": "netdev_add", "arguments": { "type": "vhost-user", "chardev": "charnet2", "id": "hostnet2" }, "id": "libvirt-20" } Code using chardevs should not be poking at the internals of the CharDriverState struct. What vhost-user wants is a chardev that is operating as reconnectable network service, along with the ability to do FD passing over the connection. The colo code simply wants a network service. Add a feature concept to the char drivers so that chardev users can query the actual features they wish to have supported. The QemuOpts member is removed to prevent future mistakes in this area. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2462 lines
74 KiB
C
2462 lines
74 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->ram->postcopy_requests) {
|
|
monitor_printf(mon, "postcopy request count: %" PRIu64 "\n",
|
|
info->ram->postcopy_requests);
|
|
}
|
|
}
|
|
|
|
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, " %s: '%s'",
|
|
MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_CREDS],
|
|
params->tls_creds ? : "");
|
|
monitor_printf(mon, " %s: '%s'",
|
|
MigrationParameter_lookup[MIGRATION_PARAMETER_TLS_HOSTNAME],
|
|
params->tls_hostname ? : "");
|
|
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 *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);
|
|
Error *err = NULL;
|
|
DriveMirror mirror = {
|
|
.device = (char *)qdict_get_str(qdict, "device"),
|
|
.target = (char *)filename,
|
|
.has_format = !!format,
|
|
.format = (char *)format,
|
|
.sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
|
|
.has_mode = true,
|
|
.mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS,
|
|
.unmap = true,
|
|
};
|
|
|
|
if (!filename) {
|
|
error_setg(&err, QERR_MISSING_PARAMETER, "target");
|
|
hmp_handle_error(mon, &err);
|
|
return;
|
|
}
|
|
qmp_drive_mirror(&mirror, &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);
|
|
bool compress = qdict_get_try_bool(qdict, "compress", false);
|
|
Error *err = NULL;
|
|
DriveBackup backup = {
|
|
.device = (char *)device,
|
|
.target = (char *)filename,
|
|
.has_format = !!format,
|
|
.format = (char *)format,
|
|
.sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
|
|
.has_mode = true,
|
|
.mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS,
|
|
.has_compress = !!compress,
|
|
.compress = compress,
|
|
};
|
|
|
|
if (!filename) {
|
|
error_setg(&err, QERR_MISSING_PARAMETER, "target");
|
|
hmp_handle_error(mon, &err);
|
|
return;
|
|
}
|
|
|
|
qmp_drive_backup(&backup, &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");
|
|
const char *valuestr = qdict_get_str(qdict, "value");
|
|
long valueint = 0;
|
|
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;
|
|
bool has_tls_creds = false;
|
|
bool has_tls_hostname = false;
|
|
bool use_int_value = 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;
|
|
use_int_value = true;
|
|
break;
|
|
case MIGRATION_PARAMETER_COMPRESS_THREADS:
|
|
has_compress_threads = true;
|
|
use_int_value = true;
|
|
break;
|
|
case MIGRATION_PARAMETER_DECOMPRESS_THREADS:
|
|
has_decompress_threads = true;
|
|
use_int_value = true;
|
|
break;
|
|
case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL:
|
|
has_cpu_throttle_initial = true;
|
|
use_int_value = true;
|
|
break;
|
|
case MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT:
|
|
has_cpu_throttle_increment = true;
|
|
break;
|
|
case MIGRATION_PARAMETER_TLS_CREDS:
|
|
has_tls_creds = true;
|
|
break;
|
|
case MIGRATION_PARAMETER_TLS_HOSTNAME:
|
|
has_tls_hostname = true;
|
|
break;
|
|
}
|
|
|
|
if (use_int_value) {
|
|
if (qemu_strtol(valuestr, NULL, 10, &valueint) < 0) {
|
|
error_setg(&err, "Unable to parse '%s' as an int",
|
|
valuestr);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
qmp_migrate_set_parameters(has_compress_level, valueint,
|
|
has_compress_threads, valueint,
|
|
has_decompress_threads, valueint,
|
|
has_cpu_throttle_initial, valueint,
|
|
has_cpu_throttle_increment, valueint,
|
|
has_tls_creds, valuestr,
|
|
has_tls_hostname, valuestr,
|
|
&err);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == MIGRATION_PARAMETER__MAX) {
|
|
error_setg(&err, QERR_INVALID_PARAMETER, param);
|
|
}
|
|
|
|
cleanup:
|
|
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(true, device, false, NULL, 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(true, device, false, NULL, 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;
|
|
BlockIOThrottle throttle = {
|
|
.device = (char *) qdict_get_str(qdict, "device"),
|
|
.bps = qdict_get_int(qdict, "bps"),
|
|
.bps_rd = qdict_get_int(qdict, "bps_rd"),
|
|
.bps_wr = qdict_get_int(qdict, "bps_wr"),
|
|
.iops = qdict_get_int(qdict, "iops"),
|
|
.iops_rd = qdict_get_int(qdict, "iops_rd"),
|
|
.iops_wr = qdict_get_int(qdict, "iops_wr"),
|
|
};
|
|
|
|
qmp_block_set_io_throttle(&throttle, &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(false, NULL, 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;
|
|
Visitor *v;
|
|
Object *obj = NULL;
|
|
|
|
opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
|
|
if (err) {
|
|
hmp_handle_error(mon, &err);
|
|
return;
|
|
}
|
|
|
|
v = opts_visitor_new(opts);
|
|
obj = user_creatable_add(qdict, v, &err);
|
|
visit_free(v);
|
|
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);
|
|
qemu_opts_del(opts);
|
|
}
|
|
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;
|
|
BlockBackend *local_blk = NULL;
|
|
AioContext *aio_context;
|
|
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) {
|
|
BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err);
|
|
if (bs) {
|
|
blk = local_blk = blk_new();
|
|
blk_insert_bs(blk, bs);
|
|
} else {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
aio_context = blk_get_aio_context(blk);
|
|
aio_context_acquire(aio_context);
|
|
|
|
qemuio_command(blk, command);
|
|
|
|
aio_context_release(aio_context);
|
|
|
|
fail:
|
|
blk_unref(local_blk);
|
|
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;
|
|
Visitor *v;
|
|
char *str;
|
|
int i = 0;
|
|
|
|
|
|
while (m) {
|
|
v = string_output_visitor_new(false, &str);
|
|
visit_type_uint16List(v, 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]);
|
|
visit_complete(v, &str);
|
|
monitor_printf(mon, " host nodes: %s\n", str);
|
|
|
|
g_free(str);
|
|
visit_free(v);
|
|
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);
|
|
}
|
|
|
|
void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict)
|
|
{
|
|
Error *err = NULL;
|
|
HotpluggableCPUList *l = qmp_query_hotpluggable_cpus(&err);
|
|
HotpluggableCPUList *saved = l;
|
|
CpuInstanceProperties *c;
|
|
|
|
if (err != NULL) {
|
|
hmp_handle_error(mon, &err);
|
|
return;
|
|
}
|
|
|
|
monitor_printf(mon, "Hotpluggable CPUs:\n");
|
|
while (l) {
|
|
monitor_printf(mon, " type: \"%s\"\n", l->value->type);
|
|
monitor_printf(mon, " vcpus_count: \"%" PRIu64 "\"\n",
|
|
l->value->vcpus_count);
|
|
if (l->value->has_qom_path) {
|
|
monitor_printf(mon, " qom_path: \"%s\"\n", l->value->qom_path);
|
|
}
|
|
|
|
c = l->value->props;
|
|
monitor_printf(mon, " CPUInstance Properties:\n");
|
|
if (c->has_node_id) {
|
|
monitor_printf(mon, " node-id: \"%" PRIu64 "\"\n", c->node_id);
|
|
}
|
|
if (c->has_socket_id) {
|
|
monitor_printf(mon, " socket-id: \"%" PRIu64 "\"\n", c->socket_id);
|
|
}
|
|
if (c->has_core_id) {
|
|
monitor_printf(mon, " core-id: \"%" PRIu64 "\"\n", c->core_id);
|
|
}
|
|
if (c->has_thread_id) {
|
|
monitor_printf(mon, " thread-id: \"%" PRIu64 "\"\n", c->thread_id);
|
|
}
|
|
|
|
l = l->next;
|
|
}
|
|
|
|
qapi_free_HotpluggableCPUList(saved);
|
|
}
|