qapi-commands.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  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. ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
  175. if not middle_mode:
  176. ret = "static " + ret
  177. return ret
  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 ret_type:
  187. if is_c_ptr(ret_type):
  188. retval = " %s retval = NULL;" % c_type(ret_type)
  189. else:
  190. retval = " %s retval;" % c_type(ret_type)
  191. ret += mcgen('''
  192. %(retval)s
  193. ''',
  194. retval=retval)
  195. if len(args) > 0:
  196. ret += mcgen('''
  197. %(visitor_input_containers_decl)s
  198. %(visitor_input_vars_decl)s
  199. %(visitor_input_block)s
  200. ''',
  201. visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"),
  202. visitor_input_vars_decl=gen_visitor_input_vars_decl(args),
  203. visitor_input_block=gen_visitor_input_block(args))
  204. else:
  205. ret += mcgen('''
  206. (void)args;
  207. ''')
  208. ret += mcgen('''
  209. %(sync_call)s
  210. ''',
  211. sync_call=gen_sync_call(name, args, ret_type, indent=4))
  212. if re.search('^ *goto out\\;', ret, re.MULTILINE):
  213. ret += mcgen('''
  214. out:
  215. ''')
  216. ret += mcgen('''
  217. error_propagate(errp, local_err);
  218. %(visitor_input_block_cleanup)s
  219. }
  220. ''',
  221. visitor_input_block_cleanup=gen_visitor_input_block(args,
  222. dealloc=True))
  223. return ret
  224. def gen_registry(commands):
  225. registry=""
  226. push_indent()
  227. for cmd in commands:
  228. options = 'QCO_NO_OPTIONS'
  229. if not cmd.get('success-response', True):
  230. options = 'QCO_NO_SUCCESS_RESP'
  231. registry += mcgen('''
  232. qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
  233. ''',
  234. name=cmd['command'], c_name=c_name(cmd['command']),
  235. opts=options)
  236. pop_indent()
  237. ret = mcgen('''
  238. static void qmp_init_marshal(void)
  239. {
  240. %(registry)s
  241. }
  242. qapi_init(qmp_init_marshal);
  243. ''',
  244. registry=registry.rstrip())
  245. return ret
  246. middle_mode = False
  247. (input_file, output_dir, do_c, do_h, prefix, opts) = \
  248. parse_command_line("m", ["middle"])
  249. for o, a in opts:
  250. if o in ("-m", "--middle"):
  251. middle_mode = True
  252. exprs = parse_schema(input_file)
  253. commands = filter(lambda expr: expr.has_key('command'), exprs)
  254. commands = filter(lambda expr: not expr.has_key('gen'), commands)
  255. c_comment = '''
  256. /*
  257. * schema-defined QMP->QAPI command dispatch
  258. *
  259. * Copyright IBM, Corp. 2011
  260. *
  261. * Authors:
  262. * Anthony Liguori <aliguori@us.ibm.com>
  263. *
  264. * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
  265. * See the COPYING.LIB file in the top-level directory.
  266. *
  267. */
  268. '''
  269. h_comment = '''
  270. /*
  271. * schema-defined QAPI function prototypes
  272. *
  273. * Copyright IBM, Corp. 2011
  274. *
  275. * Authors:
  276. * Anthony Liguori <aliguori@us.ibm.com>
  277. *
  278. * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
  279. * See the COPYING.LIB file in the top-level directory.
  280. *
  281. */
  282. '''
  283. (fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
  284. 'qmp-marshal.c', 'qmp-commands.h',
  285. c_comment, h_comment)
  286. fdef.write(mcgen('''
  287. #include "qemu-common.h"
  288. #include "qemu/module.h"
  289. #include "qapi/qmp/types.h"
  290. #include "qapi/qmp/dispatch.h"
  291. #include "qapi/visitor.h"
  292. #include "qapi/qmp-output-visitor.h"
  293. #include "qapi/qmp-input-visitor.h"
  294. #include "qapi/dealloc-visitor.h"
  295. #include "%(prefix)sqapi-types.h"
  296. #include "%(prefix)sqapi-visit.h"
  297. #include "%(prefix)sqmp-commands.h"
  298. ''',
  299. prefix=prefix))
  300. fdecl.write(mcgen('''
  301. #include "%(prefix)sqapi-types.h"
  302. #include "qapi/qmp/qdict.h"
  303. #include "qapi/error.h"
  304. ''',
  305. prefix=prefix))
  306. for cmd in commands:
  307. arglist = []
  308. ret_type = None
  309. if cmd.has_key('data'):
  310. arglist = cmd['data']
  311. if cmd.has_key('returns'):
  312. ret_type = cmd['returns']
  313. ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n"
  314. fdecl.write(ret)
  315. if ret_type:
  316. ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n"
  317. fdef.write(ret)
  318. if middle_mode:
  319. fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], arglist, ret_type, middle_mode))
  320. ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n"
  321. fdef.write(ret)
  322. if not middle_mode:
  323. ret = gen_registry(commands)
  324. fdef.write(ret)
  325. close_output(fdef, fdecl)