qapi-commands.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. #
  2. # QAPI command marshaller generator
  3. #
  4. # Copyright IBM, Corp. 2011
  5. # Copyright (C) 2014-2015 Red Hat, Inc.
  6. #
  7. # Authors:
  8. # Anthony Liguori <aliguori@us.ibm.com>
  9. # Michael Roth <mdroth@linux.vnet.ibm.com>
  10. # Markus Armbruster <armbru@redhat.com>
  11. #
  12. # This work is licensed under the terms of the GNU GPL, version 2.
  13. # See the COPYING file in the top-level directory.
  14. from ordereddict import OrderedDict
  15. from qapi import *
  16. import re
  17. def generate_command_decl(name, args, ret_type):
  18. arglist=""
  19. for argname, argtype, optional in parse_args(args):
  20. argtype = c_type(argtype, is_param=True)
  21. if optional:
  22. arglist += "bool has_%s, " % c_name(argname)
  23. arglist += "%s %s, " % (argtype, c_name(argname))
  24. return mcgen('''
  25. %(ret_type)s qmp_%(name)s(%(args)sError **errp);
  26. ''',
  27. ret_type=c_type(ret_type), name=c_name(name),
  28. args=arglist).strip()
  29. def gen_err_check(errvar):
  30. if errvar:
  31. return mcgen('''
  32. if (local_err) {
  33. goto out;
  34. }
  35. ''')
  36. return ''
  37. def gen_sync_call(name, args, ret_type, indent=0):
  38. ret = ""
  39. arglist=""
  40. retval=""
  41. if ret_type:
  42. retval = "retval = "
  43. for argname, argtype, optional in parse_args(args):
  44. if optional:
  45. arglist += "has_%s, " % c_name(argname)
  46. arglist += "%s, " % (c_name(argname))
  47. push_indent(indent)
  48. ret = mcgen('''
  49. %(retval)sqmp_%(name)s(%(args)s&local_err);
  50. ''',
  51. name=c_name(name), args=arglist, retval=retval).rstrip()
  52. if ret_type:
  53. ret += "\n" + gen_err_check('local_err')
  54. ret += "\n" + mcgen(''''
  55. %(marshal_output_call)s
  56. ''',
  57. marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip()
  58. pop_indent(indent)
  59. return ret.rstrip()
  60. def gen_marshal_output_call(name, ret_type):
  61. if not ret_type:
  62. return ""
  63. return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_name(name)
  64. def gen_visitor_input_containers_decl(args, obj):
  65. ret = ""
  66. push_indent()
  67. if len(args) > 0:
  68. ret += mcgen('''
  69. QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s);
  70. QapiDeallocVisitor *md;
  71. Visitor *v;
  72. ''',
  73. obj=obj)
  74. pop_indent()
  75. return ret.rstrip()
  76. def gen_visitor_input_vars_decl(args):
  77. ret = ""
  78. push_indent()
  79. for argname, argtype, optional in parse_args(args):
  80. if optional:
  81. ret += mcgen('''
  82. bool has_%(argname)s = false;
  83. ''',
  84. argname=c_name(argname))
  85. if is_c_ptr(argtype):
  86. ret += mcgen('''
  87. %(argtype)s %(argname)s = NULL;
  88. ''',
  89. argname=c_name(argname), argtype=c_type(argtype))
  90. else:
  91. ret += mcgen('''
  92. %(argtype)s %(argname)s = {0};
  93. ''',
  94. argname=c_name(argname), argtype=c_type(argtype))
  95. pop_indent()
  96. return ret.rstrip()
  97. def gen_visitor_input_block(args, dealloc=False):
  98. ret = ""
  99. errparg = '&local_err'
  100. errarg = 'local_err'
  101. if len(args) == 0:
  102. return ret
  103. push_indent()
  104. if dealloc:
  105. errparg = 'NULL'
  106. errarg = None;
  107. ret += mcgen('''
  108. qmp_input_visitor_cleanup(mi);
  109. md = qapi_dealloc_visitor_new();
  110. v = qapi_dealloc_get_visitor(md);
  111. ''')
  112. else:
  113. ret += mcgen('''
  114. v = qmp_input_get_visitor(mi);
  115. ''')
  116. for argname, argtype, optional in parse_args(args):
  117. if optional:
  118. ret += mcgen('''
  119. visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
  120. ''',
  121. c_name=c_name(argname), name=argname, errp=errparg)
  122. ret += gen_err_check(errarg)
  123. ret += mcgen('''
  124. if (has_%(c_name)s) {
  125. ''',
  126. c_name=c_name(argname))
  127. push_indent()
  128. ret += mcgen('''
  129. visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
  130. ''',
  131. c_name=c_name(argname), name=argname, argtype=argtype,
  132. visitor=type_name(argtype), errp=errparg)
  133. ret += gen_err_check(errarg)
  134. if optional:
  135. pop_indent()
  136. ret += mcgen('''
  137. }
  138. ''')
  139. if dealloc:
  140. ret += mcgen('''
  141. qapi_dealloc_visitor_cleanup(md);
  142. ''')
  143. pop_indent()
  144. return ret.rstrip()
  145. def gen_marshal_output(name, args, ret_type, middle_mode):
  146. if not ret_type:
  147. return ""
  148. ret = mcgen('''
  149. static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
  150. {
  151. Error *local_err = NULL;
  152. QmpOutputVisitor *mo = qmp_output_visitor_new();
  153. QapiDeallocVisitor *md;
  154. Visitor *v;
  155. v = qmp_output_get_visitor(mo);
  156. visit_type_%(visitor)s(v, &ret_in, "unused", &local_err);
  157. if (local_err) {
  158. goto out;
  159. }
  160. *ret_out = qmp_output_get_qobject(mo);
  161. out:
  162. error_propagate(errp, local_err);
  163. qmp_output_visitor_cleanup(mo);
  164. md = qapi_dealloc_visitor_new();
  165. v = qapi_dealloc_get_visitor(md);
  166. visit_type_%(visitor)s(v, &ret_in, "unused", NULL);
  167. qapi_dealloc_visitor_cleanup(md);
  168. }
  169. ''',
  170. c_ret_type=c_type(ret_type), c_name=c_name(name),
  171. visitor=type_name(ret_type))
  172. return ret
  173. def gen_marshal_input_decl(name, args, ret_type, middle_mode):
  174. if middle_mode:
  175. return 'int qmp_marshal_input_%s(Monitor *mon, const QDict *qdict, QObject **ret)' % c_name(name)
  176. else:
  177. return 'static void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
  178. def gen_marshal_input(name, args, ret_type, middle_mode):
  179. hdr = gen_marshal_input_decl(name, args, ret_type, middle_mode)
  180. ret = mcgen('''
  181. %(header)s
  182. {
  183. Error *local_err = NULL;
  184. ''',
  185. header=hdr)
  186. if middle_mode:
  187. ret += mcgen('''
  188. QDict *args = (QDict *)qdict;
  189. ''')
  190. if ret_type:
  191. if is_c_ptr(ret_type):
  192. retval = " %s retval = NULL;" % c_type(ret_type)
  193. else:
  194. retval = " %s retval;" % c_type(ret_type)
  195. ret += mcgen('''
  196. %(retval)s
  197. ''',
  198. retval=retval)
  199. if len(args) > 0:
  200. ret += mcgen('''
  201. %(visitor_input_containers_decl)s
  202. %(visitor_input_vars_decl)s
  203. %(visitor_input_block)s
  204. ''',
  205. visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"),
  206. visitor_input_vars_decl=gen_visitor_input_vars_decl(args),
  207. visitor_input_block=gen_visitor_input_block(args))
  208. else:
  209. ret += mcgen('''
  210. (void)args;
  211. ''')
  212. ret += mcgen('''
  213. %(sync_call)s
  214. ''',
  215. sync_call=gen_sync_call(name, args, ret_type, indent=4))
  216. if re.search('^ *goto out\\;', ret, re.MULTILINE):
  217. ret += mcgen('''
  218. out:
  219. ''')
  220. if not middle_mode:
  221. ret += mcgen('''
  222. error_propagate(errp, local_err);
  223. ''')
  224. ret += mcgen('''
  225. %(visitor_input_block_cleanup)s
  226. ''',
  227. visitor_input_block_cleanup=gen_visitor_input_block(args,
  228. dealloc=True))
  229. if middle_mode:
  230. ret += mcgen('''
  231. if (local_err) {
  232. qerror_report_err(local_err);
  233. error_free(local_err);
  234. return -1;
  235. }
  236. return 0;
  237. ''')
  238. else:
  239. ret += mcgen('''
  240. return;
  241. ''')
  242. ret += mcgen('''
  243. }
  244. ''')
  245. return ret
  246. def gen_registry(commands):
  247. registry=""
  248. push_indent()
  249. for cmd in commands:
  250. options = 'QCO_NO_OPTIONS'
  251. if not cmd.get('success-response', True):
  252. options = 'QCO_NO_SUCCESS_RESP'
  253. registry += mcgen('''
  254. qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
  255. ''',
  256. name=cmd['command'], c_name=c_name(cmd['command']),
  257. opts=options)
  258. pop_indent()
  259. ret = mcgen('''
  260. static void qmp_init_marshal(void)
  261. {
  262. %(registry)s
  263. }
  264. qapi_init(qmp_init_marshal);
  265. ''',
  266. registry=registry.rstrip())
  267. return ret
  268. def gen_command_decl_prologue(prefix=""):
  269. ret = mcgen('''
  270. #include "%(prefix)sqapi-types.h"
  271. #include "qapi/qmp/qdict.h"
  272. #include "qapi/error.h"
  273. ''',
  274. prefix=prefix)
  275. return ret
  276. def gen_command_def_prologue(prefix="", proxy=False):
  277. ret = mcgen('''
  278. #include "qemu-common.h"
  279. #include "qemu/module.h"
  280. #include "qapi/qmp/qerror.h"
  281. #include "qapi/qmp/types.h"
  282. #include "qapi/qmp/dispatch.h"
  283. #include "qapi/visitor.h"
  284. #include "qapi/qmp-output-visitor.h"
  285. #include "qapi/qmp-input-visitor.h"
  286. #include "qapi/dealloc-visitor.h"
  287. #include "%(prefix)sqapi-types.h"
  288. #include "%(prefix)sqapi-visit.h"
  289. ''',
  290. prefix=prefix)
  291. if not proxy:
  292. ret += '#include "%sqmp-commands.h"' % prefix
  293. return ret + "\n\n"
  294. middle_mode = False
  295. (input_file, output_dir, do_c, do_h, prefix, opts) = \
  296. parse_command_line("m", ["middle"])
  297. for o, a in opts:
  298. if o in ("-m", "--middle"):
  299. middle_mode = True
  300. exprs = parse_schema(input_file)
  301. commands = filter(lambda expr: expr.has_key('command'), exprs)
  302. commands = filter(lambda expr: not expr.has_key('gen'), commands)
  303. c_comment = '''
  304. /*
  305. * schema-defined QMP->QAPI command dispatch
  306. *
  307. * Copyright IBM, Corp. 2011
  308. *
  309. * Authors:
  310. * Anthony Liguori <aliguori@us.ibm.com>
  311. *
  312. * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
  313. * See the COPYING.LIB file in the top-level directory.
  314. *
  315. */
  316. '''
  317. h_comment = '''
  318. /*
  319. * schema-defined QAPI function prototypes
  320. *
  321. * Copyright IBM, Corp. 2011
  322. *
  323. * Authors:
  324. * Anthony Liguori <aliguori@us.ibm.com>
  325. *
  326. * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
  327. * See the COPYING.LIB file in the top-level directory.
  328. *
  329. */
  330. '''
  331. (fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
  332. 'qmp-marshal.c', 'qmp-commands.h',
  333. c_comment, h_comment)
  334. ret = gen_command_decl_prologue(prefix=prefix)
  335. fdecl.write(ret)
  336. ret = gen_command_def_prologue(prefix=prefix)
  337. fdef.write(ret)
  338. for cmd in commands:
  339. arglist = []
  340. ret_type = None
  341. if cmd.has_key('data'):
  342. arglist = cmd['data']
  343. if cmd.has_key('returns'):
  344. ret_type = cmd['returns']
  345. ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n"
  346. fdecl.write(ret)
  347. if ret_type:
  348. ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n"
  349. fdef.write(ret)
  350. if middle_mode:
  351. fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], arglist, ret_type, middle_mode))
  352. ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n"
  353. fdef.write(ret)
  354. if not middle_mode:
  355. ret = gen_registry(commands)
  356. fdef.write(ret)
  357. close_output(fdef, fdecl)