QAPI patches patches for 2021-03-16

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmBUvgwSHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZTo6sP/icaemYrSu5XNjr3caoXTn0rAEYARhcD
 OLENnlDw2ipnXtB59d7J0AulVk7DR0Ejq0MmoNw2yDYdquhopu0XAxF0TnL51GS1
 gd+HygnLVlq/rkDLQn+GIvqzE8+Gx32zHhkQBft7GEHFt4YC5PeOWzG/yYxrt0Za
 VgjfT8WALx+pM8rYxAULbESZuw7eY7g8aNeJmcDmz7zWViVIZ0jEi3Mubdiq1gdc
 GM+qv/0BWPcQSba05RJlqY57s0JaoHcZy6Z5ReE11nyYxXssvNTKAg7Bvv/75H7c
 2Q4Ls2snqsNPpCzRKRtDbhiwLxQxycQ7Jb+BJ2wGMjOjgBl8B+3h70B/ub/OqEPz
 VY+T2ekhTPWRidTDjurAXEcT07M8fMI8feiN6WP+4YMtFZoF/L931JMolJtTZKv5
 D3Dy3QYpK+TjO6tnA93Jz8L5Am0aEngmHCTu6zWQuXauJxaYALf4tWbmKXZaQX2e
 ajqfvAT4xb8WI26I+LNcf37TCSl63xVCec6meS0QNBIM1PTl6ovFQhcSrXrOSrkU
 6HhCsIg7sZ/2MyW4zuINBdyoTAK+pXVSyutQ/OdMYPueDA64NCB5Mrptw/jejwtu
 5JwdevxrHGjnfLVlHFkTtYmH33hCiDtj5RswoONFcXxLanltJtFOtJXYUrkBIAZL
 j4rbal8z0RzR
 =DJOJ
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-03-16-v4' into staging

QAPI patches patches for 2021-03-16

# gpg: Signature made Fri 19 Mar 2021 15:06:52 GMT
# gpg:                using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg:                issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qapi-2021-03-16-v4:
  qapi: New -compat deprecated-input=crash
  qapi: Implement deprecated-input=reject for QMP command arguments
  qapi: Implement deprecated-input=reject for QMP commands
  test-util-sockets: Add stub for monitor_set_cur()
  qapi: Implement deprecated-output=hide for QMP introspection
  monitor: Drop query-qmp-schema 'gen': false hack
  qapi: Implement deprecated-output=hide for QMP event data
  qapi: Implement deprecated-output=hide for QMP events
  qapi: Implement deprecated-output=hide for QMP command results
  qemu-options: New -compat to set policy for deprecated interfaces
  qemuutil: remove qemu_set_fd_handler duplicate symbol

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-03-19 16:40:00 +00:00
commit 2e1293cbaa
31 changed files with 549 additions and 64 deletions

View File

@ -0,0 +1,38 @@
/*
* Policy for handling "funny" management interfaces
*
* Copyright (C) 2020 Red Hat, Inc.
*
* Authors:
* Markus Armbruster <armbru@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* later. See the COPYING file in the top-level directory.
*/
#ifndef QAPI_COMPAT_POLICY_H
#define QAPI_COMPAT_POLICY_H
#include "qapi/qapi-types-compat.h"
extern CompatPolicy compat_policy;
/*
* Create a QObject input visitor for @obj for use with QMP
*
* This is like qobject_input_visitor_new(), except it obeys the
* policy for handling deprecated management interfaces set with
* -compat.
*/
Visitor *qobject_input_visitor_new_qmp(QObject *obj);
/*
* Create a QObject output visitor for @obj for use with QMP
*
* This is like qobject_output_visitor_new(), except it obeys the
* policy for handling deprecated management interfaces set with
* -compat.
*/
Visitor *qobject_output_visitor_new_qmp(QObject **result);
#endif

View File

@ -26,6 +26,7 @@ typedef enum QmpCommandOptions
QCO_ALLOW_OOB = (1U << 1),
QCO_ALLOW_PRECONFIG = (1U << 2),
QCO_COROUTINE = (1U << 3),
QCO_DEPRECATED = (1U << 4),
} QmpCommandOptions;
typedef struct QmpCommand

View File

@ -15,6 +15,7 @@
#ifndef QOBJECT_INPUT_VISITOR_H
#define QOBJECT_INPUT_VISITOR_H
#include "qapi/qapi-types-compat.h"
#include "qapi/visitor.h"
typedef struct QObjectInputVisitor QObjectInputVisitor;
@ -58,6 +59,9 @@ typedef struct QObjectInputVisitor QObjectInputVisitor;
*/
Visitor *qobject_input_visitor_new(QObject *obj);
void qobject_input_visitor_set_policy(Visitor *v,
CompatPolicyInput deprecated);
/*
* Create a QObject input visitor for @obj for use with keyval_parse()
*

View File

@ -15,6 +15,7 @@
#define QOBJECT_OUTPUT_VISITOR_H
#include "qapi/visitor.h"
#include "qapi/qapi-types-compat.h"
typedef struct QObjectOutputVisitor QObjectOutputVisitor;
@ -53,4 +54,7 @@ typedef struct QObjectOutputVisitor QObjectOutputVisitor;
*/
Visitor *qobject_output_visitor_new(QObject **result);
void qobject_output_visitor_set_policy(Visitor *v,
CompatPolicyOutput deprecated);
#endif

View File

@ -113,6 +113,12 @@ struct Visitor
The core takes care of the return type in the public interface. */
void (*optional)(Visitor *v, const char *name, bool *present);
/* Optional */
bool (*deprecated_accept)(Visitor *v, const char *name, Error **errp);
/* Optional */
bool (*deprecated)(Visitor *v, const char *name);
/* Must be set */
VisitorType type;

View File

@ -459,6 +459,24 @@ void visit_end_alternate(Visitor *v, void **obj);
*/
bool visit_optional(Visitor *v, const char *name, bool *present);
/*
* Should we reject deprecated member @name?
*
* @name must not be NULL. This function is only useful between
* visit_start_struct() and visit_end_struct(), since only objects
* have deprecated members.
*/
bool visit_deprecated_accept(Visitor *v, const char *name, Error **errp);
/*
* Should we visit deprecated member @name?
*
* @name must not be NULL. This function is only useful between
* visit_start_struct() and visit_end_struct(), since only objects
* have deprecated members.
*/
bool visit_deprecated(Visitor *v, const char *name);
/*
* Visit an enum value.
*

View File

@ -231,8 +231,6 @@ static void monitor_init_qmp_commands(void)
qmp_init_marshal(&qmp_commands);
qmp_register_command(&qmp_commands, "query-qmp-schema",
qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG);
qmp_register_command(&qmp_commands, "device_add", qmp_device_add,
QCO_NO_OPTIONS);

View File

@ -183,7 +183,4 @@ void help_cmd(Monitor *mon, const char *name);
void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
int hmp_compare_cmd(const char *name, const char *list);
void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
Error **errp);
#endif

View File

@ -26,10 +26,14 @@
#include "monitor-internal.h"
#include "qemu-version.h"
#include "qapi/compat-policy.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-control.h"
#include "qapi/qapi-commands-introspect.h"
#include "qapi/qapi-emit-events.h"
#include "qapi/qapi-introspect.h"
#include "qapi/qapi-visit-introspect.h"
#include "qapi/qobject-input-visitor.h"
/*
* Accept QMP capabilities in @list for @mon.
@ -130,17 +134,89 @@ CommandInfoList *qmp_query_commands(Error **errp)
return list;
}
/*
* Minor hack: generated marshalling suppressed for this command
* ('gen': false in the schema) so we can parse the JSON string
* directly into QObject instead of first parsing it with
* visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it
* to QObject with generated output marshallers, every time. Instead,
* we do it in test-qobject-input-visitor.c, just to make sure
* qapi-gen.py's output actually conforms to the schema.
*/
void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
Error **errp)
static void *split_off_generic_list(void *list,
bool (*splitp)(void *elt),
void **part)
{
*ret_data = qobject_from_qlit(&qmp_schema_qlit);
GenericList *keep = NULL, **keep_tailp = &keep;
GenericList *split = NULL, **split_tailp = &split;
GenericList *tail;
for (tail = list; tail; tail = tail->next) {
if (splitp(tail)) {
*split_tailp = tail;
split_tailp = &tail->next;
} else {
*keep_tailp = tail;
keep_tailp = &tail->next;
}
}
*keep_tailp = *split_tailp = NULL;
*part = split;
return keep;
}
static bool is_in(const char *s, strList *list)
{
strList *tail;
for (tail = list; tail; tail = tail->next) {
if (!strcmp(tail->value, s)) {
return true;
}
}
return false;
}
static bool is_entity_deprecated(void *link)
{
return is_in("deprecated", ((SchemaInfoList *)link)->value->features);
}
static bool is_member_deprecated(void *link)
{
return is_in("deprecated",
((SchemaInfoObjectMemberList *)link)->value->features);
}
static SchemaInfoList *zap_deprecated(SchemaInfoList *schema)
{
void *to_zap;
SchemaInfoList *tail;
SchemaInfo *ent;
schema = split_off_generic_list(schema, is_entity_deprecated, &to_zap);
qapi_free_SchemaInfoList(to_zap);
for (tail = schema; tail; tail = tail->next) {
ent = tail->value;
if (ent->meta_type == SCHEMA_META_TYPE_OBJECT) {
ent->u.object.members
= split_off_generic_list(ent->u.object.members,
is_member_deprecated, &to_zap);
qapi_free_SchemaInfoObjectMemberList(to_zap);
}
}
return schema;
}
SchemaInfoList *qmp_query_qmp_schema(Error **errp)
{
QObject *obj = qobject_from_qlit(&qmp_schema_qlit);
Visitor *v = qobject_input_visitor_new(obj);
SchemaInfoList *schema = NULL;
/* test_visitor_in_qmp_introspect() ensures this can't fail */
visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
g_assert(schema);
qobject_unref(obj);
visit_free(v);
if (compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) {
return zap_deprecated(schema);
}
return schema;
}

52
qapi/compat.json Normal file
View File

@ -0,0 +1,52 @@
# -*- Mode: Python -*-
##
# = Compatibility policy
##
##
# @CompatPolicyInput:
#
# Policy for handling "funny" input.
#
# @accept: Accept silently
# @reject: Reject with an error
# @crash: abort() the process
#
# Since: 6.0
##
{ 'enum': 'CompatPolicyInput',
'data': [ 'accept', 'reject', 'crash' ] }
##
# @CompatPolicyOutput:
#
# Policy for handling "funny" output.
#
# @accept: Pass on unchanged
# @hide: Filter out
#
# Since: 6.0
##
{ 'enum': 'CompatPolicyOutput',
'data': [ 'accept', 'hide' ] }
##
# @CompatPolicy:
#
# Policy for handling deprecated management interfaces.
#
# This is intended for testing users of the management interfaces.
#
# Limitation: covers only syntactic aspects of QMP, i.e. stuff tagged
# with feature 'deprecated'. We may want to extend it to cover
# semantic aspects, CLI, and experimental features.
#
# @deprecated-input: how to handle deprecated input (default 'accept')
# @deprecated-output: how to handle deprecated output (default 'accept')
#
# Since: 6.0
##
{ 'struct': 'CompatPolicy',
'data': { '*deprecated-input': 'CompatPolicyInput',
'*deprecated-output': 'CompatPolicyOutput' } }

View File

@ -49,7 +49,7 @@
##
{ 'command': 'query-qmp-schema',
'returns': [ 'SchemaInfo' ],
'gen': false } # just to simplify qmp_query_json()
'allow-preconfig': true }
##
# @SchemaMetaType:

View File

@ -25,6 +25,7 @@ qapi_all_modules = [
'block-export',
'char',
'common',
'compat',
'control',
'crypto',
'dump',

View File

@ -79,6 +79,7 @@
{ 'include': 'migration.json' }
{ 'include': 'transaction.json' }
{ 'include': 'trace.json' }
{ 'include': 'compat.json' }
{ 'include': 'control.json' }
{ 'include': 'introspect.json' }
{ 'include': 'qom.json' }

View File

@ -135,6 +135,24 @@ bool visit_optional(Visitor *v, const char *name, bool *present)
return *present;
}
bool visit_deprecated_accept(Visitor *v, const char *name, Error **errp)
{
trace_visit_deprecated_accept(v, name);
if (v->deprecated_accept) {
return v->deprecated_accept(v, name, errp);
}
return true;
}
bool visit_deprecated(Visitor *v, const char *name)
{
trace_visit_deprecated(v, name);
if (v->deprecated) {
return v->deprecated(v, name);
}
return true;
}
bool visit_is_input(Visitor *v)
{
return v->type == VISITOR_INPUT;

View File

@ -14,15 +14,36 @@
#include "qemu/osdep.h"
#include "block/aio.h"
#include "qapi/compat-policy.h"
#include "qapi/error.h"
#include "qapi/qmp/dispatch.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qobject-output-visitor.h"
#include "sysemu/runstate.h"
#include "qapi/qmp/qbool.h"
#include "qemu/coroutine.h"
#include "qemu/main-loop.h"
CompatPolicy compat_policy;
Visitor *qobject_input_visitor_new_qmp(QObject *obj)
{
Visitor *v = qobject_input_visitor_new(obj);
qobject_input_visitor_set_policy(v, compat_policy.deprecated_input);
return v;
}
Visitor *qobject_output_visitor_new_qmp(QObject **result)
{
Visitor *v = qobject_output_visitor_new(result);
qobject_output_visitor_set_policy(v, compat_policy.deprecated_output);
return v;
}
static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob,
Error **errp)
{
@ -155,6 +176,20 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
"The command %s has not been found", command);
goto out;
}
if (cmd->options & QCO_DEPRECATED) {
switch (compat_policy.deprecated_input) {
case COMPAT_POLICY_INPUT_ACCEPT:
break;
case COMPAT_POLICY_INPUT_REJECT:
error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
"Deprecated command %s disabled by policy",
command);
goto out;
case COMPAT_POLICY_INPUT_CRASH:
default:
abort();
}
}
if (!cmd->enabled) {
error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
"Command %s has been disabled%s%s",

View File

@ -14,6 +14,7 @@
#include "qemu/osdep.h"
#include <math.h>
#include "qapi/compat-policy.h"
#include "qapi/error.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/visitor-impl.h"
@ -43,6 +44,7 @@ typedef struct StackObject {
struct QObjectInputVisitor {
Visitor visitor;
CompatPolicyInput deprecated_policy;
/* Root of visit at visitor creation. */
QObject *root;
@ -662,6 +664,24 @@ static void qobject_input_optional(Visitor *v, const char *name, bool *present)
*present = true;
}
static bool qobject_input_deprecated_accept(Visitor *v, const char *name,
Error **errp)
{
QObjectInputVisitor *qiv = to_qiv(v);
switch (qiv->deprecated_policy) {
case COMPAT_POLICY_INPUT_ACCEPT:
return true;
case COMPAT_POLICY_INPUT_REJECT:
error_setg(errp, "Deprecated parameter '%s' disabled by policy",
name);
return false;
case COMPAT_POLICY_INPUT_CRASH:
default:
abort();
}
}
static void qobject_input_free(Visitor *v)
{
QObjectInputVisitor *qiv = to_qiv(v);
@ -696,6 +716,7 @@ static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj)
v->visitor.end_list = qobject_input_end_list;
v->visitor.start_alternate = qobject_input_start_alternate;
v->visitor.optional = qobject_input_optional;
v->visitor.deprecated_accept = qobject_input_deprecated_accept;
v->visitor.free = qobject_input_free;
v->root = qobject_ref(obj);
@ -718,6 +739,14 @@ Visitor *qobject_input_visitor_new(QObject *obj)
return &v->visitor;
}
void qobject_input_visitor_set_policy(Visitor *v,
CompatPolicyInput deprecated)
{
QObjectInputVisitor *qiv = to_qiv(v);
qiv->deprecated_policy = deprecated;
}
Visitor *qobject_input_visitor_new_keyval(QObject *obj)
{
QObjectInputVisitor *v = qobject_input_visitor_base_new(obj);

View File

@ -13,6 +13,7 @@
*/
#include "qemu/osdep.h"
#include "qapi/compat-policy.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/visitor-impl.h"
#include "qemu/queue.h"
@ -31,6 +32,8 @@ typedef struct QStackEntry {
struct QObjectOutputVisitor {
Visitor visitor;
CompatPolicyOutput deprecated_policy;
QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */
QObject *root; /* Root of the output visit */
QObject **result; /* User's storage location for result */
@ -207,6 +210,13 @@ static bool qobject_output_type_null(Visitor *v, const char *name,
return true;
}
static bool qobject_output_deprecated(Visitor *v, const char *name)
{
QObjectOutputVisitor *qov = to_qov(v);
return qov->deprecated_policy != COMPAT_POLICY_OUTPUT_HIDE;
}
/* Finish building, and return the root object.
* The root object is never null. The caller becomes the object's
* owner, and should use qobject_unref() when done with it. */
@ -256,6 +266,7 @@ Visitor *qobject_output_visitor_new(QObject **result)
v->visitor.type_number = qobject_output_type_number;
v->visitor.type_any = qobject_output_type_any;
v->visitor.type_null = qobject_output_type_null;
v->visitor.deprecated = qobject_output_deprecated;
v->visitor.complete = qobject_output_complete;
v->visitor.free = qobject_output_free;
@ -264,3 +275,11 @@ Visitor *qobject_output_visitor_new(QObject **result)
return &v->visitor;
}
void qobject_output_visitor_set_policy(Visitor *v,
CompatPolicyOutput deprecated)
{
QObjectOutputVisitor *qov = to_qov(v);
qov->deprecated_policy = deprecated;
}

View File

@ -17,6 +17,8 @@ visit_start_alternate(void *v, const char *name, void *obj, size_t size) "v=%p n
visit_end_alternate(void *v, void *obj) "v=%p obj=%p"
visit_optional(void *v, const char *name, bool *present) "v=%p name=%s present=%p"
visit_deprecated_accept(void *v, const char *name) "v=%p name=%s"
visit_deprecated(void *v, const char *name) "v=%p name=%s"
visit_type_enum(void *v, const char *name, int *obj) "v=%p name=%s obj=%p"
visit_type_int(void *v, const char *name, int64_t *obj) "v=%p name=%s obj=%p"

View File

@ -3507,6 +3507,28 @@ DEFHEADING()
DEFHEADING(Debug/Expert options:)
DEF("compat", HAS_ARG, QEMU_OPTION_compat,
"-compat [deprecated-input=accept|reject|crash][,deprecated-output=accept|hide]\n"
" Policy for handling deprecated management interfaces\n",
QEMU_ARCH_ALL)
SRST
``-compat [deprecated-input=@var{input-policy}][,deprecated-output=@var{output-policy}]``
Set policy for handling deprecated management interfaces (experimental):
``deprecated-input=accept`` (default)
Accept deprecated commands and arguments
``deprecated-input=reject``
Reject deprecated commands and arguments
``deprecated-input=crash``
Crash on deprecated commands and arguments
``deprecated-output=accept`` (default)
Emit deprecated command results and events
``deprecated-output=hide``
Suppress deprecated command results and events
Limitation: covers only syntactic aspects of QMP.
ERST
DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg,
"-fw_cfg [name=]<name>,file=<file>\n"
" add named fw_cfg entry with contents from file\n"

View File

@ -96,7 +96,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in,
{
Visitor *v;
v = qobject_output_visitor_new(ret_out);
v = qobject_output_visitor_new_qmp(ret_out);
if (visit_type_%(c_name)s(v, "unused", &ret_in, errp)) {
visit_complete(v, ret_out);
}
@ -154,7 +154,7 @@ def gen_marshal(name: str,
ret += mcgen('''
v = qobject_input_visitor_new(QOBJECT(args));
v = qobject_input_visitor_new_qmp(QOBJECT(args));
if (!visit_start_struct(v, NULL, NULL, 0, errp)) {
goto out;
}
@ -210,12 +210,16 @@ out:
def gen_register_command(name: str,
features: List[QAPISchemaFeature],
success_response: bool,
allow_oob: bool,
allow_preconfig: bool,
coroutine: bool) -> str:
options = []
if 'deprecated' in [f.name for f in features]:
options += ['QCO_DEPRECATED']
if not success_response:
options += ['QCO_NO_SUCCESS_RESP']
if allow_oob:
@ -251,10 +255,9 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
visit = self._module_basename('qapi-visit', name)
self._genc.add(mcgen('''
#include "qemu/osdep.h"
#include "qapi/compat-policy.h"
#include "qapi/visitor.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/dealloc-visitor.h"
#include "qapi/error.h"
#include "%(visit)s.h"
@ -326,9 +329,9 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
with self._temp_module('./init'):
with ifcontext(ifcond, self._genh, self._genc):
self._genc.add(gen_register_command(name, success_response,
allow_oob, allow_preconfig,
coroutine))
self._genc.add(gen_register_command(
name, features, success_response, allow_oob,
allow_preconfig, coroutine))
def gen_commands(schema: QAPISchema,

View File

@ -79,6 +79,7 @@ def gen_param_var(typ: QAPISchemaObjectType) -> str:
def gen_event_send(name: str,
arg_type: Optional[QAPISchemaObjectType],
features: List[QAPISchemaFeature],
boxed: bool,
event_enum_name: str,
event_emit: str) -> str:
@ -107,6 +108,14 @@ def gen_event_send(name: str,
if not boxed:
ret += gen_param_var(arg_type)
if 'deprecated' in [f.name for f in features]:
ret += mcgen('''
if (compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) {
return;
}
''')
ret += mcgen('''
qmp = qmp_event_build_dict("%(name)s");
@ -117,7 +126,7 @@ def gen_event_send(name: str,
if have_args:
assert arg_type is not None
ret += mcgen('''
v = qobject_output_visitor_new(&obj);
v = qobject_output_visitor_new_qmp(&obj);
''')
if not arg_type.is_implicit():
ret += mcgen('''
@ -136,7 +145,11 @@ def gen_event_send(name: str,
ret += mcgen('''
visit_complete(v, &obj);
qdict_put_obj(qmp, "data", obj);
if (qdict_size(qobject_to(QDict, obj))) {
qdict_put_obj(qmp, "data", obj);
} else {
qobject_unref(obj);
}
''')
ret += mcgen('''
@ -176,9 +189,9 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
#include "%(prefix)sqapi-emit-events.h"
#include "%(events)s.h"
#include "%(visit)s.h"
#include "qapi/compat-policy.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/qmp-event.h"
''',
@ -220,7 +233,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict);
boxed: bool) -> None:
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_event_send_decl(name, arg_type, boxed))
self._genc.add(gen_event_send(name, arg_type, boxed,
self._genc.add(gen_event_send(name, arg_type, features, boxed,
self._event_enum_name,
self._event_emit_name))
# Note: we generate the enum member regardless of @ifcond, to

View File

@ -77,6 +77,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
c_type=base.c_name())
for memb in members:
deprecated = 'deprecated' in [f.name for f in memb.features]
ret += gen_if(memb.ifcond)
if memb.optional:
ret += mcgen('''
@ -84,6 +85,15 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
''',
name=memb.name, c_name=c_name(memb.name))
indent.increase()
if deprecated:
ret += mcgen('''
if (!visit_deprecated_accept(v, "%(name)s", errp)) {
return false;
}
if (visit_deprecated(v, "%(name)s")) {
''',
name=memb.name)
indent.increase()
ret += mcgen('''
if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
return false;
@ -91,6 +101,11 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
''',
c_type=memb.type.c_name(), name=memb.name,
c_name=c_name(memb.name))
if deprecated:
indent.decrease()
ret += mcgen('''
}
''')
if memb.optional:
indent.decrease()
ret += mcgen('''

View File

@ -29,6 +29,7 @@
#include "exec/cpu-common.h"
#include "hw/boards.h"
#include "hw/qdev-properties.h"
#include "qapi/compat-policy.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
@ -114,6 +115,7 @@
#include "sysemu/replay.h"
#include "qapi/qapi-events-run-state.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qapi-visit-compat.h"
#include "qapi/qapi-visit-ui.h"
#include "qapi/qapi-commands-block-core.h"
#include "qapi/qapi-commands-migration.h"
@ -3452,6 +3454,21 @@ void qemu_init(int argc, char **argv, char **envp)
enable_mlock = qemu_opt_get_bool(opts, "mem-lock", false);
enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", false);
break;
case QEMU_OPTION_compat:
{
CompatPolicy *opts;
Visitor *v;
v = qobject_input_visitor_new_str(optarg, NULL,
&error_fatal);
visit_type_CompatPolicy(v, NULL, &opts, &error_fatal);
QAPI_CLONE_MEMBERS(CompatPolicy, &compat_policy, opts);
qapi_free_CompatPolicy(opts);
visit_free(v);
break;
}
case QEMU_OPTION_msg:
opts = qemu_opts_parse_noisily(qemu_find_opts("msg"), optarg,
false);

View File

@ -137,8 +137,6 @@ extern QemuOptsList qemu_chardev_opts;
static void init_qmp_commands(void)
{
qmp_init_marshal(&qmp_commands);
qmp_register_command(&qmp_commands, "query-qmp-schema",
qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG);
QTAILQ_INIT(&qmp_cap_negotiation_commands);
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",

View File

@ -34,7 +34,6 @@ stub_ss.add(files('ram-block.c'))
stub_ss.add(files('ramfb.c'))
stub_ss.add(files('replay.c'))
stub_ss.add(files('runstate-check.c'))
stub_ss.add(files('set-fd-handler.c'))
stub_ss.add(files('sysbus.c'))
stub_ss.add(files('target-get-monitor-def.c'))
stub_ss.add(files('target-monitor-defs.c'))

View File

@ -1,10 +0,0 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
void qemu_set_fd_handler(int fd,
IOHandler *fd_read,
IOHandler *fd_write,
void *opaque)
{
abort();
}

View File

@ -299,14 +299,15 @@
'features': [ 'feature1' ] }
{ 'command': 'test-features0',
'data': { 'fs0': 'FeatureStruct0',
'fs1': 'FeatureStruct1',
'fs2': 'FeatureStruct2',
'fs3': 'FeatureStruct3',
'fs4': 'FeatureStruct4',
'cfs1': 'CondFeatureStruct1',
'cfs2': 'CondFeatureStruct2',
'cfs3': 'CondFeatureStruct3' },
'data': { '*fs0': 'FeatureStruct0',
'*fs1': 'FeatureStruct1',
'*fs2': 'FeatureStruct2',
'*fs3': 'FeatureStruct3',
'*fs4': 'FeatureStruct4',
'*cfs1': 'CondFeatureStruct1',
'*cfs2': 'CondFeatureStruct2',
'*cfs3': 'CondFeatureStruct3' },
'returns': 'FeatureStruct1',
'features': [] }
{ 'command': 'test-command-features1',
@ -323,5 +324,8 @@
'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)',
'defined(TEST_IF_COND_2)'] } ] }
{ 'event': 'TEST-EVENT-FEATURES0',
'data': 'FeatureStruct1' }
{ 'event': 'TEST-EVENT-FEATURES1',
'features': [ 'deprecated' ] }

View File

@ -409,15 +409,15 @@ alternate FeatureAlternate1
case eins: FeatureStruct1
feature feature1
object q_obj_test-features0-arg
member fs0: FeatureStruct0 optional=False
member fs1: FeatureStruct1 optional=False
member fs2: FeatureStruct2 optional=False
member fs3: FeatureStruct3 optional=False
member fs4: FeatureStruct4 optional=False
member cfs1: CondFeatureStruct1 optional=False
member cfs2: CondFeatureStruct2 optional=False
member cfs3: CondFeatureStruct3 optional=False
command test-features0 q_obj_test-features0-arg -> None
member fs0: FeatureStruct0 optional=True
member fs1: FeatureStruct1 optional=True
member fs2: FeatureStruct2 optional=True
member fs3: FeatureStruct3 optional=True
member fs4: FeatureStruct4 optional=True
member cfs1: CondFeatureStruct1 optional=True
member cfs2: CondFeatureStruct2 optional=True
member cfs3: CondFeatureStruct3 optional=True
command test-features0 q_obj_test-features0-arg -> FeatureStruct1
gen=True success_response=True boxed=False oob=False preconfig=False
command test-command-features1 None -> None
gen=True success_response=True boxed=False oob=False preconfig=False
@ -440,6 +440,8 @@ command test-command-cond-features3 None -> None
gen=True success_response=True boxed=False oob=False preconfig=False
feature feature1
if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)']
event TEST-EVENT-FEATURES0 FeatureStruct1
boxed=False
event TEST-EVENT-FEATURES1 None
boxed=False
feature deprecated

View File

@ -1,4 +1,5 @@
#include "qemu/osdep.h"
#include "qapi/compat-policy.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qnum.h"
@ -49,12 +50,17 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
{
}
void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1,
FeatureStruct2 *fs2, FeatureStruct3 *fs3,
FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1,
CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3,
Error **errp)
FeatureStruct1 *qmp_test_features0(bool has_fs0, FeatureStruct0 *fs0,
bool has_fs1, FeatureStruct1 *fs1,
bool has_fs2, FeatureStruct2 *fs2,
bool has_fs3, FeatureStruct3 *fs3,
bool has_fs4, FeatureStruct4 *fs4,
bool has_cfs1, CondFeatureStruct1 *cfs1,
bool has_cfs2, CondFeatureStruct2 *cfs2,
bool has_cfs3, CondFeatureStruct3 *cfs3,
Error **errp)
{
return g_new0(FeatureStruct1, 1);
}
void qmp_test_command_features1(Error **errp)
@ -275,6 +281,75 @@ static void test_dispatch_cmd_io(void)
qobject_unref(ret3);
}
static void test_dispatch_cmd_deprecated(void)
{
const char *cmd = "{ 'execute': 'test-command-features1' }";
QDict *ret;
memset(&compat_policy, 0, sizeof(compat_policy));
/* accept */
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
assert(ret && qdict_size(ret) == 0);
qobject_unref(ret);
compat_policy.has_deprecated_input = true;
compat_policy.deprecated_input = COMPAT_POLICY_INPUT_ACCEPT;
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
assert(ret && qdict_size(ret) == 0);
qobject_unref(ret);
compat_policy.deprecated_input = COMPAT_POLICY_INPUT_REJECT;
do_qmp_dispatch_error(false, ERROR_CLASS_COMMAND_NOT_FOUND, cmd);
}
static void test_dispatch_cmd_arg_deprecated(void)
{
const char *cmd = "{ 'execute': 'test-features0',"
" 'arguments': { 'fs1': { 'foo': 42 } } }";
QDict *ret;
memset(&compat_policy, 0, sizeof(compat_policy));
/* accept */
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
assert(ret && qdict_size(ret) == 1);
qobject_unref(ret);
compat_policy.has_deprecated_input = true;
compat_policy.deprecated_input = COMPAT_POLICY_INPUT_ACCEPT;
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
assert(ret && qdict_size(ret) == 1);
qobject_unref(ret);
compat_policy.deprecated_input = COMPAT_POLICY_INPUT_REJECT;
do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, cmd);
}
static void test_dispatch_cmd_ret_deprecated(void)
{
const char *cmd = "{ 'execute': 'test-features0' }";
QDict *ret;
memset(&compat_policy, 0, sizeof(compat_policy));
/* default accept */
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
assert(ret && qdict_size(ret) == 1);
qobject_unref(ret);
compat_policy.has_deprecated_output = true;
compat_policy.deprecated_output = COMPAT_POLICY_OUTPUT_ACCEPT;
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
assert(ret && qdict_size(ret) == 1);
qobject_unref(ret);
compat_policy.deprecated_output = COMPAT_POLICY_OUTPUT_HIDE;
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
assert(ret && qdict_size(ret) == 0);
qobject_unref(ret);
}
/* test generated dealloc functions for generated types */
static void test_dealloc_types(void)
{
@ -349,6 +424,12 @@ int main(int argc, char **argv)
g_test_add_func("/qmp/dispatch_cmd_io", test_dispatch_cmd_io);
g_test_add_func("/qmp/dispatch_cmd_success_response",
test_dispatch_cmd_success_response);
g_test_add_func("/qmp/dispatch_cmd_deprecated",
test_dispatch_cmd_deprecated);
g_test_add_func("/qmp/dispatch_cmd_arg_deprecated",
test_dispatch_cmd_arg_deprecated);
g_test_add_func("/qmp/dispatch_cmd_ret_deprecated",
test_dispatch_cmd_ret_deprecated);
g_test_add_func("/qmp/dealloc_types", test_dealloc_types);
g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial);

View File

@ -14,6 +14,7 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qapi/compat-policy.h"
#include "qapi/error.h"
#include "qapi/qmp/qbool.h"
#include "qapi/qmp/qdict.h"
@ -140,6 +141,44 @@ static void test_event_d(TestEventData *data,
qobject_unref(data->expect);
}
static void test_event_deprecated(TestEventData *data, const void *unused)
{
data->expect = qdict_from_jsonf_nofail("{ 'event': 'TEST-EVENT-FEATURES1' }");
memset(&compat_policy, 0, sizeof(compat_policy));
qapi_event_send_test_event_features1();
g_assert(data->emitted);
compat_policy.has_deprecated_output = true;
compat_policy.deprecated_output = COMPAT_POLICY_OUTPUT_HIDE;
data->emitted = false;
qapi_event_send_test_event_features1();
g_assert(!data->emitted);
qobject_unref(data->expect);
}
static void test_event_deprecated_data(TestEventData *data, const void *unused)
{
memset(&compat_policy, 0, sizeof(compat_policy));
data->expect = qdict_from_jsonf_nofail("{ 'event': 'TEST-EVENT-FEATURES0',"
" 'data': { 'foo': 42 } }");
qapi_event_send_test_event_features0(42);
g_assert(data->emitted);
qobject_unref(data->expect);
compat_policy.has_deprecated_output = true;
compat_policy.deprecated_output = COMPAT_POLICY_OUTPUT_HIDE;
data->expect = qdict_from_jsonf_nofail("{ 'event': 'TEST-EVENT-FEATURES0' }");
qapi_event_send_test_event_features0(42);
g_assert(data->emitted);
qobject_unref(data->expect);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@ -148,6 +187,8 @@ int main(int argc, char **argv)
event_test_add("/event/event_b", test_event_b);
event_test_add("/event/event_c", test_event_c);
event_test_add("/event/event_d", test_event_d);
event_test_add("/event/deprecated", test_event_deprecated);
event_test_add("/event/deprecated_data", test_event_deprecated_data);
g_test_run();
return 0;

View File

@ -73,6 +73,7 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
* otherwise we get duplicate syms at link time.
*/
Monitor *monitor_cur(void) { return cur_mon; }
Monitor *monitor_set_cur(Coroutine *co, Monitor *mon) { abort(); }
int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); }
#ifndef _WIN32