1""" 2QAPI command marshaller generator 3 4Copyright IBM, Corp. 2011 5Copyright (C) 2014-2018 Red Hat, Inc. 6 7Authors: 8 Anthony Liguori <aliguori@us.ibm.com> 9 Michael Roth <mdroth@linux.vnet.ibm.com> 10 Markus Armbruster <armbru@redhat.com> 11 12This work is licensed under the terms of the GNU GPL, version 2. 13See the COPYING file in the top-level directory. 14""" 15 16from .common import c_name, mcgen 17from .gen import ( 18 QAPIGenCCode, 19 QAPISchemaModularCVisitor, 20 build_params, 21 ifcontext, 22) 23 24 25def gen_command_decl(name, arg_type, boxed, ret_type): 26 return mcgen(''' 27%(c_type)s qmp_%(c_name)s(%(params)s); 28''', 29 c_type=(ret_type and ret_type.c_type()) or 'void', 30 c_name=c_name(name), 31 params=build_params(arg_type, boxed, 'Error **errp')) 32 33 34def gen_call(name, arg_type, boxed, ret_type): 35 ret = '' 36 37 argstr = '' 38 if boxed: 39 assert arg_type 40 argstr = '&arg, ' 41 elif arg_type: 42 assert not arg_type.variants 43 for memb in arg_type.members: 44 if memb.optional: 45 argstr += 'arg.has_%s, ' % c_name(memb.name) 46 argstr += 'arg.%s, ' % c_name(memb.name) 47 48 lhs = '' 49 if ret_type: 50 lhs = 'retval = ' 51 52 ret = mcgen(''' 53 54 %(lhs)sqmp_%(c_name)s(%(args)s&err); 55 error_propagate(errp, err); 56''', 57 c_name=c_name(name), args=argstr, lhs=lhs) 58 if ret_type: 59 ret += mcgen(''' 60 if (err) { 61 goto out; 62 } 63 64 qmp_marshal_output_%(c_name)s(retval, ret, errp); 65''', 66 c_name=ret_type.c_name()) 67 return ret 68 69 70def gen_marshal_output(ret_type): 71 return mcgen(''' 72 73static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, 74 QObject **ret_out, Error **errp) 75{ 76 Visitor *v; 77 78 v = qobject_output_visitor_new(ret_out); 79 if (visit_type_%(c_name)s(v, "unused", &ret_in, errp)) { 80 visit_complete(v, ret_out); 81 } 82 visit_free(v); 83 v = qapi_dealloc_visitor_new(); 84 visit_type_%(c_name)s(v, "unused", &ret_in, NULL); 85 visit_free(v); 86} 87''', 88 c_type=ret_type.c_type(), c_name=ret_type.c_name()) 89 90 91def build_marshal_proto(name): 92 return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' 93 % c_name(name)) 94 95 96def gen_marshal_decl(name): 97 return mcgen(''' 98%(proto)s; 99''', 100 proto=build_marshal_proto(name)) 101 102 103def gen_marshal(name, arg_type, boxed, ret_type): 104 have_args = boxed or (arg_type and not arg_type.is_empty()) 105 106 ret = mcgen(''' 107 108%(proto)s 109{ 110 Error *err = NULL; 111 bool ok = false; 112 Visitor *v; 113''', 114 proto=build_marshal_proto(name)) 115 116 if ret_type: 117 ret += mcgen(''' 118 %(c_type)s retval; 119''', 120 c_type=ret_type.c_type()) 121 122 if have_args: 123 ret += mcgen(''' 124 %(c_name)s arg = {0}; 125''', 126 c_name=arg_type.c_name()) 127 128 ret += mcgen(''' 129 130 v = qobject_input_visitor_new(QOBJECT(args)); 131 if (!visit_start_struct(v, NULL, NULL, 0, errp)) { 132 goto out; 133 } 134''') 135 136 if have_args: 137 ret += mcgen(''' 138 if (visit_type_%(c_arg_type)s_members(v, &arg, errp)) { 139 ok = visit_check_struct(v, errp); 140 } 141''', 142 c_arg_type=arg_type.c_name()) 143 else: 144 ret += mcgen(''' 145 ok = visit_check_struct(v, errp); 146''') 147 148 ret += mcgen(''' 149 visit_end_struct(v, NULL); 150 if (!ok) { 151 goto out; 152 } 153''') 154 155 ret += gen_call(name, arg_type, boxed, ret_type) 156 157 ret += mcgen(''' 158 159out: 160 visit_free(v); 161''') 162 163 ret += mcgen(''' 164 v = qapi_dealloc_visitor_new(); 165 visit_start_struct(v, NULL, NULL, 0, NULL); 166''') 167 168 if have_args: 169 ret += mcgen(''' 170 visit_type_%(c_arg_type)s_members(v, &arg, NULL); 171''', 172 c_arg_type=arg_type.c_name()) 173 174 ret += mcgen(''' 175 visit_end_struct(v, NULL); 176 visit_free(v); 177''') 178 179 ret += mcgen(''' 180} 181''') 182 return ret 183 184 185def gen_register_command(name, success_response, allow_oob, allow_preconfig, 186 coroutine): 187 options = [] 188 189 if not success_response: 190 options += ['QCO_NO_SUCCESS_RESP'] 191 if allow_oob: 192 options += ['QCO_ALLOW_OOB'] 193 if allow_preconfig: 194 options += ['QCO_ALLOW_PRECONFIG'] 195 if coroutine: 196 options += ['QCO_COROUTINE'] 197 198 if not options: 199 options = ['QCO_NO_OPTIONS'] 200 201 ret = mcgen(''' 202 qmp_register_command(cmds, "%(name)s", 203 qmp_marshal_%(c_name)s, %(opts)s); 204''', 205 name=name, c_name=c_name(name), 206 opts=" | ".join(options)) 207 return ret 208 209 210def gen_registry(registry, prefix): 211 ret = mcgen(''' 212 213void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) 214{ 215 QTAILQ_INIT(cmds); 216 217''', 218 c_prefix=c_name(prefix, protect=False)) 219 ret += registry 220 ret += mcgen(''' 221} 222''') 223 return ret 224 225 226class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): 227 228 def __init__(self, prefix): 229 super().__init__( 230 prefix, 'qapi-commands', 231 ' * Schema-defined QAPI/QMP commands', None, __doc__) 232 self._regy = QAPIGenCCode(None) 233 self._visited_ret_types = {} 234 235 def _begin_user_module(self, name): 236 self._visited_ret_types[self._genc] = set() 237 commands = self._module_basename('qapi-commands', name) 238 types = self._module_basename('qapi-types', name) 239 visit = self._module_basename('qapi-visit', name) 240 self._genc.add(mcgen(''' 241#include "qemu/osdep.h" 242#include "qapi/visitor.h" 243#include "qapi/qmp/qdict.h" 244#include "qapi/qobject-output-visitor.h" 245#include "qapi/qobject-input-visitor.h" 246#include "qapi/dealloc-visitor.h" 247#include "qapi/error.h" 248#include "%(visit)s.h" 249#include "%(commands)s.h" 250 251''', 252 commands=commands, visit=visit)) 253 self._genh.add(mcgen(''' 254#include "%(types)s.h" 255 256''', 257 types=types)) 258 259 def visit_end(self): 260 self._add_system_module('init', ' * QAPI Commands initialization') 261 self._genh.add(mcgen(''' 262#include "qapi/qmp/dispatch.h" 263 264void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); 265''', 266 c_prefix=c_name(self._prefix, protect=False))) 267 self._genc.preamble_add(mcgen(''' 268#include "qemu/osdep.h" 269#include "%(prefix)sqapi-commands.h" 270#include "%(prefix)sqapi-init-commands.h" 271''', 272 prefix=self._prefix)) 273 self._genc.add(gen_registry(self._regy.get_content(), self._prefix)) 274 275 def visit_command(self, name, info, ifcond, features, 276 arg_type, ret_type, gen, success_response, boxed, 277 allow_oob, allow_preconfig, coroutine): 278 if not gen: 279 return 280 # FIXME: If T is a user-defined type, the user is responsible 281 # for making this work, i.e. to make T's condition the 282 # conjunction of the T-returning commands' conditions. If T 283 # is a built-in type, this isn't possible: the 284 # qmp_marshal_output_T() will be generated unconditionally. 285 if ret_type and ret_type not in self._visited_ret_types[self._genc]: 286 self._visited_ret_types[self._genc].add(ret_type) 287 with ifcontext(ret_type.ifcond, 288 self._genh, self._genc, self._regy): 289 self._genc.add(gen_marshal_output(ret_type)) 290 with ifcontext(ifcond, self._genh, self._genc, self._regy): 291 self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) 292 self._genh.add(gen_marshal_decl(name)) 293 self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) 294 self._regy.add(gen_register_command(name, success_response, 295 allow_oob, allow_preconfig, 296 coroutine)) 297 298 299def gen_commands(schema, output_dir, prefix): 300 vis = QAPISchemaGenCommandVisitor(prefix) 301 schema.visit(vis) 302 vis.write(output_dir) 303