d9c631ea9f
This moves these commands from MAINTAINERS section "QMP" to "Machine core". Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20230124121946.1139465-10-armbru@redhat.com>
327 lines
8.9 KiB
C
327 lines
8.9 KiB
C
/*
|
|
* QEMU Management Protocol commands
|
|
*
|
|
* 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 "monitor/monitor.h"
|
|
#include "monitor/qmp-helpers.h"
|
|
#include "sysemu/sysemu.h"
|
|
#include "sysemu/kvm.h"
|
|
#include "sysemu/runstate.h"
|
|
#include "sysemu/runstate-action.h"
|
|
#include "sysemu/block-backend.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/qapi-commands-acpi.h"
|
|
#include "qapi/qapi-commands-control.h"
|
|
#include "qapi/qapi-commands-misc.h"
|
|
#include "qapi/qapi-commands-stats.h"
|
|
#include "qapi/type-helpers.h"
|
|
#include "hw/mem/memory-device.h"
|
|
#include "hw/acpi/acpi_dev_interface.h"
|
|
#include "hw/intc/intc.h"
|
|
#include "hw/rdma/rdma.h"
|
|
#include "monitor/stats.h"
|
|
|
|
NameInfo *qmp_query_name(Error **errp)
|
|
{
|
|
NameInfo *info = g_malloc0(sizeof(*info));
|
|
|
|
info->name = g_strdup(qemu_name);
|
|
return info;
|
|
}
|
|
|
|
void qmp_quit(Error **errp)
|
|
{
|
|
shutdown_action = SHUTDOWN_ACTION_POWEROFF;
|
|
qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_QMP_QUIT);
|
|
}
|
|
|
|
void qmp_stop(Error **errp)
|
|
{
|
|
/* if there is a dump in background, we should wait until the dump
|
|
* finished */
|
|
if (qemu_system_dump_in_progress()) {
|
|
error_setg(errp, "There is a dump in process, please wait.");
|
|
return;
|
|
}
|
|
|
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
autostart = 0;
|
|
} else {
|
|
vm_stop(RUN_STATE_PAUSED);
|
|
}
|
|
}
|
|
|
|
void qmp_cont(Error **errp)
|
|
{
|
|
BlockBackend *blk;
|
|
BlockJob *job;
|
|
Error *local_err = NULL;
|
|
|
|
/* if there is a dump in background, we should wait until the dump
|
|
* finished */
|
|
if (qemu_system_dump_in_progress()) {
|
|
error_setg(errp, "There is a dump in process, please wait.");
|
|
return;
|
|
}
|
|
|
|
if (runstate_needs_reset()) {
|
|
error_setg(errp, "Resetting the Virtual Machine is required");
|
|
return;
|
|
} else if (runstate_check(RUN_STATE_SUSPENDED)) {
|
|
return;
|
|
} else if (runstate_check(RUN_STATE_FINISH_MIGRATE)) {
|
|
error_setg(errp, "Migration is not finalized yet");
|
|
return;
|
|
}
|
|
|
|
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
|
|
blk_iostatus_reset(blk);
|
|
}
|
|
|
|
WITH_JOB_LOCK_GUARD() {
|
|
for (job = block_job_next_locked(NULL); job;
|
|
job = block_job_next_locked(job)) {
|
|
block_job_iostatus_reset_locked(job);
|
|
}
|
|
}
|
|
|
|
/* Continuing after completed migration. Images have been inactivated to
|
|
* allow the destination to take control. Need to get control back now.
|
|
*
|
|
* If there are no inactive block nodes (e.g. because the VM was just
|
|
* paused rather than completing a migration), bdrv_inactivate_all() simply
|
|
* doesn't do anything. */
|
|
bdrv_activate_all(&local_err);
|
|
if (local_err) {
|
|
error_propagate(errp, local_err);
|
|
return;
|
|
}
|
|
|
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
autostart = 1;
|
|
} else {
|
|
vm_start();
|
|
}
|
|
}
|
|
|
|
void qmp_add_client(const char *protocol, const char *fdname,
|
|
bool has_skipauth, bool skipauth, bool has_tls, bool tls,
|
|
Error **errp)
|
|
{
|
|
static const struct {
|
|
const char *name;
|
|
bool (*add_client)(int fd, bool has_skipauth, bool skipauth,
|
|
bool has_tls, bool tls, Error **errp);
|
|
} protocol_table[] = {
|
|
{ "spice", qmp_add_client_spice },
|
|
#ifdef CONFIG_VNC
|
|
{ "vnc", qmp_add_client_vnc },
|
|
#endif
|
|
#ifdef CONFIG_DBUS_DISPLAY
|
|
{ "@dbus-display", qmp_add_client_dbus_display },
|
|
#endif
|
|
};
|
|
int fd, i;
|
|
|
|
fd = monitor_get_fd(monitor_cur(), fdname, errp);
|
|
if (fd < 0) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(protocol_table); i++) {
|
|
if (!strcmp(protocol, protocol_table[i].name)) {
|
|
if (!protocol_table[i].add_client(fd, has_skipauth, skipauth,
|
|
has_tls, tls, errp)) {
|
|
close(fd);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!qmp_add_client_char(fd, has_skipauth, skipauth, has_tls, tls,
|
|
protocol, errp)) {
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
ACPIOSTInfoList *qmp_query_acpi_ospm_status(Error **errp)
|
|
{
|
|
bool ambig;
|
|
ACPIOSTInfoList *head = NULL;
|
|
ACPIOSTInfoList **prev = &head;
|
|
Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, &ambig);
|
|
|
|
if (obj) {
|
|
AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj);
|
|
AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj);
|
|
|
|
adevc->ospm_status(adev, &prev);
|
|
} else {
|
|
error_setg(errp, "command is not supported, missing ACPI device");
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
typedef struct StatsCallbacks {
|
|
StatsProvider provider;
|
|
StatRetrieveFunc *stats_cb;
|
|
SchemaRetrieveFunc *schemas_cb;
|
|
QTAILQ_ENTRY(StatsCallbacks) next;
|
|
} StatsCallbacks;
|
|
|
|
static QTAILQ_HEAD(, StatsCallbacks) stats_callbacks =
|
|
QTAILQ_HEAD_INITIALIZER(stats_callbacks);
|
|
|
|
void add_stats_callbacks(StatsProvider provider,
|
|
StatRetrieveFunc *stats_fn,
|
|
SchemaRetrieveFunc *schemas_fn)
|
|
{
|
|
StatsCallbacks *entry = g_new(StatsCallbacks, 1);
|
|
entry->provider = provider;
|
|
entry->stats_cb = stats_fn;
|
|
entry->schemas_cb = schemas_fn;
|
|
|
|
QTAILQ_INSERT_TAIL(&stats_callbacks, entry, next);
|
|
}
|
|
|
|
static bool invoke_stats_cb(StatsCallbacks *entry,
|
|
StatsResultList **stats_results,
|
|
StatsFilter *filter, StatsRequest *request,
|
|
Error **errp)
|
|
{
|
|
ERRP_GUARD();
|
|
strList *targets = NULL;
|
|
strList *names = NULL;
|
|
|
|
if (request) {
|
|
if (request->provider != entry->provider) {
|
|
return true;
|
|
}
|
|
if (request->has_names && !request->names) {
|
|
return true;
|
|
}
|
|
names = request->has_names ? request->names : NULL;
|
|
}
|
|
|
|
switch (filter->target) {
|
|
case STATS_TARGET_VM:
|
|
break;
|
|
case STATS_TARGET_VCPU:
|
|
if (filter->u.vcpu.has_vcpus) {
|
|
if (!filter->u.vcpu.vcpus) {
|
|
/* No targets allowed? Return no statistics. */
|
|
return true;
|
|
}
|
|
targets = filter->u.vcpu.vcpus;
|
|
}
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
entry->stats_cb(stats_results, filter->target, names, targets, errp);
|
|
if (*errp) {
|
|
qapi_free_StatsResultList(*stats_results);
|
|
*stats_results = NULL;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
StatsResultList *qmp_query_stats(StatsFilter *filter, Error **errp)
|
|
{
|
|
StatsResultList *stats_results = NULL;
|
|
StatsCallbacks *entry;
|
|
StatsRequestList *request;
|
|
|
|
QTAILQ_FOREACH(entry, &stats_callbacks, next) {
|
|
if (filter->has_providers) {
|
|
for (request = filter->providers; request; request = request->next) {
|
|
if (!invoke_stats_cb(entry, &stats_results, filter,
|
|
request->value, errp)) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (!invoke_stats_cb(entry, &stats_results, filter, NULL, errp)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return stats_results;
|
|
}
|
|
|
|
StatsSchemaList *qmp_query_stats_schemas(bool has_provider,
|
|
StatsProvider provider,
|
|
Error **errp)
|
|
{
|
|
ERRP_GUARD();
|
|
StatsSchemaList *stats_results = NULL;
|
|
StatsCallbacks *entry;
|
|
|
|
QTAILQ_FOREACH(entry, &stats_callbacks, next) {
|
|
if (!has_provider || provider == entry->provider) {
|
|
entry->schemas_cb(&stats_results, errp);
|
|
if (*errp) {
|
|
qapi_free_StatsSchemaList(stats_results);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return stats_results;
|
|
}
|
|
|
|
void add_stats_entry(StatsResultList **stats_results, StatsProvider provider,
|
|
const char *qom_path, StatsList *stats_list)
|
|
{
|
|
StatsResult *entry = g_new0(StatsResult, 1);
|
|
|
|
entry->provider = provider;
|
|
entry->qom_path = g_strdup(qom_path);
|
|
entry->stats = stats_list;
|
|
|
|
QAPI_LIST_PREPEND(*stats_results, entry);
|
|
}
|
|
|
|
void add_stats_schema(StatsSchemaList **schema_results,
|
|
StatsProvider provider, StatsTarget target,
|
|
StatsSchemaValueList *stats_list)
|
|
{
|
|
StatsSchema *entry = g_new0(StatsSchema, 1);
|
|
|
|
entry->provider = provider;
|
|
entry->target = target;
|
|
entry->stats = stats_list;
|
|
QAPI_LIST_PREPEND(*schema_results, entry);
|
|
}
|
|
|
|
bool apply_str_list_filter(const char *string, strList *list)
|
|
{
|
|
strList *str_list = NULL;
|
|
|
|
if (!list) {
|
|
return true;
|
|
}
|
|
for (str_list = list; str_list; str_list = str_list->next) {
|
|
if (g_str_equal(string, str_list->value)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|