[RFCv2 00/46] RFC: Generate parsexml/formatbuf functions based on directives

V1 here: [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html] Differ from V1: * Move the generator into scripts/xmlgen and rename it 'xmlgen'. * Declare virXMLChildNode and virXMLChildNodeSet in libvirt_private.syms. * Replace VIR_FREE with g_free and VIR_ALLOC[_N] with g_new0. * Adjust virReportError to avoid unnecessary translation. * Remove the macro VIR_USED and use G_GNUC_UNUSED to declare arguments. * When parsing string member, assign value to it directly instead of using middle variable. * Don't set libclang_path. Just use python-clang's default setting. * Use virEscapeString for escaping xml characters. * Enable directive 'genformat' with a parameter to support separation mode. * Add directive 'xmlswitch' and 'xmlgroup' to support discriminated unions. * Allow directive 'array' and 'specified' to carry with a parameter, which specifies its counterpart explicitly. * Enable directive 'xmlattr' with path. * Add directive 'formatflag' and 'formathook'. For those new and changed directives, illustrate them by an example: struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ char *passwd; /* xmlattr, formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE */ bool expires; time_t validTo; /* xmlattr:passwdValidTo, specified:expires */ virDomainGraphicsAuthConnectedType connected; /* xmlattr */ }; struct _virDomainGraphicsListenDef { /* genparse:withhook, genformat */ virDomainGraphicsListenType type; /* xmlattr */ char *address; /* xmlattr, formathook */ char *network; /* xmlattr, formatflag:VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK */ char *socket; /* xmlattr, formathook */ int fromConfig; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ bool autoGenerated; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ }; struct _virDomainGraphicsSDLDef { /* genparse, genformat:separate */ char *display; /* xmlattr */ char *xauth; /* xmlattr */ bool fullscreen; /* xmlattr */ virTristateBool gl; /* xmlattr:gl/enable */ }; struct _virDomainGraphicsDef { /* genparse:concisehook, genformat */ virObjectPtr privateData; virDomainGraphicsType type; /* xmlattr */ size_t nListens; virDomainGraphicsListenDefPtr listens; /* xmlelem, array:nListens */ union { virDomainGraphicsSDLDef sdl; /* xmlgroup */ virDomainGraphicsVNCDef vnc; /* xmlgroup */ virDomainGraphicsRDPDef rdp; /* xmlgroup */ virDomainGraphicsDesktopDef desktop; /* xmlgroup */ virDomainGraphicsSpiceDef spice; /* xmlgroup */ virDomainGraphicsEGLHeadlessDef egl_headless; /* xmlgroup */ } data; /* xmlswitch:type */ }; Explanation for these directives: - genformat[:separate|onlyattrs|onlyelems] Only work on a struct. Generate formatbuf function for this struct only if 'genformat' is specified. The function name is based on struct-name and suffixed with 'FormatBuf'. When 'genformat:separate' is specified, generate two formatbuf functions rather than a single full-mode formatbuf function. One for formatting attributes and another for formatting elements. These function names are based on struct-name and suffixed with 'FormatAttr' and 'FormatElem' respectively. The 'onlyattrs' and 'onlyelems' are just like 'separate', but only generate one of those two functions according to its denotation. - xmlattr[:[parentname/]thename] Parse/Format the field as an XML attribute or attribute wrapped by an XML element. If only 'thename' is specified, use it as the XML attribute name; or use the filed name. The 'parentname' is the name of the attribute's parent element. If 'parentname/thename' is specified, the corresponding form is <parentname thename='..' />. - xmlgroup The field is a struct, but its corresponding form in XML is a group rather than an element. - xmlswitch:thename Only for discriminated union. 'thename' is the name of its relative enum. The name of each union member should match a shortname of the enum. - array[:countername] Parse/Format the field as an array. Each array field must have an related counter field, which name is specified by 'countername'. If 'countername' is omitted, follow the pattern: n + 'field_name'. - specified[:thename] This field has an related field to indicate its existence, and 'thename' specifies the name of this related field. When 'thename' is omitted, follow the pattern: 'field_name' + '_specified'. - formatflag:[!|%]flag This field will be formatted and written out to XML only if the 'flag' hits a target flagset. The target flagset is passed into the formatbuf function through the argument 'opaque'. Adding a '!' before 'flag' means NOT hitting. Adding a '%' before 'flag' means that flag hitting-check is the unique condition for formatting this field. For example, for 'passwd' in 'virDomainGraphicsAuthDef', the directive is: formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE then the generated code: if (def->passwd && (virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE)) virBufferEscapeString(buf, " passwd='%s'", def->passwd); If '%' is inserted like this: formatflag:%VIR_DOMAIN_DEF_FORMAT_SECURE then the generated code: if ((virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE)) virBufferEscapeString(buf, " passwd='%s'", def->passwd); - formathook Introduce hooks to handle the field if xmlgen can't deal with it now. E.g., virDomainGraphicsListenDef have two fields with 'formathook', which are 'address' and 'socket'. The xmlgen will generate the declaration of some hooks for formatting these fields and developers should implement them. 1) Check the declaration of hook by a commandline. # ./scripts/xmlgen/go show virDomainGraphicsListenDef -kf int virDomainGraphicsListenDefFormatHook(const virDomainGraphicsListenDef *def, const void *parent, const void *opaque, virBufferPtr addressBuf, virBufferPtr socketBuf); bool virDomainGraphicsListenDefCheckHook(const virDomainGraphicsListenDef *def, const void *parent, void *opaque, bool result); 2) Implement these two hooks in src/conf/domain_conf.c. 2.1) virXXXFormatHook It is the hook for formatting field 'address' and 'socket'. The 'addressBuf' and 'socketBuf' are used for output destinations respectively. 2.2) virXXXCheckHook For structs, the xmlgen generates virXXXCheck function to come with the virXXXFormatBuf. The virXXXCheck reports whether the corresponding XML element is null. The virXXXCheckHook intercepts the 'result' of virXXXCheck. It changes 'result' or just forwards it according to those fields with 'formathook'. Thanks! Shi Lei (46): scripts: Add a tool to generate xml parse/format functions maint: Check python3-clang maint: Call xmlgen automatically when c-head-files change util: Add some xml-helper-functions to cooperate with xmlgen util: Add helper functions for 'bool' and 'time_t' and cooperate with xmlgen util: Add parsexml/formatbuf helper functions for virSocketAddr conf: Extract error-checking code from virNetworkDNSTxtDefParseXML conf: Replace virNetworkDNSTxtDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSTxtDefFormatBuf conf: Extract error-checking code from virNetworkDNSSrvDefParseXML conf: Replace virNetworkDNSSrvDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSSrvDefFormatBuf conf: Extract error-checking code from virNetworkDNSHostDefParseXML conf: Replace virNetworkDNSHostDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSHostDefFormatBuf conf: Extract virNetworkDNSForwarderParseXML from virNetworkDNSParseXML conf: Replace virNetworkDNSForwarderParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSForwarderFormatBuf conf: Extract error-checking code from virNetworkDNSDefParseXML conf: Replace virNetworkDNSDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSDefFormatBuf conf: Extract embedded structs from virDomainGraphicsDef as standalone structs conf: Replace virDomainGraphicsDefParseXMLSDL(hardcoded) with virDomainGraphicsSDLDefParseXML(generated) conf: Generate format functions for virDomainGraphicsSDLDef conf: Replace virDomainGraphicsAuthDefParseXML(hardcoded) with namesake(generated) conf: Generate format functions for virDomainGraphicsAuthDef conf: Extract error-checking code from virDomainGraphicsDefParseXMLVNC conf: Replace virDomainGraphicsDefParseXMLVNC(hardcoded) with virDomainGraphicsVNCDefParseXML(generated) conf: Generate virDomainGraphicsVNCDefFormatAttr conf: Extract error-checking code from virDomainGraphicsDefParseXMLRDP conf: Replace virDomainGraphicsDefParseXMLRDP(hardcoded) with virDomainGraphicsRDPDefParseXML(generated) conf: Generate virDomainGraphicsRDPDefFormatAttr conf: Replace virDomainGraphicsDefParseXMLDesktop(hardcoded) with virDomainGraphicsDesktopDefParseXML(generated) conf: Generate virDomainGraphicsDesktopDefFormatAttr conf: Add virSpiceChannelDef to help to parse the member 'channels' of virDomainGraphicsSpiceDef conf: Extract error-checking code from virDomainGraphicsDefParseXMLSpice conf: Replace virDomainGraphicsDefParseXMLSpice(hardcoded) with virDomainGraphicsSpiceDefParseXML(generated) conf: Generate virDomainGraphicsSpiceDefFormatElem and virDomainGraphicsSpiceDefFormatAttr conf: Replace virDomainGraphicsDefParseXMLEGLHeadless(hardcoded) with virDomainGraphicsEGLHeadlessDefParseXML(generated) conf: Generate virDomainGraphicsEGLHeadlessDefFormatElem conf: Extract error-checking code from virDomainGraphicsListenDefParseXML conf: Replace virDomainGraphicsListenDefParseXML(hardcoded) with namesake(generated) conf: Generate virDomainGraphicsListenDefFormatBuf conf: Extract error-checking code from virDomainGraphicsDefParseXML conf: Replace virDomainGraphicsDefParseXML(hardcoded) with namesake(generated) conf: Replace virDomainGraphicsDefFormat(hardcoded) with virDomainGraphicsDefFormatBuf(generated) meson.build | 5 + po/POTFILES.in | 3 + scripts/meson.build | 8 + scripts/xmlgen/directive.py | 1115 ++++++++++++++++++++ scripts/xmlgen/go | 7 + scripts/xmlgen/main.py | 439 ++++++++ scripts/xmlgen/utils.py | 121 +++ src/conf/domain_conf.c | 1650 +++++++++--------------------- src/conf/domain_conf.h | 179 ++-- src/conf/meson.build | 41 + src/conf/network_conf.c | 467 ++------- src/conf/network_conf.h | 54 +- src/conf/virconftypes.h | 18 + src/libvirt_private.syms | 9 + src/meson.build | 6 + src/qemu/qemu_command.c | 4 + src/qemu/qemu_driver.c | 2 + src/qemu/qemu_hotplug.c | 2 + src/qemu/qemu_migration_cookie.c | 1 + src/qemu/qemu_process.c | 5 + src/qemu/qemu_validate.c | 2 + src/util/virsocketaddr.c | 42 + src/util/virsocketaddr.h | 26 +- src/util/virstring.c | 57 ++ src/util/virstring.h | 9 + src/util/virxml.c | 105 ++ src/util/virxml.h | 6 + src/vmx/vmx.c | 1 + tests/meson.build | 1 + tools/meson.build | 2 + 30 files changed, 2738 insertions(+), 1649 deletions(-) create mode 100644 scripts/xmlgen/directive.py create mode 100755 scripts/xmlgen/go create mode 100755 scripts/xmlgen/main.py create mode 100644 scripts/xmlgen/utils.py -- 2.25.1

This tool is used to generate parsexml/formatbuf functions. It is based on libclang and its python-binding. Some directives (such as genparse, xmlattr, etc.) need to be added on the declarations of structs to direct the tool. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- po/POTFILES.in | 1 + scripts/xmlgen/directive.py | 1115 +++++++++++++++++++++++++++++++++++ scripts/xmlgen/go | 7 + scripts/xmlgen/main.py | 439 ++++++++++++++ scripts/xmlgen/utils.py | 121 ++++ 5 files changed, 1683 insertions(+) create mode 100644 scripts/xmlgen/directive.py create mode 100755 scripts/xmlgen/go create mode 100755 scripts/xmlgen/main.py create mode 100644 scripts/xmlgen/utils.py diff --git a/po/POTFILES.in b/po/POTFILES.in index 3d6c20c..c9c0262 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -5,6 +5,7 @@ @BUILDDIR@src/admin/admin_server_dispatch_stubs.h @BUILDDIR@src/remote/remote_client_bodies.h @BUILDDIR@src/remote/remote_daemon_dispatch_stubs.h +@SRCDIR@scripts/xmlgen/directive.py @SRCDIR@src/access/viraccessdriverpolkit.c @SRCDIR@src/access/viraccessmanager.c @SRCDIR@src/admin/admin_server.c diff --git a/scripts/xmlgen/directive.py b/scripts/xmlgen/directive.py new file mode 100644 index 0000000..9b73931 --- /dev/null +++ b/scripts/xmlgen/directive.py @@ -0,0 +1,1115 @@ +# +# Copyright (C) 2020 Shandong Massclouds Co.,Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# <http://www.gnu.org/licenses/>. +# + +import json +from collections import OrderedDict +from utils import singleton, dedup, Block, Terms, render + +BUILTIN_TYPES = { + 'String': {}, + 'Bool': {}, + 'Chars': { + 'conv': 'virStrcpyStatic(def->${name}, ${name}Str)' + }, + 'UChars': { + 'conv': 'virStrcpyStatic((char *)def->${name}, ${mdvar})' + }, + 'Int': { + 'fmt': '%d', + 'conv': 'virStrToLong_i(${mdvar}, NULL, 0, &def->${name})' + }, + 'UInt': { + 'fmt': '%u', + 'conv': 'virStrToLong_uip(${mdvar}, NULL, 0, &def->${name})' + }, + 'ULong': { + 'fmt': '%lu', + 'conv': 'virStrToLong_ulp(${mdvar}, NULL, 0, &def->${name})' + }, + 'ULongLong': { + 'fmt': '%llu', + 'conv': 'virStrToLong_ullp(${mdvar}, NULL, 0, &def->${name})' + }, + 'U8': { + 'fmt': '%u', + 'conv': 'virStrToLong_u8p(${mdvar}, NULL, 0, &def->${name})' + }, + 'U32': { + 'fmt': '%u', + 'conv': 'virStrToLong_uip(${mdvar}, NULL, 0, &def->${name})' + }, + 'Time': { + 'conv': 'virStrToTime(${mdvar}, &def->${name})' + }, +} + + +@singleton +class TypeTable(OrderedDict): + def __init__(self): + OrderedDict.__init__(self) + for name, kvs in BUILTIN_TYPES.items(): + kvs['name'] = name + kvs['meta'] = 'Builtin' + self[name] = kvs + + def register(self, kvs): + name = kvs['name'] + if name not in self: + self[name] = kvs + return name + + def get(self, name): + if name in self: + return self[name] + return {'meta': 'Struct', 'name': name, 'external': True} + + def check(self, name): + return name in self + + +T_NAMESPACE_PARSE = [ + 'if (xmlopt)', + ' def->ns = xmlopt->ns;', + 'if (def->ns.parse) {', + ' if (virXMLNamespaceRegister(ctxt, &def->ns) < 0)', + ' goto error;', + ' if ((def->ns.parse)(ctxt, &def->namespaceData) < 0)', + ' goto error;', + '}' +] + +T_NAMESPACE_FORMAT_BEGIN = [ + 'if (def->namespaceData && def->ns.format)', + ' virXMLNamespaceFormatNS(buf, &def->ns);' +] + +T_NAMESPACE_FORMAT_END = [ + 'if (def->namespaceData && def->ns.format) {', + ' if ((def->ns.format)(buf, def->namespaceData) < 0)', + ' return -1;', + '}' +] + + +def funcSignature(rtype, name, args): + alignment = ' ' * (len(name) + 1) + connector = ',\n' + alignment + + ret = [] + ret.append(rtype) + ret.append('%s(%s)' % (name, connector.join(args))) + return Block(ret) + + +def counterName(member): + if isinstance(member['array'], bool): + return 'n' + member['name'] + return member['array'] + + +def loop(count, block): + assert isinstance(block, list) and len(block) > 0 + lp = ' {' if len(block) > 1 else '' + + ret = Block() + ret.format('if (${count} > 0) {', count=count) + ret.format(' size_t i;') + ret.format(' for (i = 0; i < ${count}; i++)${lp}', count=count, lp=lp) + ret.mapfmt(' ${_each_line_}', block) + ret.format(' }' if lp else None) + ret.format('}') + return ret + + +def ispointer(member): + mtype = TypeTable().get(member['type']) + return member['pointer'] and not mtype.get('external') + + +def clearMember(member, pre_name=None): + + # Callback for make_switch + def _switch_clear_cb(child, switch, _): + return clearMember(child, switch['name']) + + if member.get('xmlswitch'): + return makeSwitch(member, _switch_clear_cb, None) + + mtype = TypeTable().get(member['type']) + + if pre_name: + ref = 'def->%s.%s' % (pre_name, member['name']) + else: + ref = 'def->%s' % member['name'] + + if member.get('array'): + ref += '[i]' + + ret = Block() + if mtype['meta'] == 'Struct': + if ispointer(member): + ret.format('${tname}Clear(${ref});', tname=mtype['name'], ref=ref) + ret.format('g_free(${ref});', ref=ref) + ret.format('${ref} = NULL;', ref=ref) + else: + ret.format('${tname}Clear(&${ref});', tname=mtype['name'], ref=ref) + elif mtype['name'] == 'String': + ret.format('g_free(${ref});', ref=ref) + ret.format('${ref} = NULL;', ref=ref) + elif mtype['name'] in ['Chars', 'UChars']: + ret.format('memset(${ref}, 0, sizeof(${ref}));', ref=ref) + elif not member.get('array'): + ret.format('${ref} = 0;', ref=ref) + + if member.get('specified'): + assert not member.get('array'), "'specified' can't come with 'array'." + if isinstance(member['specified'], str): + ret.format('def->${sname} = false;', sname=member['specified']) + else: + ret.format('${ref}_specified = false;', ref=ref) + + if member.get('array') and len(ret) > 0: + count = 'def->' + counterName(member) + ret = loop(count, ret) + ret.format('g_free(def->${name});', name=member['name']) + ret.format('def->${name} = 0;', name=member['name']) + ret.format('${count} = 0;', count=count) + + return ret + + +def makeClearFunc(writer, atype): + if 'genparse' not in atype: + return + + args = ['%sPtr def' % atype['name']] + signature = funcSignature('void', atype['name'] + 'Clear', args) + writer.write(atype, 'clearfunc', '.h', signature.output() + ';') + + ret = Block() + ret.extend(signature) + ret.format('{') + ret.format(' if (!def)') + ret.format(' return;') + ret.newline() + + delay_members = [] + for member in atype['members']: + if member.get('delay_clear'): + delay_members.append(member) + continue + + ret.mapfmt(' ${_each_line_}', clearMember(member)) + ret.newline() + + for member in delay_members: + ret.mapfmt(' ${_each_line_}', clearMember(member)) + ret.newline() + + if 'namespace' in atype: + ret.format(' if (def->namespaceData && def->ns.free)') + ret.format(' (def->ns.free)(def->namespaceData);') + else: + ret.pop() # Remove last newline + + ret.format('}') + writer.write(atype, 'clearfunc', '.c', ret.output()) + + +def reportInvalid(tname, mdvar): + ret = Block() + ret.append('virReportError(VIR_ERR_XML_ERROR,') + ret.append(" _(\"Invalid '%s' setting '%s' in '%s'\"),") + ret.format(' "${tname}", ${mdvar}, instname);', + tname=tname, mdvar=mdvar) + return ret + + +def reportMissing(tname): + ret = Block() + ret.append('virReportError(VIR_ERR_XML_ERROR,') + ret.append(" _(\"Missing '%s' setting in '%s'\"),") + ret.format(' "${tname}", instname);', tname=tname) + return ret + + +def checkError(kind, cond, tname, mdvar): + if kind == 'Invalid': + report = reportInvalid(tname, mdvar) + elif kind == 'Missing': + report = reportMissing(tname) + else: + assert False, '%s is unsupported.' % kind + + ret = Block() + ret.format('if (${cond}) {', cond=cond) + ret.mapfmt(' ${_each_line_}', report) + ret.append(' goto error;') + ret.append('}') + return ret + + +def parseMember(member, parent, tmpvars, pre_name=None): + mtype = TypeTable().get(member['type']) + + # + # Helper functions + # + def _middle_var(member): + ret = member['name'].replace('.', '_') + if member.get('xmlgroup'): + ret = 'node' + elif mtype['name'] == 'String': + ret = 'def->' + ret + elif member.get('xmlattr') or mtype['meta'] != 'Struct': + ret += 'Str' + else: + ret += 'Node' + return ret + + def _read_xml_func(mdvar, tname): + if member.get('xmlattr'): + if '/' in tname: + funcname = 'virXMLChildPropString' + else: + funcname = 'virXMLPropString' + elif member.get('xmlelem'): + if mtype['meta'] == 'Struct': + funcname = 'virXMLChildNode' + else: + funcname = 'virXMLChildNodeContent' + else: + return None + + return render('${mdvar} = ${funcname}(node, "${tname}");', + mdvar=mdvar, funcname=funcname, tname=tname) + + def _assign_struct(name, member, mdvar): + ret = Block() + if ispointer(member): + ret.format('def->${name} = g_new0(typeof(*def->${name}), 1);', + name=name) + + func = mtype['name'] + 'ParseXML' + amp = '' if ispointer(member) else '&' + tmpl = '${func}(${mdvar}, ${amp}def->${name}, instname, def, opaque)' + fn = render(tmpl, func=func, mdvar=mdvar, amp=amp, name=name) + return if_cond(fn + ' < 0', ['goto error;']) + + def _assign_non_struct(name, member, mdvar): + if mtype['meta'] == 'Enum': + typename = mtype['name'] + if not typename.endswith('Type'): + typename += 'Type' + expr = render('(def->${name} = ${typename}FromString(${mdvar}))', + name=name, typename=typename, mdvar=mdvar) + expr += ' <= 0' + elif mtype['name'] == 'Bool': + truevalue = member.get('truevalue', 'yes') + expr = render('virStrToBool(${mdvar}, "${truth}", &def->${name})', + mdvar=mdvar, truth=truevalue, name=name) + expr += ' < 0' + else: + builtin = BUILTIN_TYPES.get(mtype['name']) + assert builtin, mtype['name'] + tmpl = builtin.get('conv', None) + if tmpl: + expr = render(tmpl, name=name, mdvar=mdvar, tname=tname) + expr += ' < 0' + else: + return None + + return checkError('Invalid', expr, tname, mdvar) + + def _parse_array(name, member, tname): + num = 'n%sNodes' % Terms.upperInitial(tname) + tmpvars.append(num) + tmpvars.append('nodes') + count = counterName(member) + + if mtype['meta'] == 'Struct': + item = _assign_struct(name + '[i]', member, 'tnode') + else: + item = ['def->%s[i] = virXMLNodeContentString(tnode);' % name] + + ret = Block() + ret.format('${num} = virXMLChildNodeSet(node, "${tname}", &nodes);', + num=num, tname=tname) + ret.format('if (${num} > 0) {', num=num) + ret.format(' size_t i;') + ret.newline() + ret.format(' def->${name} = g_new0(typeof(*def->${name}), ${num});', + name=name, num=num) + ret.format(' for (i = 0; i < ${num}; i++) {', num=num) + ret.format(' xmlNodePtr tnode = nodes[i];') + ret.mapfmt(' ${_each_line_}', item) + ret.format(' }') + ret.format(' def->${count} = ${num};', count=count, num=num) + ret.format(' g_free(nodes);') + ret.format(' nodes = NULL;') + ret.format('} else if (${num} < 0) {', num=num) + ret.format(' virReportError(VIR_ERR_XML_ERROR,') + ret.format(' _("Invalid %s element found."),') + ret.format(' "${tname}");', tname=tname) + ret.format(' goto error;') + + if member.get('required'): + ret.append('} else {') + ret.mapfmt(' ${_each_line_}', reportMissing(tname)) + ret.append(' goto error;') + + ret.append('}') + return ret + + # + # Main routine + # + if not member.get('xmlattr') and not member.get('xmlelem') \ + and not member.get('xmlgroup'): + return None + + if pre_name: + name = pre_name + '.' + member['name'] + else: + name = member['name'] + + tname = None + if member.get('xmlattr'): + tname = member['xmlattr'] + elif member.get('xmlelem'): + tname = member['xmlelem'] + + # For array member + if member.get('array'): + return _parse_array(name, member, tname) + + # For common member + mdvar = _middle_var(member) + if mdvar.endswith('Str') or mdvar.endswith('Node'): + tmpvars.append(mdvar) + + block = Block() + if tname: + block.append(_read_xml_func(mdvar, tname)) + if member.get('required'): + cond = render('${mdvar} == NULL', mdvar=mdvar) + block.extend(checkError('Missing', cond, tname, mdvar)) + + if mtype['meta'] == 'Struct': + assignment = _assign_struct(name, member, mdvar) + else: + assignment = _assign_non_struct(name, member, mdvar) + if not assignment: + return block + + if member.get('specified'): + if isinstance(member['specified'], str): + assignment.format('def->${name} = true;', name=member['specified']) + else: + assignment.format('def->${name}_specified = true;', name=name) + + if tname: + block.extend(if_cond(mdvar, assignment)) + else: + block.extend(assignment) + + return block + + +def makeParseFunc(writer, atype): + + # + # Helper functions + # + def _switch_parse_cb(child, switch, tmpvars): + return parseMember(child, atype, tmpvars, switch['name']) + + def _members_block(tmpvars): + block = Block() + for member in atype['members']: + if member.get('xmlswitch'): + block.extend(makeSwitch(member, _switch_parse_cb, tmpvars)) + else: + block.extend(parseMember(member, atype, tmpvars)) + block.newline() + return block + + def _post_hook(tmpvars): + if atype['genparse'] not in ['withhook', 'concisehook']: + return None + + args = ['node', 'def', 'instname', 'parent', 'opaque'] + if 'namespace' in atype: + args.append('ctxt') + args.append('xmlopt') + + if atype['genparse'] == 'withhook': + args.extend(tmpvars) + if 'nodes' in args: + args.remove('nodes') + + funcname = atype['name'] + 'ParseXMLHook' + cond = '%s(%s) < 0' % (funcname, ', '.join(args)) + return if_cond(cond, ['goto error;']) + + def _handle_tmpvars(tmpvars): + args, heads, tails = [], [], [] + for var in tmpvars: + if var == 'nodes': + heads.append('xmlNodePtr *nodes = NULL;') + tails.append('g_free(nodes);') + tails.append('nodes = NULL;') + elif var.endswith('Str'): + heads.append('g_autofree char *%s = NULL;' % var) + args.append('const char *%s' % var) + elif var.endswith('Node'): + heads.append('xmlNodePtr %s = NULL;' % var) + args.append('xmlNodePtr %s' % var) + else: + assert var.endswith('Nodes') and var.startswith('n') + heads.append('int %s = 0;' % var) + args.append('int %s' % var) + + if atype['genparse'] != 'withhook': + args = [] + + if heads: + heads.append('') + + heads.append('if (!def)') + heads.append(' goto error;') + tails.append('%sClear(def);' % atype['name']) + return args, heads, tails + + # + # Composite + # + if 'genparse' not in atype: + return + + typename = atype['name'] + funcname = typename + 'ParseXML' + + # Declare virXXXParseXML + args = Block([ + 'xmlNodePtr node', + '%sPtr def' % typename, + 'const char *instname', + 'void *parent', + 'void *opaque' + ]) + + if 'namespace' in atype: + args.append('xmlXPathContextPtr ctxt') + args.append('virNetworkXMLOptionPtr xmlopt') + + signature = funcSignature('int', funcname, args) + writer.write(atype, 'parsefunc', '.h', signature.output() + ';') + + # Prepare for implementation + tmpvars = [] + parseblock = _members_block(tmpvars) + tmpvars = dedup(tmpvars) + tmpargs, headlines, cleanup = _handle_tmpvars(tmpvars) + posthook = _post_hook(tmpvars) + + if atype['genparse'] in ['withhook', 'concisehook']: + # Declare virXXXParseXMLHook + signature = funcSignature('int', funcname + 'Hook', args + tmpargs) + writer.write(atype, 'parsefunc', '.h', signature.output() + ';') + else: + # Without hook, instname, parent and opaque are unused. + args[2] += ' G_GNUC_UNUSED' + args[3] += ' G_GNUC_UNUSED' + args[4] += ' G_GNUC_UNUSED' + + # Implement virXXXParseXML + impl = funcSignature('int', funcname, args) + impl.format('{') + impl.mapfmt(' ${_each_line_}', headlines) + impl.newline() + impl.mapfmt(' ${_each_line_}', parseblock) + + if posthook: + impl.mapfmt(' ${_each_line_}', posthook) + impl.newline() + + if 'namespace' in atype: + impl.extend(' ${_each_line_}', T_NAMESPACE_PARSE) + + impl.format(' return 0;') + impl.newline() + impl.format(' error:') + impl.mapfmt(' ${_each_line_}', cleanup) + impl.format(' return -1;') + impl.format('}') + + writer.write(atype, 'parsefunc', '.c', impl.output()) + + +def if_cond(condition, block): + assert isinstance(block, list) and len(block) > 0 + lp = ' {' if len(block) > 1 else '' + + ret = Block() + ret.format('if (${condition})${lp}', condition=condition, lp=lp) + ret.mapfmt(' ${_each_line_}', block) + ret.format('}' if lp else None) + return ret + + +def formatMember(member, parent): + mtype = TypeTable().get(member['type']) + + # + # Helper functions. + # + def _checkOnCondition(var): + ret = None + if ispointer(member): + ret = var + elif member.get('specified'): + if isinstance(member['specified'], str): + ret = 'def->' + member['specified'] + else: + ret = var + '_specified' + if ret.startswith('&'): + ret = ret[1:] + elif mtype['meta'] == 'Struct': + ret = '%sCheck(&%s, def, opaque)' % (mtype['name'], var) + elif member.get('required'): + pass + elif mtype['meta'] == 'Enum': + ret = var + elif mtype['meta'] == 'Builtin': + if mtype['name'] in ['Chars', 'UChars']: + ret = var + '[0]' + else: + ret = var + + if 'formatflag' in member: + flag = member['formatflag'] + if flag == '_ALWAYS_': + return None + + exclusive = False + reverse = False + if flag[0] == '%': + flag = flag[1:] + exclusive = True + if flag[0] == '!': + flag = flag[1:] + reverse = True + cond = '(virXMLFlag(opaque) & %s)' % flag + if reverse: + cond = '!' + cond + + if ret and not exclusive: + ret = ret + ' && ' + cond + else: + ret = cond + + return ret + + def _handleMore(code): + counter = 'def->' + counterName(member) + return loop(counter, code), counter + + def _format(layout, var): + if mtype['meta'] == 'Struct': + if not ispointer(member): + var = '&' + var + + fname = mtype['name'] + 'FormatBuf' + cond = '%s(buf, "%s", %s, def, opaque) < 0' % (fname, layout, var) + return if_cond(cond, ['return -1;']) + elif mtype['meta'] == 'Enum': + name = mtype['name'] + if not name.endswith('Type'): + name += 'Type' + + ret = Block() + ret.format('const char *str = ${name}ToString(${var});', + name=name, var=var) + ret.format('if (!str) {') + ret.format(' virReportError(VIR_ERR_INTERNAL_ERROR,') + ret.format(' _("Unknown %s type %d"),') + ret.format(' "${tname}", ${var});', + tname=member['xmlattr'], var=var) + ret.format(' return -1;') + ret.format('}') + ret.format('virBufferAsprintf(buf, "${layout}", str);', + layout=layout) + return ret + elif mtype['name'] == 'Bool': + truevalue = member.get('truevalue', 'yes') + if truevalue == 'yes': + var = '%s ? "yes" : "no"' % var + elif truevalue == 'on': + var = '%s ? "on" : "off"' % var + else: + var = '%s ? "%s" : ""' % (var, truevalue) + return ['virBufferEscapeString(buf, "%s", %s);' % (layout, var)] + elif mtype['name'] == 'String': + return ['virBufferEscapeString(buf, "%s", %s);' % (layout, var)] + elif mtype['name'] == 'Time': + return ['virTimeFormatBuf(buf, "%s", %s);' % (layout, var)] + else: + return ['virBufferAsprintf(buf, "%s", %s);' % (layout, var)] + + def _handleAttr(tagname, var): + if 'xmlattr' not in member: + return None + + fmt = '%s' + if mtype['meta'] == 'Builtin': + fmt = BUILTIN_TYPES[mtype['name']].get('fmt', '%s') + + layout = " %s='%s'" % (tagname, fmt) + return _format(layout, var) + + def _handleElem(tagname, var): + if 'xmlattr' in member: + return None + + if mtype['meta'] != 'Struct': + layout = '<%s>%%s</%s>\\n' % (tagname, tagname) + else: + layout = tagname + + code = _format(layout, var) + return code + + # + # Main routine + # + name = member['name'] + if member.get('array'): + name = name + '[i]' + var = 'def->' + name + + ret = None + if 'xmlattr' in member: + tagname = member['xmlattr'] + ret = _handleAttr(tagname, var) + else: + tagname = member['xmlelem'] + ret = _handleElem(tagname, var) + + if not ret: + return None, None + + if member.get('array'): + return _handleMore(ret) + + checks = _checkOnCondition(var) + if checks: + ret = if_cond(checks, ret) + + if checks: + if '&&' in checks or '||' in checks: + checks = '(%s)' % checks + + return ret, checks + + +def makeSwitch(switch, callback, opaque=None): + assert switch.get('xmlswitch', None) and switch.get('switch_type', None) + + captype = Terms.allcaps(switch['switch_type']) + block = Block() + block.format('switch (def->${etype}) {', etype=switch['xmlswitch']) + block.newline() + for child in switch['members']: + value = captype + '_' + Terms.allcaps(child['name']) + block.format('case ${value}:', value=value) + block.mapfmt(' ${_each_line_}', callback(child, switch, opaque)) + block.format(' break;') + block.newline() + block.format('case ${captype}_NONE:', captype=captype) + block.format('case ${captype}_LAST:', captype=captype) + block.format(' break;') + block.format('}') + return block + + +def makeFormatFunc(writer, atype): + if 'genformat' not in atype: + return + + # + # Helper functions and classes. + # + def _handleHeads(tmpvars, typename, kind=''): + tmpvars = dedup(tmpvars) + heads = Block() + if tmpvars: + heads.mapfmt( + 'g_auto(virBuffer) ${_each_line_} = VIR_BUFFER_INITIALIZER;', + tmpvars + ) + heads.newline() + + heads.format('if (!def || !buf)') + heads.format(' return 0;') + + if tmpvars: + funcname = typename + 'Format' + kind + 'Hook' + + heads.newline() + args = Block(['def', 'parent', 'opaque']) + args.mapfmt('&${_each_line_}', tmpvars) + heads.format('if (%s(%s) < 0)' % (funcname, ', '.join(args))) + heads.format(' return -1;') + + args = Block([ + 'const %s *def' % typename, + 'const void *parent', + 'const void *opaque' + ]) + args.mapfmt('virBufferPtr ${_each_line_}', tmpvars) + signature = funcSignature('int', funcname, args) + writer.write(atype, 'formatfunc', '.h', signature.output() + ';') + + args = [ + 'const %s *def' % typename, + 'const void *parent', + 'void *opaque', + 'bool value' + ] + funcname = typename + 'Check' + kind + 'Hook' + signature = funcSignature('bool', funcname, args) + writer.write(atype, 'formatfunc', '.h', signature.output() + ';') + + return '\n'.join(heads) + + def _format_group(child, switch, kind): + kind = kind if kind else '' + prefix = (switch['name'] + '.') if switch else '' + mtype = TypeTable().get(child['type']) + funcname = '%sFormat%s' % (mtype['name'], kind) + var = 'def->%s%s' % (prefix, child['name']) + if not ispointer(child): + var = '&' + var + r_check = '%sCheck%s(%s, def, opaque)' % (mtype['name'], kind, var) + cond = '%s(buf, %s, def, opaque) < 0' % (funcname, var) + return if_cond(cond, ['return -1;']), r_check + + def _switch_format_cb(child, switch, kind): + return _format_group(child, switch, kind)[0] + + def _handle_wrap_attr(member): + wrap, member['xmlattr'] = member['xmlattr'].split('/') + ret, check = formatMember(member, atype) + return ret, check, wrap + + def _switch_check_cb(child, switch, kind): + return ['ret = %s;' % _format_group(child, switch, kind)[1]] + + def _prepare_member(member, atype): + wrap, attr, attr_chk, elem, elem_chk = None, None, None, None, None + if member.get('xmlswitch'): + attr = makeSwitch(member, _switch_format_cb, 'Attr') + elem = makeSwitch(member, _switch_format_cb, 'Elem') + basename = atype['name'] + Terms.upperInitial(member['name']) + attr_chk = '%sCheckAttr(def, opaque)' % basename + elem_chk = '%sCheckElem(def, opaque)' % basename + + # Declare virXXX<UnionName>Check[Attr|Elem] for switch. + for kind in ['Attr', 'Elem']: + args = ['const %s *def' % atype['name'], 'void *opaque'] + decl = funcSignature('bool', basename + 'Check' + kind, args) + writer.write(atype, 'formatfunc', '.h', decl.output() + ';') + + # Implement virXXX<UnionName>Check[Attr|Elem] for switch. + checks = makeSwitch(member, _switch_check_cb, kind) + + args[1] += ' G_GNUC_UNUSED' + impl = funcSignature('bool', basename + 'Check' + kind, args) + impl.format('{') + impl.format(' bool ret = false;') + impl.format(' if (!def)') + impl.format(' return false;') + impl.newline() + impl.mapfmt(' ${_each_line_}', checks) + impl.newline() + impl.format(' return ret;') + impl.format('}') + writer.write(atype, 'formatfunc', '.c', impl.output()) + + elif member.get('xmlattr'): + if '/' in member['xmlattr']: + attr, attr_chk, wrap = _handle_wrap_attr(member) + else: + attr, attr_chk = formatMember(member, atype) + elif member.get('xmlelem'): + elem, elem_chk = formatMember(member, atype) + elif member.get('xmlgroup'): + attr, attr_chk = _format_group(member, None, 'Attr') + elem, elem_chk = _format_group(member, None, 'Elem') + return wrap, attr, attr_chk, elem, elem_chk + + def _prepare_hook(member): + assert member.get('xmlattr') or member.get('xmlelem') + buf = member['name'] + 'Buf' + ret = if_cond('virBufferUse(&%s)' % buf, + ['virBufferAddBuffer(buf, &%s);' % buf]) + return ret, buf + + class _WrapItem: + def __init__(self): + self.attrs = Block() + self.checks = [] + self.optional = True + self.pos = 0 + + def _prepare(): + attrs = Block() + elems = Block() + attr_checks = [] + elem_checks = [] + attr_hook_vars = [] + elem_hook_vars = [] + attrs_optional = True + elems_optional = True + wraps = OrderedDict() + for member in atype['members']: + if member.get('formathook'): + block, hookvar = _prepare_hook(member) + if member.get('xmlattr'): + attrs.extend(block) + attrs.newline(block) + attr_hook_vars.append(hookvar) + elif member.get('xmlelem'): + elems.extend(block) + elems.newline(block) + elem_hook_vars.append(hookvar) + else: + assert False, 'formathook is only with [xmlattr|xmlelem].' + else: + wrap, attr, attr_chk, elem, elem_chk = \ + _prepare_member(member, atype) + if wrap: + item = wraps.setdefault(wrap, _WrapItem()) + item.pos = len(elems) + item.attrs.extend(attr) + item.attrs.newline() + item.checks.append(attr_chk) + if member.get('required'): + item.optional = False + continue + + attrs.extend(attr) + attrs.newline(attr) + elems.extend(elem) + elems.newline(elem) + if attr_chk: + attr_checks.append(attr_chk) + if elem_chk: + elem_checks.append(elem_chk) + if member.get('required'): + attrs_optional = False + + while wraps: + wrap, item = wraps.popitem() + lines = Block() + lines.format('virBufferAddLit(buf, "<${name}");', name=wrap) + lines.newline() + lines.extend(item.attrs) + lines.format('virBufferAddLit(buf, "/>\\n");') + if item.optional: + elem_checks.extend(item.checks) + lines = if_cond(' || '.join(item.checks), lines) + lines.newline() + else: + elems_optional = False + + for line in reversed(lines): + elems.insert(item.pos, line) + + attr_checks = dedup(attr_checks) + elem_checks = dedup(elem_checks) + return (attrs, attr_checks, attrs_optional, attr_hook_vars, + elems, elem_checks, elems_optional, elem_hook_vars) + + def _check_null(optional, checks, has_hook, kind=''): + if not optional: + return None + + # Declare virXXXCheck[Attr|Elem] + typename = atype['name'] + funcname = typename + 'Check' + kind + args = [ + 'const %s *def' % typename, + 'const void *parent', + 'void *opaque' + ] + signature = funcSignature('bool', funcname, args) + writer.write(atype, 'formatfunc', '.h', signature.output() + ';') + + # Implement virXXXFormat[Attr|Elem] + check = ' || '.join(checks) if optional else 'true' + if not check: + check = 'true' + + if has_hook: + check = '%sCheck%sHook(def, parent, opaque, %s)' \ + % (typename, kind, check) + + args[1] += ' G_GNUC_UNUSED' + args[2] += ' G_GNUC_UNUSED' + + impl = funcSignature('bool', funcname, args) + impl.format('{') + impl.format(' if (!def)') + impl.format(' return false;') + impl.newline() + impl.format(' return ${check};', check=check) + impl.format('}') + writer.write(atype, 'formatfunc', '.c', impl.output()) + + return if_cond('!%s(def, parent, opaque)' % funcname, ['return 0;']) + + def _compose_full(attrs, attr_checks, attrs_optional, attr_hook_vars, + elems, elem_checks, elems_optional, elem_hook_vars): + has_hook = (attr_hook_vars or elem_hook_vars) + typename = atype['name'] + + # Declare virXXXFormatBuf + args = [ + 'virBufferPtr buf', + 'const char *name', + 'const %s *def' % typename, + 'const void *parent', + 'void *opaque' + ] + signature = funcSignature('int', typename + 'FormatBuf', args) + writer.write(atype, 'formatfunc', '.h', signature.output() + ';') + + # Implement virXXXFormatBuf + headlines = _handleHeads(attr_hook_vars + elem_hook_vars, typename) + checknull = _check_null(attrs_optional and elems_optional, + attr_checks + elem_checks, has_hook) + + args[3] += ' G_GNUC_UNUSED' + args[4] += ' G_GNUC_UNUSED' + + impl = funcSignature('int', typename + 'FormatBuf', args) + impl.format('{') + impl.mapfmt(' ${_each_line_}', headlines.split('\n')) + impl.newline() + + if checknull: + impl.mapfmt(' ${_each_line_}', checknull) + impl.newline() + + impl.format(' virBufferAsprintf(buf, "<%s", name);') + impl.newline() + + if 'namespace' in atype: + impl.mapfmt(' ${_each_line}', T_NAMESPACE_FORMAT_BEGIN) + + impl.mapfmt(' ${_each_line_}', attrs) + + if elems: + if attrs and elems_optional: + impl.format(' if (!(${checks})) {', + checks=' || '.join(elem_checks)) + impl.format(' virBufferAddLit(buf, "/>\\n");') + impl.format(' return 0;') + impl.format(' }') + impl.newline() + + if 'namespace' in atype: + impl.mapfmt(' ${_each_line}', T_NAMESPACE_FORMAT_END) + + impl.format(' virBufferAddLit(buf, ">\\n");') + impl.newline() + impl.format(' virBufferAdjustIndent(buf, 2);') + impl.newline() + impl.mapfmt(' ${_each_line_}', elems) + impl.format(' virBufferAdjustIndent(buf, -2);') + impl.format(' virBufferAsprintf(buf, "</%s>\\n", name);') + else: + impl.format(' virBufferAddLit(buf, "/>\\n");') + + impl.newline() + impl.format(' return 0;') + impl.format('}') + writer.write(atype, 'formatfunc', '.c', impl.output()) + + def _compose_part(kind, block, checks, optional, hook_vars): + typename = atype['name'] + funcname = typename + 'Format' + kind + headlines = _handleHeads(hook_vars, atype['name'], kind) + if not block: + block = ['/* empty */', ''] + + checknull = _check_null(optional, checks, len(hook_vars), kind) + + # Declare virXXXFormat[Attr|Elem] + args = [ + 'virBufferPtr buf', + 'const %s *def' % typename, + 'const void *parent', + 'void *opaque' + ] + signature = funcSignature('int', funcname, args) + writer.write(atype, 'formatfunc', '.h', signature.output() + ';') + + # Implement virXXXFormat[Attr|Elem] + args[2] += ' G_GNUC_UNUSED' + args[3] += ' G_GNUC_UNUSED' + + impl = funcSignature('int', funcname, args) + impl.format('{') + impl.mapfmt(' ${_each_line_}', headlines.split('\n')) + impl.newline() + + if checknull: + impl.mapfmt(' ${_each_line_}', checknull) + impl.newline() + + impl.mapfmt(' ${_each_line_}', block) + impl.format(' return 0;') + impl.format('}') + writer.write(atype, 'formatfunc', '.c', impl.output()) + + # + # Main routine of formating. + # + (attrs, attr_checks, attrs_optional, attr_hook_vars, + elems, elem_checks, elems_optional, elem_hook_vars) = _prepare() + + if atype['genformat'] in ['separate', 'onlyattrs', 'onlyelems']: + if atype['genformat'] in ['separate', 'onlyattrs']: + _compose_part('Attr', attrs, attr_checks, + attrs_optional, attr_hook_vars) + + if atype['genformat'] in ['separate', 'onlyelems']: + _compose_part('Elem', elems, elem_checks, + elems_optional, elem_hook_vars) + else: + _compose_full(attrs, attr_checks, attrs_optional, attr_hook_vars, + elems, elem_checks, elems_optional, elem_hook_vars) + + +def showDirective(atype): + print('\n[Directive]\n') + print(json.dumps(atype, indent=4)) diff --git a/scripts/xmlgen/go b/scripts/xmlgen/go new file mode 100755 index 0000000..3821ce0 --- /dev/null +++ b/scripts/xmlgen/go @@ -0,0 +1,7 @@ +# This is a command-line tool + +export PYTHONDONTWRITEBYTECODE=1 +WORK_DIR=$(cd $(dirname $0); pwd) +SRCDIR="${WORK_DIR}/../../src" +BUILDDIR="${WORK_DIR}/../../build/src" +${WORK_DIR}/main.py -s ${SRCDIR} -b ${BUILDDIR} $@ diff --git a/scripts/xmlgen/main.py b/scripts/xmlgen/main.py new file mode 100755 index 0000000..5054640 --- /dev/null +++ b/scripts/xmlgen/main.py @@ -0,0 +1,439 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 Shandong Massclouds Co.,Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# <http://www.gnu.org/licenses/>. +# + +import os +import re +import sys +import argparse +from clang.cindex import Index, CursorKind +from clang.cindex import SourceLocation, SourceRange, TokenKind +from datetime import datetime +from directive import TypeTable, showDirective +from directive import makeClearFunc, makeParseFunc, makeFormatFunc +from utils import Terms + +TOOL_DESC = ''' +Generate xml parse/format functions based on directives. + +Subcommand:\n + list: List types. By default, only list structs tagged by + 'genparse'/'genformat'. When the option '-a' is specified, + list all types discovered by this tool.\n + show: Show the target type's directives and its code for preview. + Specify target type by its name. The option '-k' indicates + the kinds of code for preview.\n + generate: Generate code. To be called by Makefile. + It needs option -k to filter output. + +Option:\n + -k: Specify kinds to filter code output. More than one kind can be + specified, 'c' for clearfunc; 'p' for parsefunc; + 'f' for formatfunc.\n + The option '-k' is only valid for show and generate. +''' + +# Three builtin types need to be handled specially: +# 'char *' => String +# 'char XXXX[...]' => Chars +# 'unsigned char XXXX[...]' => UChars +BUILTIN_MAP = { + 'bool': 'Bool', + 'char': 'Char', + 'unsigned char': 'UChar', + 'int': 'Int', + 'unsigned int': 'UInt', + 'long': 'Long', + 'unsigned long': 'ULong', + 'long long': 'LongLong', + 'unsigned long long': 'ULongLong', + 'uint8_t': 'U8', + 'uint32_t': 'U32', + 'time_t': 'Time', +} + + +def getBuiltinType(ctype, ptr=False, size=None): + if ctype == 'char': + if ptr: + return 'String' + elif size: + return 'Chars' + + if ctype == 'unsigned char' and size: + return 'UChars' + + return BUILTIN_MAP.get(ctype, None) + + +def cursorLineExtent(cursor, tu): + loc = cursor.location + start = SourceLocation.from_position(tu, loc.file, loc.line, 1) + end = SourceLocation.from_position(tu, loc.file, loc.line, -1) + return SourceRange.from_locations(start, end) + + +def getTokens(cursor, tu): + return tu.get_tokens(extent=cursorLineExtent(cursor, tu)) + + +DIRECTIVES = [ + 'genparse', 'genformat', 'namespace', 'xmlattr', 'xmlelem', 'xmlgroup', + 'required', 'array', 'specified', 'truevalue', 'formathook', 'xmlswitch', + 'formatflag' +] + + +def createDirectives(text): + tlist = re.findall(r'/\*(.*)\*/', text) + if len(tlist) != 1: + return None + + tlist = tlist[0].split(',') + if len(tlist) == 0: + return None + + directives = {} + for item in tlist: + item = item.strip() + if ':' in item: + key, value = item.split(':') + else: + key, value = item, None + + if key in DIRECTIVES: + directives[key] = value + return directives + + +def getDirectives(tokens, cursor): + for token in tokens: + if token.location.column <= cursor.location.column: + continue + if token.kind == TokenKind.COMMENT: + directive = createDirectives(token.spelling) + if directive: + return directive + return None + + +def determinType(kvs, tokens, cursor): + prefix = [] + kind = None + for token in tokens: + if token.location.column >= cursor.location.column: + break + if not kind: + kind = token.kind + prefix.append(token.spelling) + + suffix = [] + for token in tokens: + if token.spelling == ';': + break + suffix.append(token.spelling) + + size = None + if len(suffix) == 3 and suffix[0] == '[' and suffix[2] == ']': + size = suffix[1] + + assert kind in [TokenKind.IDENTIFIER, TokenKind.KEYWORD], \ + 'Bad field "%s" (%s).' % (cursor.spelling, kind) + + assert prefix + typename = ' '.join(prefix) + + # For array, remove the most-outer pointer + if kvs.get('array'): + if typename.endswith('Ptr'): + typename = typename[:-3] + elif typename.endswith('*'): + typename = typename[:-1].strip() + + ptr = False + if typename.endswith('Ptr'): + typename = typename[:-3] + ptr = True + elif prefix[-1] == '*': + typename = typename[:-1].strip() + ptr = True + + ret = getBuiltinType(typename, ptr, size) + if ret: + typename = ret + + kvs.update({'type': typename, 'pointer': ptr}) + if size: + kvs['size'] = size + return kvs + + +def analyseMember(cursor, tu): + dvs = getDirectives(getTokens(cursor, tu), cursor) + if not dvs: + return None + + kvs = {'name': cursor.spelling} + kvs.update(dvs) + + # Formalize member + for key in ['array', 'required', 'specified', 'formathook']: + if key in kvs and not kvs[key]: + kvs[key] = True + + if 'formatflag' in kvs: + assert kvs.get('formatflag'), 'Directive "formatflag" is None' + + for tag in ['xmlattr', 'xmlelem', 'xmlgroup']: + if tag in kvs: + if not kvs[tag]: + if kvs.get('array'): + kvs[tag] = Terms.singularize(kvs['name']) + else: + kvs[tag] = kvs['name'] + + return determinType(kvs, getTokens(cursor, tu), cursor) + + +def findMember(struct, name): + for member in struct['members']: + if member['name'] == name: + return member + + return None + + +def analyseStruct(struct, cursor, tu): + tokens = getTokens(cursor, tu) + kvs = getDirectives(tokens, cursor) + if kvs: + path, _ = os.path.splitext(cursor.location.file.name) + path, filename = os.path.split(path) + _, dirname = os.path.split(path) + kvs['output'] = dirname + '/' + filename + struct.update(kvs) + + last_kind = None + inner_members = [] + for child in cursor.get_children(): + if inner_members: + if last_kind == CursorKind.STRUCT_DECL: + # Flatten the members of embedded struct + for member in inner_members: + member['name'] = child.spelling + '.' + member['name'] + struct['members'].append(member) + else: + # Embedded union + union = getDirectives(getTokens(child, tu), child) + if union and 'xmlswitch' in union: + switch = findMember(struct, union['xmlswitch']) + switch['delay_clear'] = True + union['switch_type'] = switch['type'] + union['name'] = child.spelling + union['members'] = inner_members + struct['members'].append(union) + struct['xmlswitch'] = union['xmlswitch'] + + inner_members = [] + last_kind = None + continue + + if child.kind in [CursorKind.STRUCT_DECL, CursorKind.UNION_DECL]: + last_kind = child.kind + for ichild in child.get_children(): + member = analyseMember(ichild, tu) + if member: + inner_members.append(member) + continue + + member = analyseMember(child, tu) + if member: + struct['members'].append(member) + + return struct + + +def discoverStructures(tu): + for cursor in tu.cursor.get_children(): + if cursor.kind == CursorKind.STRUCT_DECL and cursor.is_definition(): + # Detect structs + name = cursor.spelling + if not name: + continue + if name.startswith('_'): + name = name[1:] + if TypeTable().check(name): + continue + struct = {'name': name, 'meta': 'Struct', 'members': []} + analyseStruct(struct, cursor, tu) + TypeTable().register(struct) + elif cursor.kind == CursorKind.TYPEDEF_DECL: + # Detect enums + # We can't seek out enums by CursorKind.ENUM_DECL, + # since almost all enums are anonymous. + token = cursor.get_tokens() + try: + next(token) # skip 'typedef' + if next(token).spelling == 'enum': + if not TypeTable().check(cursor.spelling): + enum = {'name': cursor.spelling, 'meta': 'Enum'} + TypeTable().register(enum) + except StopIteration: + pass + + +class CodeWriter(object): + def __init__(self, args): + self._builddir = args.builddir + self._cmd = args.cmd + self._files = {} + self._filters = {} + self._filters['clearfunc'] = args.kinds and 'c' in args.kinds + self._filters['parsefunc'] = args.kinds and 'p' in args.kinds + self._filters['formatfunc'] = args.kinds and 'f' in args.kinds + if args.cmd == 'show': + self._filters['target'] = args.target + + def _getFile(self, path, ext): + assert ext in ['.h', '.c'] + _, basename = os.path.split(path) + path = '%s.generated%s' % (path, ext) + f = self._files.get(path) + if f is None: + f = open(path, 'w') + f.write('/* Generated by scripts/xmlgen */\n\n') + if ext in ['.c']: + f.write('#include <config.h>\n') + f.write('#include "%s.h"\n' % basename) + f.write('#include "viralloc.h"\n') + f.write('#include "virerror.h"\n') + f.write('#include "virstring.h"\n\n') + f.write('#define VIR_FROM_THIS VIR_FROM_NONE\n') + else: + f.write('#pragma once\n\n') + f.write('#include "internal.h"\n') + f.write('#include "virxml.h"\n') + self._files[path] = f + return f + + def write(self, atype, kind, extname, content): + if not self._filters[kind]: + return + + if self._cmd == 'show': + target = self._filters['target'] + if not target or target == atype['name']: + if extname == '.h': + print('\n[.h]') + else: + print('\n[.c]') + print('\n' + content) + return + + assert self._cmd == 'generate' + + if atype.get('output'): + lfs = '\n' if extname == '.h' else '\n\n' + path = self._builddir + '/' + atype['output'] + f = self._getFile(path, extname) + f.write(lfs + content + '\n') + + def complete(self): + for name in self._files: + self._files[name].close() + self._files.clear() + + +def getHFiles(path): + retlist = [] + for fname in os.listdir(path): + if fname.endswith('.h'): + retlist.append(os.path.join(path, fname)) + return retlist + + +HELP_LIST = 'list structs tagged by "genparse"/"genformat"' +HELP_LIST_ALL = 'list all discovered types' + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=TOOL_DESC) + parser.add_argument('-s', dest='srcdir') + parser.add_argument('-b', dest='builddir') + subparsers = parser.add_subparsers(dest='cmd') + parser_list = subparsers.add_parser('list', help=HELP_LIST) + parser_list.add_argument('-a', dest='list_all', action='store_true', + default=False, help=HELP_LIST_ALL) + parser_show = subparsers.add_parser('show', help='show target code') + parser_show.add_argument('target', help='target for being previewed') + parser_show.add_argument('-k', dest='kinds', + help='kinds of code to be previewed') + parser_generate = subparsers.add_parser('generate', help='generate code') + parser_generate.add_argument('-k', dest='kinds', + help='kinds of code to be generated') + args = parser.parse_args() + + if not args.cmd or not args.srcdir or not args.builddir: + parser.print_help() + sys.exit(1) + + if args.cmd == 'generate': + print('###### xmlgen: start ... ######') + if not args.kinds: + print("[dry run]: no kinds specified for 'generate'") + + timestamp = datetime.now() + + # Examine all *.h in "$(srcdir)/[util|conf]" + index = Index.create() + srcdir = args.srcdir + hfiles = getHFiles(srcdir + '/util') + getHFiles(srcdir + '/conf') + for hfile in hfiles: + tu = index.parse(hfile) + discoverStructures(tu) # find all structs and enums + + if args.cmd == 'list': + print('%-64s %s' % ('TYPENAME', 'META')) + for name, kvs in TypeTable().items(): + if not args.list_all: + if not ('genparse' in kvs or 'genparse' in kvs): + continue + print('%-64s %s' % (name, kvs['meta'])) + sys.exit(0) + elif args.cmd == 'show': + assert args.target, args + atype = TypeTable().get(args.target) + if not atype: + sys.exit(0) + showDirective(atype) + + writer = CodeWriter(args) + + for atype in TypeTable().values(): + makeClearFunc(writer, atype) + makeParseFunc(writer, atype) + makeFormatFunc(writer, atype) + + writer.complete() + + if args.cmd == 'generate': + elapse = (datetime.now() - timestamp).microseconds + print('\n###### xmlgen: elapse %d(us) ######\n' % elapse) + + sys.exit(0) diff --git a/scripts/xmlgen/utils.py b/scripts/xmlgen/utils.py new file mode 100644 index 0000000..4ac1f39 --- /dev/null +++ b/scripts/xmlgen/utils.py @@ -0,0 +1,121 @@ +# +# Copyright (C) 2020 Shandong Massclouds Co.,Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# <http://www.gnu.org/licenses/>. +# + +from string import Template + + +def singleton(cls): + _instances = {} + + def inner(): + if cls not in _instances: + _instances[cls] = cls() + return _instances[cls] + return inner + + +class Terms(object): + abbrs = ['uuid', 'pci', 'zpci', 'ptr', 'mac', 'mtu', 'dns', 'ip', 'dhcp'] + plurals = {'addresses': 'address'} + caps = {'NET_DEV': 'NETDEV', 'MACTABLE': 'MAC_TABLE'} + + @classmethod + def _split(cls, word): + ret = [] + if not word: + return ret + head = 0 + for pos in range(1, len(word)): + if word[pos].isupper() and not word[pos - 1].isupper(): + ret.append(word[head:pos]) + head = pos + ret.append(word[head:]) + return ret + + @classmethod + def singularize(cls, name): + ret = cls.plurals.get(name, None) + if ret: + return ret + assert name.endswith('s') + return name[:-1] + + # Don't use str.capitalize() which force other letters to be lowercase. + @classmethod + def upperInitial(cls, word): + if not word: + return '' + if word in cls.abbrs: + return word.upper() + if len(word) > 0 and word[0].isupper(): + return word + return word[0].upper() + word[1:] + + @classmethod + def allcaps(cls, word): + if len(word) == 0: + return word + parts = cls._split(word) + ret = '_'.join([part.upper() for part in parts]) + for key, value in cls.caps.items(): + ret = ret.replace('_%s_' % key, '_%s_' % value) + return ret + + +def render(template, **kwargs): + return Template(template).substitute(kwargs) + + +class Block(list): + def format(self, template, **args): + if template: + self.append(Template(template).substitute(**args)) + + def extend(self, block): + if isinstance(block, list): + super(Block, self).extend(block) + + # ${_each_line_} is the only legal key for template + # and represents each line of the block. + def mapfmt(self, template, block): + if not block or not template: + return + + assert isinstance(block, list), block + for line in block: + if line: + self.append(Template(template).substitute(_each_line_=line)) + else: + self.append('') + + def newline(self, condition=True): + if condition: + super(Block, self).append('') + + def output(self, connector='\n'): + return connector.join(self) + + +def dedup(alist): + assert isinstance(alist, list) + ret = [] + for e in alist: + if e not in ret: + ret.append(e) + + return ret -- 2.25.1

On Fri, Sep 04, 2020 at 11:34:53AM +0800, Shi Lei wrote:
This tool is used to generate parsexml/formatbuf functions. It is based on libclang and its python-binding. Some directives (such as genparse, xmlattr, etc.) need to be added on the declarations of structs to direct the tool.
In testing this I hit one problem with use of libclang python binding. In RHEL-7, we have clang 3.4.1 via EPEL repos, and that only has a python 2 binding available, no python 3 binding. IOW, if we take te xml generator, we loose support for building on RHEL-7 as we consume it today. I see a few options 1 Wait until April 2021 to merge this series, because our platform matrix lets us drop RHEL-7 support in April. https://libvirt.org/platforms.html 2 Enable use of software collections in our RHEL-7 CI container images and pull clang 7 from there https://www.softwarecollections.org/en/scls/rhscl/llvm-toolset-7.0/ 3 Merge this feature now, commit the generated files to git and only run the generator manually when we need changes. Once we drop RHEL7 we can delete the generated files from git. 4 Drop RHEL-7 immediately in violation of our platform support matrix. I know of people, however, who still rely on being able to build new libvirt on RHEL-7 this would harm. My feeling is to go for option (3) and temporarily commit the generated files to git, as it will only need to be a short term hack for 4-5 months. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 2020-12-05 at 01:22, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:34:53AM +0800, Shi Lei wrote:
This tool is used to generate parsexml/formatbuf functions. It is based on libclang and its python-binding. Some directives (such as genparse, xmlattr, etc.) need to be added on the declarations of structs to direct the tool.
In testing this I hit one problem with use of libclang python binding.
In RHEL-7, we have clang 3.4.1 via EPEL repos, and that only has a python 2 binding available, no python 3 binding.
IOW, if we take te xml generator, we loose support for building on RHEL-7 as we consume it today.
I see a few options
1 Wait until April 2021 to merge this series, because our platform matrix lets us drop RHEL-7 support in April.
https://libvirt.org/platforms.html
2 Enable use of software collections in our RHEL-7 CI container images and pull clang 7 from there
https://www.softwarecollections.org/en/scls/rhscl/llvm-toolset-7.0/
3 Merge this feature now, commit the generated files to git and only run the generator manually when we need changes. Once we drop RHEL7 we can delete the generated files from git.
4 Drop RHEL-7 immediately in violation of our platform support matrix. I know of people, however, who still rely on being able to build new libvirt on RHEL-7 this would harm.
My feeling is to go for option (3) and temporarily commit the generated files to git, as it will only need to be a short term hack for 4-5 months.
I would like to select option(1), since it's just a short waiting time. During this time, we can refine the document, testcase and the generator itself. Regards, Shi Lei
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Make sure python3-clang has been installed. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/meson.build b/meson.build index 1eadea3..f97a03b 100644 --- a/meson.build +++ b/meson.build @@ -2504,3 +2504,8 @@ if conf.has('WITH_QEMU') } summary(priv_summary, section: 'Privileges') endif + +py3_clang = run_command('python3', '-c', 'import clang.cindex;print("ok")') +if py3_clang.returncode() != 0 + error('python3-clang is required.') +endif -- 2.25.1

On Fri, Sep 04, 2020 at 11:34:54AM +0800, Shi Lei wrote:
Make sure python3-clang has been installed.
Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- meson.build | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/meson.build b/meson.build index 1eadea3..f97a03b 100644 --- a/meson.build +++ b/meson.build @@ -2504,3 +2504,8 @@ if conf.has('WITH_QEMU') } summary(priv_summary, section: 'Privileges') endif + +py3_clang = run_command('python3', '-c', 'import clang.cindex;print("ok")') +if py3_clang.returncode() != 0 + error('python3-clang is required.') +endif
This probably needs to be a bit of a stronger check, as I found it is possible "pip install clang", without having libclang.so actually installed, and this "import clang.cindex" will still succeeed. I think we need to add something like py3_clang_working = run_command('python3', '-c', 'import clang.cindex;cindex.Index()') if py3_clang_working.returncode() != 0 error('python3-clang is present, but not working. Perhaps libclang is missing?') endif Also, we need to add python3-clang to the libvirt.spec.in and the mingw-libvirt.spec.in, and also the containers in ci/containers/ Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 2020-12-04 at 21:01, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:34:54AM +0800, Shi Lei wrote:
Make sure python3-clang has been installed.
Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- meson.build | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/meson.build b/meson.build index 1eadea3..f97a03b 100644 --- a/meson.build +++ b/meson.build @@ -2504,3 +2504,8 @@ if conf.has('WITH_QEMU') } summary(priv_summary, section: 'Privileges') endif + +py3_clang = run_command('python3', '-c', 'import clang.cindex;print("ok")') +if py3_clang.returncode() != 0 + error('python3-clang is required.') +endif
This probably needs to be a bit of a stronger check, as I found it is possible "pip install clang", without having libclang.so actually installed, and this "import clang.cindex" will still succeeed.
I think we need to add something like
py3_clang_working = run_command('python3', '-c', 'import clang.cindex;cindex.Index()') if py3_clang_working.returncode() != 0 error('python3-clang is present, but not working. Perhaps libclang is missing?') endif
Okay. We can use 'clang.cindex.Config().get_cindex_library' to check libclang.so. And also, the version of the libclang needs to be printed in error message when it is missing.
Also, we need to add python3-clang to the libvirt.spec.in and the mingw-libvirt.spec.in, and also the containers in ci/containers/
Okay. Regards, Shi Lei
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- scripts/meson.build | 8 ++++++++ src/conf/meson.build | 39 +++++++++++++++++++++++++++++++++++++++ src/meson.build | 6 ++++++ tests/meson.build | 1 + tools/meson.build | 2 ++ 5 files changed, 56 insertions(+) diff --git a/scripts/meson.build b/scripts/meson.build index 59b3c9b..dad82a9 100644 --- a/scripts/meson.build +++ b/scripts/meson.build @@ -37,3 +37,11 @@ foreach name : scripts sname = name.split('.')[0].underscorify() set_variable('@0@_prog'.format(sname), find_program(name)) endforeach + +xmlgen_self = files( + 'xmlgen/main.py', + 'xmlgen/directive.py', + 'xmlgen/utils.py' +) + +set_variable('virxmlgen_prog', find_program('xmlgen/main.py')) diff --git a/src/conf/meson.build b/src/conf/meson.build index 03b90aa..d82bffc 100644 --- a/src/conf/meson.build +++ b/src/conf/meson.build @@ -1,3 +1,39 @@ +xmlgen_input = [ +] + +xmlgen_output = [] +foreach name : xmlgen_input + xmlgen_output += '@0@.generated.c'.format(name.split('.')[0]) + xmlgen_output += '@0@.generated.h'.format(name.split('.')[0]) +endforeach + +xmlgen_headers = [] +if xmlgen_output.length() > 0 + xmlgen_objects = custom_target( + 'virxmlgen', + input: xmlgen_self + xmlgen_input, + output: xmlgen_output, + command: [ + meson_python_prog, python3_prog.path(), '-B', virxmlgen_prog.path(), + '-s', meson.source_root() / 'src', + '-b', meson.build_root() / 'src', + 'generate', '-k', 'cpf', + ], + ) + + index = 0 + foreach header : xmlgen_objects.to_list() + if index % 2 == 1 + xmlgen_headers += header + endif + index += 1 + endforeach +else + xmlgen_objects = [] +endif + +xmlgen_dep = declare_dependency(sources: xmlgen_headers) + netdev_conf_sources = [ 'netdev_bandwidth_conf.c', 'netdev_vlan_conf.c', @@ -87,6 +123,7 @@ device_conf_sources = [ virt_conf_lib = static_library( 'virt_conf', [ + xmlgen_objects, chrdev_conf_sources, cpu_conf_sources, device_conf_sources, @@ -108,3 +145,5 @@ virt_conf_lib = static_library( libvirt_libs += virt_conf_lib conf_inc_dir = include_directories('.') + +subdir_done() diff --git a/src/meson.build b/src/meson.build index 897b5ec..2362897 100644 --- a/src/meson.build +++ b/src/meson.build @@ -247,6 +247,12 @@ src_dep = declare_dependency( ) subdir('conf') + +src_dep = declare_dependency( + dependencies: [ src_dep, xmlgen_dep ], + include_directories: [ conf_inc_dir ], +) + subdir('rpc') subdir('access') subdir('cpu') diff --git a/tests/meson.build b/tests/meson.build index ad13e2d..c23910b 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -17,6 +17,7 @@ tests_dep = declare_dependency( selinux_dep, xdr_dep, yajl_dep, + xmlgen_dep, ], include_directories: [ conf_inc_dir, diff --git a/tools/meson.build b/tools/meson.build index b8c6802..788eed0 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -5,11 +5,13 @@ tools_dep = declare_dependency( dependencies: [ libxml_dep, glib_dep, + xmlgen_dep, ], include_directories: [ libvirt_inc, src_inc_dir, util_inc_dir, + conf_inc_dir, top_inc_dir, ], link_args: ( -- 2.25.1

1) virXMLChildNode and virXMLChildNodeSet Parse xml without using xmlXPathContext. 2) virXMLChildPropString Support to parse attribute by path. 3) virXMLFlag Convert opaque to flag. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/libvirt_private.syms | 4 ++ src/util/virxml.c | 105 +++++++++++++++++++++++++++++++++++++++ src/util/virxml.h | 6 +++ 3 files changed, 115 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 5736a2d..191eab0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3505,7 +3505,11 @@ virVsockSetGuestCid; virParseScaledValue; virXMLCheckIllegalChars; virXMLChildElementCount; +virXMLChildNode; +virXMLChildNodeSet; +virXMLChildPropString; virXMLExtractNamespaceXML; +virXMLFlag; virXMLFormatElement; virXMLNodeContentString; virXMLNodeNameEqual; diff --git a/src/util/virxml.c b/src/util/virxml.c index 5315d4f..a7d9a2c 100644 --- a/src/util/virxml.c +++ b/src/util/virxml.c @@ -1481,3 +1481,108 @@ virParseScaledValue(const char *xpath, *val = bytes; return 1; } + + +/** + * virXMLChildNode: + * @node: Parent XML dom node pointer + * @name: Name of the child element + * + * Convenience function to return the child element of a XML node. + * + * Returns the pointer of child element node or NULL in case of failure. + * If there are many nodes match condition, it only returns the first node. + */ +xmlNodePtr +virXMLChildNode(xmlNodePtr node, const char *name) +{ + xmlNodePtr cur = node->children; + while (cur) { + if (cur->type == XML_ELEMENT_NODE && virXMLNodeNameEqual(cur, name)) + return cur; + cur = cur->next; + } + return NULL; +} + + +/** + * virXMLChildPropString: + * @node: Parent XML dom node pointer + * @path: Path of the property (attribute) to get + * + * Convenience function to return copy of an attribute value by a path. + * Path consists of names of child elements and an attribute. + * The delimiter is '/'. + * + * E.g.: For <self><son><grandson name="..."></grandson></son></self>, + * the path is 'son/grandSon/name' to get attribute 'name'. + * + * Returns the property (attribute) value as string or NULL in case of failure. + * The caller is responsible for freeing the returned buffer. + */ +char * +virXMLChildPropString(xmlNodePtr node, const char *path) +{ + char *next; + char *sep; + xmlNodePtr tnode = node; + g_autofree char *tmp = NULL; + tmp = g_strdup(path); + + next = tmp; + while ((sep = strchr(next, '/'))) { + *sep = '\0'; + + if ((tnode = virXMLChildNode(tnode, next)) == NULL) + return NULL; + + next = sep + 1; + } + + return virXMLPropString(tnode, next); +} + + +/** + * virXMLChildNodeSet: + * @node: Parent XML dom node pointer + * @name: Name of the children element + * @list: the returned list of nodes (or NULL if only count matters) + * + * Convenience function to evaluate a set of children elements + * + * Returns the number of nodes found in which case @list is set (and + * must be freed) or -1 if the evaluation failed. + */ +int +virXMLChildNodeSet(xmlNodePtr node, const char *name, xmlNodePtr **list) +{ + size_t count = 0; + xmlNodePtr cur = node->children; + + if (list != NULL) + *list = NULL; + + while (cur) { + if (cur->type == XML_ELEMENT_NODE && virXMLNodeNameEqual(cur, name)) { + if (list != NULL) { + if (VIR_APPEND_ELEMENT_COPY(*list, count, cur) < 0) + return -1; + } else { + count++; + } + } + cur = cur->next; + } + return count; +} + + +unsigned int +virXMLFlag(void *opaque) +{ + if (opaque) + return *((unsigned int *) opaque); + return 0; +} diff --git a/src/util/virxml.h b/src/util/virxml.h index 0301f15..85e8eed 100644 --- a/src/util/virxml.h +++ b/src/util/virxml.h @@ -79,6 +79,10 @@ char * virXMLPropStringLimit(xmlNodePtr node, char * virXMLNodeContentString(xmlNodePtr node); long virXMLChildElementCount(xmlNodePtr node); +xmlNodePtr virXMLChildNode(xmlNodePtr node, const char *name); +int virXMLChildNodeSet(xmlNodePtr node, const char *name, xmlNodePtr **list); +char * virXMLChildPropString(xmlNodePtr node, const char *path); + /* Internal function; prefer the macros below. */ xmlDocPtr virXMLParseHelper(int domcode, const char *filename, @@ -279,3 +283,5 @@ int virParseScaledValue(const char *xpath, unsigned long long scale, unsigned long long max, bool required); + +unsigned int virXMLFlag(void *opaque); -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/libvirt_private.syms | 3 +++ src/util/virstring.c | 57 ++++++++++++++++++++++++++++++++++++++++ src/util/virstring.h | 9 +++++++ 3 files changed, 69 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 191eab0..4ad9d1e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3242,6 +3242,7 @@ virStringStripSuffix; virStringToUpper; virStringTrimOptionalNewline; virStrncpy; +virStrToBool; virStrToDouble; virStrToLong_i; virStrToLong_l; @@ -3252,6 +3253,8 @@ virStrToLong_ul; virStrToLong_ull; virStrToLong_ullp; virStrToLong_ulp; +virStrToTime; +virTimeFormatBuf; virTrimSpaces; diff --git a/src/util/virstring.c b/src/util/virstring.c index de2ef96..23ade40 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -1349,3 +1349,60 @@ int virStringParseYesNo(const char *str, bool *result) return 0; } + + +int +virStrToBool(const char *str, const char *truevalue, bool *result) +{ + if (STREQ(str, truevalue)) + *result = true; + else + *result = false; + + return 0; +} + + +int virStrToTime(const char *str, time_t *result) +{ + g_autoptr(GDateTime) then = NULL; + g_autoptr(GTimeZone) tz = g_time_zone_new_utc(); + char *tmp; + int year, mon, mday, hour, min, sec; + + /* Expect: YYYY-MM-DDTHH:MM:SS (%d-%d-%dT%d:%d:%d) eg 2010-11-28T14:29:01 */ + if (/* year */ + virStrToLong_i(str, &tmp, 10, &year) < 0 || *tmp != '-' || + /* month */ + virStrToLong_i(tmp+1, &tmp, 10, &mon) < 0 || *tmp != '-' || + /* day */ + virStrToLong_i(tmp+1, &tmp, 10, &mday) < 0 || *tmp != 'T' || + /* hour */ + virStrToLong_i(tmp+1, &tmp, 10, &hour) < 0 || *tmp != ':' || + /* minute */ + virStrToLong_i(tmp+1, &tmp, 10, &min) < 0 || *tmp != ':' || + /* second */ + virStrToLong_i(tmp+1, &tmp, 10, &sec) < 0 || *tmp != '\0') { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid time '%s', expect YYYY-MM-DDTHH:MM:SS"), + str); + return -1; + } + + then = g_date_time_new(tz, year, mon, mday, hour, min, sec); + *result = (time_t)g_date_time_to_unix(then); + return 0; +} + + +int +virTimeFormatBuf(virBufferPtr buf, const char *layout, const time_t time) +{ + g_autoptr(GDateTime) then = NULL; + g_autofree char *thenstr = NULL; + + then = g_date_time_new_from_unix_utc(time); + thenstr = g_date_time_format(then, "%Y-%m-%dT%H:%M:%S"); + virBufferAsprintf(buf, layout, thenstr); + return 0; +} diff --git a/src/util/virstring.h b/src/util/virstring.h index 561ce0c..658b575 100644 --- a/src/util/virstring.h +++ b/src/util/virstring.h @@ -19,6 +19,7 @@ #pragma once #include "internal.h" +#include "virbuffer.h" #define VIR_INT64_STR_BUFLEN 21 @@ -179,6 +180,14 @@ int virStringParsePort(const char *str, int virStringParseYesNo(const char *str, bool *result) G_GNUC_WARN_UNUSED_RESULT; + +int virStrToBool(const char *str, const char *truevalue, bool *result); + +int virStrToTime(const char *str, time_t *result) + G_GNUC_WARN_UNUSED_RESULT; + +int virTimeFormatBuf(virBufferPtr buf, const char *layout, const time_t time); + /** * VIR_AUTOSTRINGLIST: * -- 2.25.1

On Fri, Sep 04, 2020 at 11:34:57AM +0800, Shi Lei wrote:
Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/libvirt_private.syms | 3 +++ src/util/virstring.c | 57 ++++++++++++++++++++++++++++++++++++++++ src/util/virstring.h | 9 +++++++ 3 files changed, 69 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 191eab0..4ad9d1e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3242,6 +3242,7 @@ virStringStripSuffix; virStringToUpper; virStringTrimOptionalNewline; virStrncpy; +virStrToBool; virStrToDouble; virStrToLong_i; virStrToLong_l; @@ -3252,6 +3253,8 @@ virStrToLong_ul; virStrToLong_ull; virStrToLong_ullp; virStrToLong_ulp; +virStrToTime; +virTimeFormatBuf; virTrimSpaces;
diff --git a/src/util/virstring.c b/src/util/virstring.c index de2ef96..23ade40 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -1349,3 +1349,60 @@ int virStringParseYesNo(const char *str, bool *result)
return 0; } + + +int +virStrToBool(const char *str, const char *truevalue, bool *result) +{ + if (STREQ(str, truevalue)) + *result = true; + else + *result = false; + + return 0; +}
I'd suggest we should explicitly check both the true and false values, and raise an error for any other value. I think we probably ought to have wrappers for the pairs of words we consider sane. eg virStrToBoolYesNo, virStrToBoolTrueFalse and virStrToBoolOnOff I think are probably the only ones we want to permit
+ +
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 2020-12-05 at 02:02, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:34:57AM +0800, Shi Lei wrote:
Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/libvirt_private.syms | 3 +++ src/util/virstring.c | 57 ++++++++++++++++++++++++++++++++++++++++ src/util/virstring.h | 9 +++++++ 3 files changed, 69 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 191eab0..4ad9d1e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3242,6 +3242,7 @@ virStringStripSuffix; virStringToUpper; virStringTrimOptionalNewline; virStrncpy; +virStrToBool; virStrToDouble; virStrToLong_i; virStrToLong_l; @@ -3252,6 +3253,8 @@ virStrToLong_ul; virStrToLong_ull; virStrToLong_ullp; virStrToLong_ulp; +virStrToTime; +virTimeFormatBuf; virTrimSpaces; diff --git a/src/util/virstring.c b/src/util/virstring.c index de2ef96..23ade40 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -1349,3 +1349,60 @@ int virStringParseYesNo(const char *str, bool *result) return 0; } + + +int +virStrToBool(const char *str, const char *truevalue, bool *result) +{ + if (STREQ(str, truevalue)) + *result = true; + else + *result = false; + + return 0; +}
I'd suggest we should explicitly check both the true and false values, and raise an error for any other value.
I think we probably ought to have wrappers for the pairs of words we consider sane. eg virStrToBoolYesNo, virStrToBoolTrueFalse and virStrToBoolOnOff I think are probably the only ones we want to permit
Okay. Shi Lei
+ +
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Implement the parsexml/formatbuf functions for virSocketAddr. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/libvirt_private.syms | 2 ++ src/util/virsocketaddr.c | 42 ++++++++++++++++++++++++++++++++++++++++ src/util/virsocketaddr.h | 26 +++++++++++++++++++++---- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4ad9d1e..6b5c664 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3074,6 +3074,7 @@ virSocketAddrBroadcastByPrefix; virSocketAddrCheckNetmask; virSocketAddrEqual; virSocketAddrFormat; +virSocketAddrFormatBuf; virSocketAddrFormatFull; virSocketAddrGetIPPrefix; virSocketAddrGetNumNetmaskBits; @@ -3091,6 +3092,7 @@ virSocketAddrParse; virSocketAddrParseAny; virSocketAddrParseIPv4; virSocketAddrParseIPv6; +virSocketAddrParseXML; virSocketAddrPrefixToNetmask; virSocketAddrPTRDomain; virSocketAddrResolveService; diff --git a/src/util/virsocketaddr.c b/src/util/virsocketaddr.c index e0eb76d..bbb3a81 100644 --- a/src/util/virsocketaddr.c +++ b/src/util/virsocketaddr.c @@ -157,6 +157,15 @@ int virSocketAddrParse(virSocketAddrPtr addr, const char *val, int family) return len; } +int virSocketAddrParseXML(const char *val, + virSocketAddrPtr addr, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + return virSocketAddrParse(addr, val, AF_UNSPEC); +} + /** * virSocketAddrParseAny: * @addr: where to store the return value, optional. @@ -1307,3 +1316,36 @@ virSocketAddrFree(virSocketAddrPtr addr) { VIR_FREE(addr); } + +void +virSocketAddrClear(virSocketAddrPtr addr) +{ + memset(addr, 0, sizeof(virSocketAddr)); +} + +int +virSocketAddrFormatBuf(virBufferPtr buf, + const char *fmt, + const virSocketAddr *addr, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + g_autofree char *str = NULL; + if (!VIR_SOCKET_ADDR_VALID(addr)) + return 0; + + str = virSocketAddrFormatFull(addr, false, NULL); + if (!str) + return -1; + + virBufferAsprintf(buf, fmt, str); + return 0; +} + +bool +virSocketAddrCheck(const virSocketAddr *addr, + const void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + return VIR_SOCKET_ADDR_VALID(addr); +} diff --git a/src/util/virsocketaddr.h b/src/util/virsocketaddr.h index 6c08f8b..fa51d5c 100644 --- a/src/util/virsocketaddr.h +++ b/src/util/virsocketaddr.h @@ -18,11 +18,14 @@ #pragma once +#include "virbuffer.h" #include "virsocket.h" #define VIR_LOOPBACK_IPV4_ADDR "127.0.0.1" -typedef struct { +typedef struct _virSocketAddr virSocketAddr; +typedef virSocketAddr *virSocketAddrPtr; +struct _virSocketAddr { union { struct sockaddr sa; struct sockaddr_storage stor; @@ -33,7 +36,7 @@ typedef struct { #endif } data; socklen_t len; -} virSocketAddr; +}; #define VIR_SOCKET_ADDR_VALID(s) \ ((s)->data.sa.sa_family != AF_UNSPEC) @@ -50,8 +53,6 @@ typedef struct { #define VIR_SOCKET_ADDR_IPV4_ARPA "in-addr.arpa" #define VIR_SOCKET_ADDR_IPV6_ARPA "ip6.arpa" -typedef virSocketAddr *virSocketAddrPtr; - typedef struct _virSocketAddrRange virSocketAddrRange; typedef virSocketAddrRange *virSocketAddrRangePtr; struct _virSocketAddrRange { @@ -70,6 +71,12 @@ int virSocketAddrParse(virSocketAddrPtr addr, const char *val, int family); +int virSocketAddrParseXML(const char *val, + virSocketAddrPtr addr, + const char *instname, + void *parent, + void *opaque); + int virSocketAddrParseAny(virSocketAddrPtr addr, const char *val, int family, @@ -93,6 +100,12 @@ char *virSocketAddrFormatFull(const virSocketAddr *addr, bool withService, const char *separator); +int virSocketAddrFormatBuf(virBufferPtr buf, + const char *fmt, + const virSocketAddr *addr, + const void *parent, + void *opaque); + char *virSocketAddrGetPath(virSocketAddrPtr addr); int virSocketAddrSetPort(virSocketAddrPtr addr, int port); @@ -145,5 +158,10 @@ int virSocketAddrPTRDomain(const virSocketAddr *addr, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); void virSocketAddrFree(virSocketAddrPtr addr); +void virSocketAddrClear(virSocketAddrPtr addr); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virSocketAddr, virSocketAddrFree); + +bool virSocketAddrCheck(const virSocketAddr *addr, + const void *parent, + void *opaque); -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 49 ++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 2fcd498..9ca8f74 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -886,26 +886,26 @@ virNetworkDNSSrvDefParseXML(const char *networkName, static int -virNetworkDNSTxtDefParseXML(const char *networkName, - xmlNodePtr node, - virNetworkDNSTxtDefPtr def, - bool partialOkay) +virNetworkDNSTxtDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSTxtDefPtr def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque) { const char *bad = " ,"; + bool partialOkay = false; + + if (opaque) + partialOkay = *((bool *) opaque); - if (!(def->name = virXMLPropString(node, "name"))) { - virReportError(VIR_ERR_XML_DETAIL, - _("missing required name attribute in DNS TXT record " - "of network %s"), networkName); - goto error; - } if (strcspn(def->name, bad) != strlen(def->name)) { virReportError(VIR_ERR_XML_DETAIL, _("prohibited character in DNS TXT record " "name '%s' of network %s"), def->name, networkName); goto error; } - if (!(def->value = virXMLPropString(node, "value")) && !partialOkay) { + + if (!def->value && !partialOkay) { virReportError(VIR_ERR_XML_DETAIL, _("missing required value attribute in DNS TXT record " "named '%s' of network %s"), def->name, networkName); @@ -918,6 +918,33 @@ virNetworkDNSTxtDefParseXML(const char *networkName, "in DNS TXT record of network %s"), networkName); goto error; } + + return 0; + + error: + return -1; +} + + +static int +virNetworkDNSTxtDefParseXML(const char *networkName, + xmlNodePtr node, + virNetworkDNSTxtDefPtr def, + bool partialOkay) +{ + if (!(def->name = virXMLPropString(node, "name"))) { + virReportError(VIR_ERR_XML_DETAIL, + _("missing required name attribute in DNS TXT record " + "of network %s"), networkName); + goto error; + } + + def->value = virXMLPropString(node, "value"); + + if (virNetworkDNSTxtDefParseXMLHook(node, def, networkName, + NULL, &partialOkay) < 0) + goto error; + return 0; error: -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- po/POTFILES.in | 1 + src/conf/meson.build | 1 + src/conf/network_conf.c | 45 ++++++----------------------------------- src/conf/network_conf.h | 8 +++++--- 4 files changed, 13 insertions(+), 42 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index c9c0262..0356f1e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -3,6 +3,7 @@ @BUILDDIR@src/access/viraccessapicheckqemu.c @BUILDDIR@src/admin/admin_client.h @BUILDDIR@src/admin/admin_server_dispatch_stubs.h +@BUILDDIR@src/conf/network_conf.generated.c @BUILDDIR@src/remote/remote_client_bodies.h @BUILDDIR@src/remote/remote_daemon_dispatch_stubs.h @SRCDIR@scripts/xmlgen/directive.py diff --git a/src/conf/meson.build b/src/conf/meson.build index d82bffc..0619ca6 100644 --- a/src/conf/meson.build +++ b/src/conf/meson.build @@ -1,4 +1,5 @@ xmlgen_input = [ + 'network_conf.h', ] xmlgen_output = [] diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 9ca8f74..036df20 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -174,14 +174,6 @@ virNetworkIPDefClear(virNetworkIPDefPtr def) } -static void -virNetworkDNSTxtDefClear(virNetworkDNSTxtDefPtr def) -{ - VIR_FREE(def->name); - VIR_FREE(def->value); -} - - static void virNetworkDNSHostDefClear(virNetworkDNSHostDefPtr def) { @@ -885,7 +877,7 @@ virNetworkDNSSrvDefParseXML(const char *networkName, } -static int +int virNetworkDNSTxtDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSTxtDefPtr def, const char *networkName, @@ -926,33 +918,6 @@ virNetworkDNSTxtDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSTxtDefParseXML(const char *networkName, - xmlNodePtr node, - virNetworkDNSTxtDefPtr def, - bool partialOkay) -{ - if (!(def->name = virXMLPropString(node, "name"))) { - virReportError(VIR_ERR_XML_DETAIL, - _("missing required name attribute in DNS TXT record " - "of network %s"), networkName); - goto error; - } - - def->value = virXMLPropString(node, "value"); - - if (virNetworkDNSTxtDefParseXMLHook(node, def, networkName, - NULL, &partialOkay) < 0) - goto error; - - return 0; - - error: - virNetworkDNSTxtDefClear(def); - return -1; -} - - static int virNetworkDNSDefParseXML(const char *networkName, xmlNodePtr node, @@ -1080,8 +1045,8 @@ virNetworkDNSDefParseXML(const char *networkName, return -1; for (i = 0; i < ntxts; i++) { - if (virNetworkDNSTxtDefParseXML(networkName, txtNodes[i], - &def->txts[def->ntxts], false) < 0) { + if (virNetworkDNSTxtDefParseXML(txtNodes[i], &def->txts[def->ntxts], + networkName, def, NULL) < 0) { return -1; } def->ntxts++; @@ -3637,6 +3602,7 @@ virNetworkDefUpdateDNSTxt(virNetworkDefPtr def, virNetworkDNSTxtDef txt; bool isAdd = (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST || command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST); + bool notAdd; memset(&txt, 0, sizeof(txt)); @@ -3650,7 +3616,8 @@ virNetworkDefUpdateDNSTxt(virNetworkDefPtr def, if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "txt") < 0) goto cleanup; - if (virNetworkDNSTxtDefParseXML(def->name, ctxt->node, &txt, !isAdd) < 0) + notAdd = !isAdd; + if (virNetworkDNSTxtDefParseXML(ctxt->node, &txt, def->name, def, ¬Add) < 0) goto cleanup; for (foundIdx = 0; foundIdx < dns->ntxts; foundIdx++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 34b9aa9..d5fd8cc 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -130,9 +130,9 @@ struct _virNetworkDHCPHostDef { typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef; typedef virNetworkDNSTxtDef *virNetworkDNSTxtDefPtr; -struct _virNetworkDNSTxtDef { - char *name; - char *value; +struct _virNetworkDNSTxtDef { /* genparse:concisehook */ + char *name; /* xmlattr, required */ + char *value; /* xmlattr */ }; typedef struct _virNetworkDNSSrvDef virNetworkDNSSrvDef; @@ -443,3 +443,5 @@ virNetworkDefUpdateSection(virNetworkDefPtr def, unsigned int flags); /* virNetworkUpdateFlags */ VIR_ENUM_DECL(virNetworkTaint); + +#include "network_conf.generated.h" -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 4 ++-- src/conf/network_conf.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 036df20..7b17e44 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -2219,8 +2219,8 @@ virNetworkDNSDefFormat(virBufferPtr buf, } for (i = 0; i < def->ntxts; i++) { - virBufferEscapeString(buf, "<txt name='%s' ", def->txts[i].name); - virBufferEscapeString(buf, "value='%s'/>\n", def->txts[i].value); + if (virNetworkDNSTxtDefFormatBuf(buf, "txt", &def->txts[i], def, NULL) < 0) + return -1; } for (i = 0; i < def->nsrvs; i++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index d5fd8cc..338ac65 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -130,7 +130,7 @@ struct _virNetworkDHCPHostDef { typedef struct _virNetworkDNSTxtDef virNetworkDNSTxtDef; typedef virNetworkDNSTxtDef *virNetworkDNSTxtDefPtr; -struct _virNetworkDNSTxtDef { /* genparse:concisehook */ +struct _virNetworkDNSTxtDef { /* genparse:concisehook, genformat */ char *name; /* xmlattr, required */ char *value; /* xmlattr */ }; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 107 +++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 24 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 7b17e44..dc39afb 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -767,23 +767,26 @@ virNetworkDNSHostDefParseXML(const char *networkName, "_-+/*" static int -virNetworkDNSSrvDefParseXML(const char *networkName, - xmlNodePtr node, - xmlXPathContextPtr ctxt, - virNetworkDNSSrvDefPtr def, - bool partialOkay) +virNetworkDNSSrvDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSSrvDefPtr def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque, + const char *portStr, + const char *priorityStr, + const char *weightStr) { - int ret; - VIR_XPATH_NODE_AUTORESTORE(ctxt) - - ctxt->node = node; + bool partialOkay = false; + if (opaque) + partialOkay = *((bool *) opaque); - if (!(def->service = virXMLPropString(node, "service")) && !partialOkay) { + if (!def->service && !partialOkay) { virReportError(VIR_ERR_XML_DETAIL, _("missing required service attribute in DNS SRV record " "of network '%s'"), networkName); goto error; } + if (def->service) { if (strlen(def->service) > DNS_RECORD_LENGTH_SRV) { virReportError(VIR_ERR_XML_DETAIL, @@ -801,13 +804,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName, } } - if (!(def->protocol = virXMLPropString(node, "protocol")) && !partialOkay) { + if (!def->protocol && !partialOkay) { virReportError(VIR_ERR_XML_DETAIL, _("missing required protocol attribute " "in DNS SRV record '%s' of network '%s'"), def->service, networkName); goto error; } + if (def->protocol && strspn(def->protocol, PROTOCOL_CHARS) < strlen(def->protocol)) { virReportError(VIR_ERR_XML_DETAIL, @@ -817,19 +821,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName, goto error; } - /* Following attributes are optional */ - def->domain = virXMLPropString(node, "domain"); - def->target = virXMLPropString(node, "target"); - - ret = virXPathUInt("string(./@port)", ctxt, &def->port); - if (ret >= 0 && !def->target) { + if (portStr && !def->target) { virReportError(VIR_ERR_XML_DETAIL, _("DNS SRV port attribute not permitted without " "target for service '%s' in network '%s'"), def->service, networkName); goto error; } - if (ret == -2 || (ret >= 0 && (def->port < 1 || def->port > 65535))) { + if (portStr && (def->port < 1 || def->port > 65535)) { virReportError(VIR_ERR_XML_DETAIL, _("invalid DNS SRV port attribute " "for service '%s' in network '%s'"), @@ -837,15 +836,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName, goto error; } - ret = virXPathUInt("string(./@priority)", ctxt, &def->priority); - if (ret >= 0 && !def->target) { + if (priorityStr && !def->target) { virReportError(VIR_ERR_XML_DETAIL, _("DNS SRV priority attribute not permitted without " "target for service '%s' in network '%s'"), def->service, networkName); goto error; } - if (ret == -2 || (ret >= 0 && def->priority > 65535)) { + if (priorityStr && def->priority > 65535) { virReportError(VIR_ERR_XML_DETAIL, _("Invalid DNS SRV priority attribute " "for service '%s' in network '%s'"), @@ -853,15 +851,14 @@ virNetworkDNSSrvDefParseXML(const char *networkName, goto error; } - ret = virXPathUInt("string(./@weight)", ctxt, &def->weight); - if (ret >= 0 && !def->target) { + if (weightStr && !def->target) { virReportError(VIR_ERR_XML_DETAIL, _("DNS SRV weight attribute not permitted without " "target for service '%s' in network '%s'"), def->service, networkName); goto error; } - if (ret == -2 || (ret >= 0 && def->weight > 65535)) { + if (weightStr && def->weight > 65535) { virReportError(VIR_ERR_XML_DETAIL, _("invalid DNS SRV weight attribute " "for service '%s' in network '%s'"), @@ -871,6 +868,68 @@ virNetworkDNSSrvDefParseXML(const char *networkName, return 0; + error: + return -1; +} + + +static int +virNetworkDNSSrvDefParseXML(const char *networkName, + xmlNodePtr node, + xmlXPathContextPtr ctxt G_GNUC_UNUSED, + virNetworkDNSSrvDefPtr def, + bool partialOkay) +{ + g_autofree char *portStr = NULL; + g_autofree char *priorityStr = NULL; + g_autofree char *weightStr = NULL; + + def->service = virXMLPropString(node, "service"); + def->protocol = virXMLPropString(node, "protocol"); + + /* Following attributes are optional */ + def->domain = virXMLPropString(node, "domain"); + def->target = virXMLPropString(node, "target"); + + portStr = virXMLPropString(node, "port"); + if (portStr) { + if (virStrToLong_uip(portStr, NULL, 0, &def->port) < 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("invalid DNS SRV port attribute " + "for service '%s' in network '%s'"), + def->service, networkName); + goto error; + } + } + + priorityStr = virXMLPropString(node, "priority"); + if (priorityStr) { + if (virStrToLong_uip(priorityStr, NULL, 0, &def->priority) < 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("Invalid DNS SRV priority attribute " + "for service '%s' in network '%s'"), + def->service, networkName); + goto error; + } + } + + weightStr = virXMLPropString(node, "weight"); + if (weightStr) { + if (virStrToLong_uip(weightStr, NULL, 0, &def->weight) < 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("invalid DNS SRV weight attribute " + "for service '%s' in network '%s'"), + def->service, networkName); + goto error; + } + } + + if (virNetworkDNSSrvDefParseXMLHook(node, def, networkName, def, &partialOkay, + portStr, priorityStr, weightStr) < 0) + goto error; + + return 0; + error: virNetworkDNSSrvDefClear(def); return -1; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 83 +++-------------------------------------- src/conf/network_conf.h | 16 ++++---- 2 files changed, 14 insertions(+), 85 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index dc39afb..96664ab 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -183,16 +183,6 @@ virNetworkDNSHostDefClear(virNetworkDNSHostDefPtr def) } -static void -virNetworkDNSSrvDefClear(virNetworkDNSSrvDefPtr def) -{ - VIR_FREE(def->domain); - VIR_FREE(def->service); - VIR_FREE(def->protocol); - VIR_FREE(def->target); -} - - static void virNetworkDNSForwarderClear(virNetworkDNSForwarderPtr def) { @@ -766,7 +756,7 @@ virNetworkDNSHostDefParseXML(const char *networkName, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" \ "_-+/*" -static int +int virNetworkDNSSrvDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSSrvDefPtr def, const char *networkName, @@ -873,69 +863,6 @@ virNetworkDNSSrvDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSSrvDefParseXML(const char *networkName, - xmlNodePtr node, - xmlXPathContextPtr ctxt G_GNUC_UNUSED, - virNetworkDNSSrvDefPtr def, - bool partialOkay) -{ - g_autofree char *portStr = NULL; - g_autofree char *priorityStr = NULL; - g_autofree char *weightStr = NULL; - - def->service = virXMLPropString(node, "service"); - def->protocol = virXMLPropString(node, "protocol"); - - /* Following attributes are optional */ - def->domain = virXMLPropString(node, "domain"); - def->target = virXMLPropString(node, "target"); - - portStr = virXMLPropString(node, "port"); - if (portStr) { - if (virStrToLong_uip(portStr, NULL, 0, &def->port) < 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("invalid DNS SRV port attribute " - "for service '%s' in network '%s'"), - def->service, networkName); - goto error; - } - } - - priorityStr = virXMLPropString(node, "priority"); - if (priorityStr) { - if (virStrToLong_uip(priorityStr, NULL, 0, &def->priority) < 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("Invalid DNS SRV priority attribute " - "for service '%s' in network '%s'"), - def->service, networkName); - goto error; - } - } - - weightStr = virXMLPropString(node, "weight"); - if (weightStr) { - if (virStrToLong_uip(weightStr, NULL, 0, &def->weight) < 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("invalid DNS SRV weight attribute " - "for service '%s' in network '%s'"), - def->service, networkName); - goto error; - } - } - - if (virNetworkDNSSrvDefParseXMLHook(node, def, networkName, def, &partialOkay, - portStr, priorityStr, weightStr) < 0) - goto error; - - return 0; - - error: - virNetworkDNSSrvDefClear(def); - return -1; -} - - int virNetworkDNSTxtDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSTxtDefPtr def, @@ -1084,8 +1011,8 @@ virNetworkDNSDefParseXML(const char *networkName, return -1; for (i = 0; i < nsrvs; i++) { - if (virNetworkDNSSrvDefParseXML(networkName, srvNodes[i], ctxt, - &def->srvs[def->nsrvs], false) < 0) { + if (virNetworkDNSSrvDefParseXML(srvNodes[i], &def->srvs[def->nsrvs], + networkName, def, NULL) < 0) { return -1; } def->nsrvs++; @@ -3576,6 +3503,7 @@ virNetworkDefUpdateDNSSrv(virNetworkDefPtr def, bool isAdd = (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST || command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST); int foundCt = 0; + bool notAdd; memset(&srv, 0, sizeof(srv)); @@ -3589,7 +3517,8 @@ virNetworkDefUpdateDNSSrv(virNetworkDefPtr def, if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "srv") < 0) goto cleanup; - if (virNetworkDNSSrvDefParseXML(def->name, ctxt->node, ctxt, &srv, !isAdd) < 0) + notAdd = !isAdd; + if (virNetworkDNSSrvDefParseXML(ctxt->node, &srv, def->name, def, ¬Add) < 0) goto cleanup; for (i = 0; i < dns->nsrvs; i++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 338ac65..3636698 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -137,14 +137,14 @@ struct _virNetworkDNSTxtDef { /* genparse:concisehook, genformat */ typedef struct _virNetworkDNSSrvDef virNetworkDNSSrvDef; typedef virNetworkDNSSrvDef *virNetworkDNSSrvDefPtr; -struct _virNetworkDNSSrvDef { - char *domain; - char *service; - char *protocol; - char *target; - unsigned int port; - unsigned int priority; - unsigned int weight; +struct _virNetworkDNSSrvDef { /* genparse:withhook */ + char *service; /* xmlattr */ + char *protocol; /* xmlattr */ + char *domain; /* xmlattr */ + char *target; /* xmlattr */ + unsigned int port; /* xmlattr */ + unsigned int priority; /* xmlattr */ + unsigned int weight; /* xmlattr */ }; typedef struct _virNetworkDNSHostDef virNetworkDNSHostDef; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 18 ++---------------- src/conf/network_conf.h | 2 +- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 96664ab..518f6da 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -2211,22 +2211,8 @@ virNetworkDNSDefFormat(virBufferPtr buf, for (i = 0; i < def->nsrvs; i++) { if (def->srvs[i].service && def->srvs[i].protocol) { - virBufferEscapeString(buf, "<srv service='%s' ", - def->srvs[i].service); - virBufferEscapeString(buf, "protocol='%s'", def->srvs[i].protocol); - - if (def->srvs[i].domain) - virBufferEscapeString(buf, " domain='%s'", def->srvs[i].domain); - if (def->srvs[i].target) - virBufferEscapeString(buf, " target='%s'", def->srvs[i].target); - if (def->srvs[i].port) - virBufferAsprintf(buf, " port='%d'", def->srvs[i].port); - if (def->srvs[i].priority) - virBufferAsprintf(buf, " priority='%d'", def->srvs[i].priority); - if (def->srvs[i].weight) - virBufferAsprintf(buf, " weight='%d'", def->srvs[i].weight); - - virBufferAddLit(buf, "/>\n"); + if (virNetworkDNSSrvDefFormatBuf(buf, "srv", &def->srvs[i], def, NULL) < 0) + return -1; } } diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 3636698..041119e 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -137,7 +137,7 @@ struct _virNetworkDNSTxtDef { /* genparse:concisehook, genformat */ typedef struct _virNetworkDNSSrvDef virNetworkDNSSrvDef; typedef virNetworkDNSSrvDef *virNetworkDNSSrvDefPtr; -struct _virNetworkDNSSrvDef { /* genparse:withhook */ +struct _virNetworkDNSSrvDef { /* genparse:withhook, genformat */ char *service; /* xmlattr */ char *protocol; /* xmlattr */ char *domain; /* xmlattr */ -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 63 +++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 518f6da..44032e6 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -675,21 +675,57 @@ virNetworkDHCPDefParseXML(const char *networkName, static int -virNetworkDNSHostDefParseXML(const char *networkName, - xmlNodePtr node, - virNetworkDNSHostDefPtr def, - bool partialOkay) +virNetworkDNSHostDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSHostDefPtr def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque, + const char *ip, + int nHostnameNodes G_GNUC_UNUSED) { - xmlNodePtr cur; - g_autofree char *ip = NULL; + bool partialOkay = false; - if (!(ip = virXMLPropString(node, "ip")) && !partialOkay) { + if (opaque) + partialOkay = *((bool *) opaque); + + if (!ip && !partialOkay) { virReportError(VIR_ERR_XML_DETAIL, _("Missing IP address in network '%s' DNS HOST record"), networkName); goto error; } + if (def->nnames == 0 && !partialOkay) { + virReportError(VIR_ERR_XML_DETAIL, + _("Missing hostname in network '%s' DNS HOST record"), + networkName); + goto error; + } + + if (!VIR_SOCKET_ADDR_VALID(&def->ip) && def->nnames == 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("Missing ip and hostname in network '%s' DNS HOST record"), + networkName); + goto error; + } + + return 0; + + error: + return -1; +} + + +static int +virNetworkDNSHostDefParseXML(const char *networkName, + xmlNodePtr node, + virNetworkDNSHostDefPtr def, + bool partialOkay) +{ + xmlNodePtr cur; + g_autofree char *ip = NULL; + + ip = virXMLPropString(node, "ip"); if (ip && (virSocketAddrParse(&def->ip, ip, AF_UNSPEC) < 0)) { virReportError(VIR_ERR_XML_DETAIL, _("Invalid IP address in network '%s' DNS HOST record"), @@ -719,19 +755,10 @@ virNetworkDNSHostDefParseXML(const char *networkName, } cur = cur->next; } - if (def->nnames == 0 && !partialOkay) { - virReportError(VIR_ERR_XML_DETAIL, - _("Missing hostname in network '%s' DNS HOST record"), - networkName); - goto error; - } - if (!VIR_SOCKET_ADDR_VALID(&def->ip) && def->nnames == 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("Missing ip and hostname in network '%s' DNS HOST record"), - networkName); + if (virNetworkDNSHostDefParseXMLHook(node, def, networkName, def, &partialOkay, + ip, def->nnames) < 0) goto error; - } return 0; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 71 ++++------------------------------------- src/conf/network_conf.h | 6 ++-- 2 files changed, 9 insertions(+), 68 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 44032e6..e2d0297 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -174,15 +174,6 @@ virNetworkIPDefClear(virNetworkIPDefPtr def) } -static void -virNetworkDNSHostDefClear(virNetworkDNSHostDefPtr def) -{ - while (def->nnames) - VIR_FREE(def->names[--def->nnames]); - VIR_FREE(def->names); -} - - static void virNetworkDNSForwarderClear(virNetworkDNSForwarderPtr def) { @@ -674,7 +665,7 @@ virNetworkDHCPDefParseXML(const char *networkName, } -static int +int virNetworkDNSHostDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSHostDefPtr def, const char *networkName, @@ -716,58 +707,6 @@ virNetworkDNSHostDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSHostDefParseXML(const char *networkName, - xmlNodePtr node, - virNetworkDNSHostDefPtr def, - bool partialOkay) -{ - xmlNodePtr cur; - g_autofree char *ip = NULL; - - ip = virXMLPropString(node, "ip"); - if (ip && (virSocketAddrParse(&def->ip, ip, AF_UNSPEC) < 0)) { - virReportError(VIR_ERR_XML_DETAIL, - _("Invalid IP address in network '%s' DNS HOST record"), - networkName); - goto error; - } - - cur = node->children; - while (cur != NULL) { - if (cur->type == XML_ELEMENT_NODE && - virXMLNodeNameEqual(cur, "hostname")) { - if (cur->children != NULL) { - g_autofree char *name = virXMLNodeContentString(cur); - - if (!name) - goto error; - - if (!name[0]) { - virReportError(VIR_ERR_XML_DETAIL, - _("Missing hostname in network '%s' DNS HOST record"), - networkName); - goto error; - } - if (VIR_APPEND_ELEMENT(def->names, def->nnames, name) < 0) - goto error; - } - } - cur = cur->next; - } - - if (virNetworkDNSHostDefParseXMLHook(node, def, networkName, def, &partialOkay, - ip, def->nnames) < 0) - goto error; - - return 0; - - error: - virNetworkDNSHostDefClear(def); - return -1; -} - - /* This includes all characters used in the names of current * /etc/services and /etc/protocols files (on Fedora 20), except ".", * which we can't allow because it would conflict with the use of "." @@ -1018,8 +957,8 @@ virNetworkDNSDefParseXML(const char *networkName, return -1; for (i = 0; i < nhosts; i++) { - if (virNetworkDNSHostDefParseXML(networkName, hostNodes[i], - &def->hosts[def->nhosts], false) < 0) { + if (virNetworkDNSHostDefParseXML(hostNodes[i], &def->hosts[def->nhosts], + networkName, def, NULL) < 0) { return -1; } def->nhosts++; @@ -3421,6 +3360,7 @@ virNetworkDefUpdateDNSHost(virNetworkDefPtr def, bool isAdd = (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST || command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST); int foundCt = 0; + bool notAdd; memset(&host, 0, sizeof(host)); @@ -3434,7 +3374,8 @@ virNetworkDefUpdateDNSHost(virNetworkDefPtr def, if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "host") < 0) goto cleanup; - if (virNetworkDNSHostDefParseXML(def->name, ctxt->node, &host, !isAdd) < 0) + notAdd = !isAdd; + if (virNetworkDNSHostDefParseXML(ctxt->node, &host, def->name, def, ¬Add) < 0) goto cleanup; for (i = 0; i < dns->nhosts; i++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 041119e..c30fd4f 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -149,10 +149,10 @@ struct _virNetworkDNSSrvDef { /* genparse:withhook, genformat */ typedef struct _virNetworkDNSHostDef virNetworkDNSHostDef; typedef virNetworkDNSHostDef *virNetworkDNSHostDefPtr; -struct _virNetworkDNSHostDef { - virSocketAddr ip; +struct _virNetworkDNSHostDef { /* genparse:withhook */ + virSocketAddr ip; /* xmlattr */ size_t nnames; - char **names; + char **names; /* xmlelem:hostname, array */ }; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 14 +++----------- src/conf/network_conf.h | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index e2d0297..0ed2332 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -2115,7 +2115,7 @@ static int virNetworkDNSDefFormat(virBufferPtr buf, const virNetworkDNSDef *def) { - size_t i, j; + size_t i; if (!(def->enable || def->forwardPlainNames || def->nfwds || def->nhosts || def->nsrvs || def->ntxts)) @@ -2184,16 +2184,8 @@ virNetworkDNSDefFormat(virBufferPtr buf, if (def->nhosts) { for (i = 0; i < def->nhosts; i++) { - g_autofree char *ip = virSocketAddrFormat(&def->hosts[i].ip); - - virBufferAsprintf(buf, "<host ip='%s'>\n", ip); - virBufferAdjustIndent(buf, 2); - for (j = 0; j < def->hosts[i].nnames; j++) - virBufferEscapeString(buf, "<hostname>%s</hostname>\n", - def->hosts[i].names[j]); - - virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</host>\n"); + if (virNetworkDNSHostDefFormatBuf(buf, "host", &def->hosts[i], def, NULL) < 0) + return -1; } } virBufferAdjustIndent(buf, -2); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index c30fd4f..61863b0 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -149,7 +149,7 @@ struct _virNetworkDNSSrvDef { /* genparse:withhook, genformat */ typedef struct _virNetworkDNSHostDef virNetworkDNSHostDef; typedef virNetworkDNSHostDef *virNetworkDNSHostDefPtr; -struct _virNetworkDNSHostDef { /* genparse:withhook */ +struct _virNetworkDNSHostDef { /* genparse:withhook, genformat */ virSocketAddr ip; /* xmlattr */ size_t nnames; char **names; /* xmlelem:hostname, array */ -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 68 +++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 0ed2332..2af537c 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -870,6 +870,52 @@ virNetworkDNSTxtDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } +static int +virNetworkDNSForwarderParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSForwarderPtr def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED, + const char *addr) +{ + if (!(addr || def->domain)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Invalid forwarder element, must contain " + "at least one of addr or domain")); + return -1; + } + + return 0; +} + + +static int +virNetworkDNSForwarderParseXML(xmlNodePtr node, + virNetworkDNSForwarderPtr def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque) +{ + g_autofree char *addr = virXMLPropString(node, "addr"); + + if (addr && virSocketAddrParse(&def->addr, addr, AF_UNSPEC) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid forwarder IP address '%s' " + "in network '%s'"), + addr, networkName); + return -1; + } + + def->domain = virXMLPropString(node, "domain"); + + if (virNetworkDNSForwarderParseXMLHook(node, def, networkName, def, opaque, + addr) < 0) + return -1; + + return 0; +} + + static int virNetworkDNSDefParseXML(const char *networkName, xmlNodePtr node, @@ -924,23 +970,13 @@ virNetworkDNSDefParseXML(const char *networkName, return -1; for (i = 0; i < nfwds; i++) { - g_autofree char *addr = virXMLPropString(fwdNodes[i], "addr"); - - if (addr && virSocketAddrParse(&def->forwarders[i].addr, - addr, AF_UNSPEC) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid forwarder IP address '%s' " - "in network '%s'"), - addr, networkName); + if (virNetworkDNSForwarderParseXML(fwdNodes[i], + &def->forwarders[i], + networkName, + def, + NULL) < 0) return -1; - } - def->forwarders[i].domain = virXMLPropString(fwdNodes[i], "domain"); - if (!(addr || def->forwarders[i].domain)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Invalid forwarder element, must contain " - "at least one of addr or domain")); - return -1; - } + def->nfwds++; } } -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 36 +----------------------------------- src/conf/network_conf.h | 6 +++--- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 2af537c..608843d 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -174,13 +174,6 @@ virNetworkIPDefClear(virNetworkIPDefPtr def) } -static void -virNetworkDNSForwarderClear(virNetworkDNSForwarderPtr def) -{ - VIR_FREE(def->domain); -} - - static void virNetworkDNSDefClear(virNetworkDNSDefPtr def) { @@ -870,7 +863,7 @@ virNetworkDNSTxtDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int +int virNetworkDNSForwarderParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSForwarderPtr def, const char *instname G_GNUC_UNUSED, @@ -889,33 +882,6 @@ virNetworkDNSForwarderParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSForwarderParseXML(xmlNodePtr node, - virNetworkDNSForwarderPtr def, - const char *networkName, - void *parent G_GNUC_UNUSED, - void *opaque) -{ - g_autofree char *addr = virXMLPropString(node, "addr"); - - if (addr && virSocketAddrParse(&def->addr, addr, AF_UNSPEC) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid forwarder IP address '%s' " - "in network '%s'"), - addr, networkName); - return -1; - } - - def->domain = virXMLPropString(node, "domain"); - - if (virNetworkDNSForwarderParseXMLHook(node, def, networkName, def, opaque, - addr) < 0) - return -1; - - return 0; -} - - static int virNetworkDNSDefParseXML(const char *networkName, xmlNodePtr node, diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 61863b0..881a9f2 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -158,9 +158,9 @@ struct _virNetworkDNSHostDef { /* genparse:withhook, genformat */ typedef struct _virNetworkDNSForwarder virNetworkDNSForwarder; typedef virNetworkDNSForwarder *virNetworkDNSForwarderPtr; -struct _virNetworkDNSForwarder { - virSocketAddr addr; - char *domain; +struct _virNetworkDNSForwarder { /* genparse:withhook */ + virSocketAddr addr; /* xmlattr */ + char *domain; /* xmlattr */ }; typedef struct _virNetworkDNSDef virNetworkDNSDef; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 18 +++--------------- src/conf/network_conf.h | 4 ++-- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 608843d..437c2c6 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -2155,21 +2155,9 @@ virNetworkDNSDefFormat(virBufferPtr buf, virBufferAdjustIndent(buf, 2); for (i = 0; i < def->nfwds; i++) { - - virBufferAddLit(buf, "<forwarder"); - if (def->forwarders[i].domain) { - virBufferEscapeString(buf, " domain='%s'", - def->forwarders[i].domain); - } - if (VIR_SOCKET_ADDR_VALID(&def->forwarders[i].addr)) { - g_autofree char *addr = virSocketAddrFormat(&def->forwarders[i].addr); - - if (!addr) - return -1; - - virBufferAsprintf(buf, " addr='%s'", addr); - } - virBufferAddLit(buf, "/>\n"); + if (virNetworkDNSForwarderFormatBuf(buf, "forwarder", + &def->forwarders[i], def, NULL) < 0) + return -1; } for (i = 0; i < def->ntxts; i++) { diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 881a9f2..84b636e 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -158,9 +158,9 @@ struct _virNetworkDNSHostDef { /* genparse:withhook, genformat */ typedef struct _virNetworkDNSForwarder virNetworkDNSForwarder; typedef virNetworkDNSForwarder *virNetworkDNSForwarderPtr; -struct _virNetworkDNSForwarder { /* genparse:withhook */ - virSocketAddr addr; /* xmlattr */ +struct _virNetworkDNSForwarder { /* genparse:withhook, genformat */ char *domain; /* xmlattr */ + virSocketAddr addr; /* xmlattr */ }; typedef struct _virNetworkDNSDef virNetworkDNSDef; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 437c2c6..910d23f 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -882,6 +882,31 @@ virNetworkDNSForwarderParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } +static int +virNetworkDNSDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, + virNetworkDNSDefPtr def, + const char *networkName, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED, + const char *enable G_GNUC_UNUSED, + const char *forwardPlainNames G_GNUC_UNUSED, + int ntxts, + int nhosts, + int nsrvs, + int nfwds) +{ + if (def->enable == VIR_TRISTATE_BOOL_NO && + (nfwds || nhosts || nsrvs || ntxts)) { + virReportError(VIR_ERR_XML_ERROR, + _("Extra data in disabled network '%s'"), + networkName); + return -1; + } + + return 0; +} + + static int virNetworkDNSDefParseXML(const char *networkName, xmlNodePtr node, @@ -1007,13 +1032,10 @@ virNetworkDNSDefParseXML(const char *networkName, } } - if (def->enable == VIR_TRISTATE_BOOL_NO && - (nfwds || nhosts || nsrvs || ntxts)) { - virReportError(VIR_ERR_XML_ERROR, - _("Extra data in disabled network '%s'"), - networkName); + if (virNetworkDNSDefParseXMLHook(node, def, networkName, def, NULL, + enable, forwardPlainNames, + ntxts, nhosts, nsrvs, nfwds) < 0) return -1; - } return 0; } -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 164 +--------------------------------------- src/conf/network_conf.h | 14 ++-- 2 files changed, 9 insertions(+), 169 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 910d23f..2f1a88b 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -174,32 +174,6 @@ virNetworkIPDefClear(virNetworkIPDefPtr def) } -static void -virNetworkDNSDefClear(virNetworkDNSDefPtr def) -{ - if (def->forwarders) { - while (def->nfwds) - virNetworkDNSForwarderClear(&def->forwarders[--def->nfwds]); - VIR_FREE(def->forwarders); - } - if (def->txts) { - while (def->ntxts) - virNetworkDNSTxtDefClear(&def->txts[--def->ntxts]); - VIR_FREE(def->txts); - } - if (def->hosts) { - while (def->nhosts) - virNetworkDNSHostDefClear(&def->hosts[--def->nhosts]); - VIR_FREE(def->hosts); - } - if (def->srvs) { - while (def->nsrvs) - virNetworkDNSSrvDefClear(&def->srvs[--def->nsrvs]); - VIR_FREE(def->srvs); - } -} - - static void virNetworkForwardDefClear(virNetworkForwardDefPtr def) { @@ -882,7 +856,7 @@ virNetworkDNSForwarderParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int +int virNetworkDNSDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virNetworkDNSDefPtr def, const char *networkName, @@ -907,140 +881,6 @@ virNetworkDNSDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virNetworkDNSDefParseXML(const char *networkName, - xmlNodePtr node, - xmlXPathContextPtr ctxt, - virNetworkDNSDefPtr def) -{ - g_autofree xmlNodePtr *hostNodes = NULL; - g_autofree xmlNodePtr *srvNodes = NULL; - g_autofree xmlNodePtr *txtNodes = NULL; - g_autofree xmlNodePtr *fwdNodes = NULL; - g_autofree char *forwardPlainNames = NULL; - g_autofree char *enable = NULL; - int nhosts, nsrvs, ntxts, nfwds; - size_t i; - VIR_XPATH_NODE_AUTORESTORE(ctxt) - - ctxt->node = node; - - enable = virXPathString("string(./@enable)", ctxt); - if (enable) { - def->enable = virTristateBoolTypeFromString(enable); - if (def->enable <= 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid dns enable setting '%s' " - "in network '%s'"), - enable, networkName); - return -1; - } - } - - forwardPlainNames = virXPathString("string(./@forwardPlainNames)", ctxt); - if (forwardPlainNames) { - def->forwardPlainNames = virTristateBoolTypeFromString(forwardPlainNames); - if (def->forwardPlainNames <= 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid dns forwardPlainNames setting '%s' " - "in network '%s'"), - forwardPlainNames, networkName); - return -1; - } - } - - nfwds = virXPathNodeSet("./forwarder", ctxt, &fwdNodes); - if (nfwds < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid <forwarder> element found in <dns> of network %s"), - networkName); - return -1; - } - if (nfwds > 0) { - if (VIR_ALLOC_N(def->forwarders, nfwds) < 0) - return -1; - - for (i = 0; i < nfwds; i++) { - if (virNetworkDNSForwarderParseXML(fwdNodes[i], - &def->forwarders[i], - networkName, - def, - NULL) < 0) - return -1; - - def->nfwds++; - } - } - - nhosts = virXPathNodeSet("./host", ctxt, &hostNodes); - if (nhosts < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid <host> element found in <dns> of network %s"), - networkName); - return -1; - } - if (nhosts > 0) { - if (VIR_ALLOC_N(def->hosts, nhosts) < 0) - return -1; - - for (i = 0; i < nhosts; i++) { - if (virNetworkDNSHostDefParseXML(hostNodes[i], &def->hosts[def->nhosts], - networkName, def, NULL) < 0) { - return -1; - } - def->nhosts++; - } - } - - nsrvs = virXPathNodeSet("./srv", ctxt, &srvNodes); - if (nsrvs < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid <srv> element found in <dns> of network %s"), - networkName); - return -1; - } - if (nsrvs > 0) { - if (VIR_ALLOC_N(def->srvs, nsrvs) < 0) - return -1; - - for (i = 0; i < nsrvs; i++) { - if (virNetworkDNSSrvDefParseXML(srvNodes[i], &def->srvs[def->nsrvs], - networkName, def, NULL) < 0) { - return -1; - } - def->nsrvs++; - } - } - - ntxts = virXPathNodeSet("./txt", ctxt, &txtNodes); - if (ntxts < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid <txt> element found in <dns> of network %s"), - networkName); - return -1; - } - if (ntxts > 0) { - if (VIR_ALLOC_N(def->txts, ntxts) < 0) - return -1; - - for (i = 0; i < ntxts; i++) { - if (virNetworkDNSTxtDefParseXML(txtNodes[i], &def->txts[def->ntxts], - networkName, def, NULL) < 0) { - return -1; - } - def->ntxts++; - } - } - - if (virNetworkDNSDefParseXMLHook(node, def, networkName, def, NULL, - enable, forwardPlainNames, - ntxts, nhosts, nsrvs, nfwds) < 0) - return -1; - - return 0; -} - - static int virNetworkIPDefParseXML(const char *networkName, xmlNodePtr node, @@ -1812,7 +1652,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt, dnsNode = virXPathNode("./dns", ctxt); if (dnsNode != NULL && - virNetworkDNSDefParseXML(def->name, dnsNode, ctxt, &def->dns) < 0) { + virNetworkDNSDefParseXML(dnsNode, &def->dns, def->name, def, NULL) < 0) { return NULL; } diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 84b636e..45cfa4e 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -165,17 +165,17 @@ struct _virNetworkDNSForwarder { /* genparse:withhook, genformat */ typedef struct _virNetworkDNSDef virNetworkDNSDef; typedef virNetworkDNSDef *virNetworkDNSDefPtr; -struct _virNetworkDNSDef { - int enable; /* enum virTristateBool */ - int forwardPlainNames; /* enum virTristateBool */ +struct _virNetworkDNSDef { /* genparse:withhook */ + virTristateBool enable; /* xmlattr */ + virTristateBool forwardPlainNames; /* xmlattr */ size_t ntxts; - virNetworkDNSTxtDefPtr txts; + virNetworkDNSTxtDefPtr txts; /* xmlelem, array */ size_t nhosts; - virNetworkDNSHostDefPtr hosts; + virNetworkDNSHostDefPtr hosts; /* xmlelem, array */ size_t nsrvs; - virNetworkDNSSrvDefPtr srvs; + virNetworkDNSSrvDefPtr srvs; /* xmlelem, array */ size_t nfwds; - virNetworkDNSForwarderPtr forwarders; + virNetworkDNSForwarderPtr forwarders; /* xmlelem, array:nfwds */ }; typedef struct _virNetworkIPDef virNetworkIPDef; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/network_conf.c | 77 ++--------------------------------------- src/conf/network_conf.h | 10 +++--- 2 files changed, 8 insertions(+), 79 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 2f1a88b..3a126d4 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -864,10 +864,10 @@ virNetworkDNSDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, void *opaque G_GNUC_UNUSED, const char *enable G_GNUC_UNUSED, const char *forwardPlainNames G_GNUC_UNUSED, + int nfwds, int ntxts, - int nhosts, int nsrvs, - int nfwds) + int nhosts) { if (def->enable == VIR_TRISTATE_BOOL_NO && (nfwds || nhosts || nsrvs || ntxts)) { @@ -1975,77 +1975,6 @@ virNetworkDefParseNode(xmlDocPtr xml, } -static int -virNetworkDNSDefFormat(virBufferPtr buf, - const virNetworkDNSDef *def) -{ - size_t i; - - if (!(def->enable || def->forwardPlainNames || def->nfwds || def->nhosts || - def->nsrvs || def->ntxts)) - return 0; - - virBufferAddLit(buf, "<dns"); - if (def->enable) { - const char *fwd = virTristateBoolTypeToString(def->enable); - - if (!fwd) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unknown enable type %d in network"), - def->enable); - return -1; - } - virBufferAsprintf(buf, " enable='%s'", fwd); - } - if (def->forwardPlainNames) { - const char *fwd = virTristateBoolTypeToString(def->forwardPlainNames); - - if (!fwd) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unknown forwardPlainNames type %d in network"), - def->forwardPlainNames); - return -1; - } - virBufferAsprintf(buf, " forwardPlainNames='%s'", fwd); - } - if (!(def->nfwds || def->nhosts || def->nsrvs || def->ntxts)) { - virBufferAddLit(buf, "/>\n"); - return 0; - } - - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 2); - - for (i = 0; i < def->nfwds; i++) { - if (virNetworkDNSForwarderFormatBuf(buf, "forwarder", - &def->forwarders[i], def, NULL) < 0) - return -1; - } - - for (i = 0; i < def->ntxts; i++) { - if (virNetworkDNSTxtDefFormatBuf(buf, "txt", &def->txts[i], def, NULL) < 0) - return -1; - } - - for (i = 0; i < def->nsrvs; i++) { - if (def->srvs[i].service && def->srvs[i].protocol) { - if (virNetworkDNSSrvDefFormatBuf(buf, "srv", &def->srvs[i], def, NULL) < 0) - return -1; - } - } - - if (def->nhosts) { - for (i = 0; i < def->nhosts; i++) { - if (virNetworkDNSHostDefFormatBuf(buf, "host", &def->hosts[i], def, NULL) < 0) - return -1; - } - } - virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</dns>\n"); - return 0; -} - - static int virNetworkIPDefFormat(virBufferPtr buf, const virNetworkIPDef *def) @@ -2455,7 +2384,7 @@ virNetworkDefFormatBuf(virBufferPtr buf, virBufferAddLit(buf, "/>\n"); } - if (virNetworkDNSDefFormat(buf, &def->dns) < 0) + if (virNetworkDNSDefFormatBuf(buf, "dns", &def->dns, def, NULL) < 0) return -1; if (virNetDevVlanFormat(&def->vlan, buf) < 0) diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 45cfa4e..60739bb 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -165,17 +165,17 @@ struct _virNetworkDNSForwarder { /* genparse:withhook, genformat */ typedef struct _virNetworkDNSDef virNetworkDNSDef; typedef virNetworkDNSDef *virNetworkDNSDefPtr; -struct _virNetworkDNSDef { /* genparse:withhook */ +struct _virNetworkDNSDef { /* genparse:withhook, genformat */ virTristateBool enable; /* xmlattr */ virTristateBool forwardPlainNames; /* xmlattr */ + size_t nfwds; + virNetworkDNSForwarderPtr forwarders; /* xmlelem, array:nfwds */ size_t ntxts; virNetworkDNSTxtDefPtr txts; /* xmlelem, array */ - size_t nhosts; - virNetworkDNSHostDefPtr hosts; /* xmlelem, array */ size_t nsrvs; virNetworkDNSSrvDefPtr srvs; /* xmlelem, array */ - size_t nfwds; - virNetworkDNSForwarderPtr forwarders; /* xmlelem, array:nfwds */ + size_t nhosts; + virNetworkDNSHostDefPtr hosts; /* xmlelem, array */ }; typedef struct _virNetworkIPDef virNetworkIPDef; -- 2.25.1

Extract those embedded structs from virDomainGraphicsDef so that we can specify directives on them. Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.h | 112 ++++++++++++++++++++++------------------ src/conf/virconftypes.h | 18 +++++++ 2 files changed, 80 insertions(+), 50 deletions(-) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 14a3763..3d14aa1 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1672,6 +1672,62 @@ struct _virDomainGraphicsListenDef { bool autoGenerated; }; +struct _virDomainGraphicsSDLDef { + char *display; + char *xauth; + bool fullscreen; + virTristateBool gl; +}; + +struct _virDomainGraphicsVNCDef { + int port; + bool portReserved; + int websocket; + bool websocketGenerated; + bool autoport; + char *keymap; + virDomainGraphicsAuthDef auth; + int sharePolicy; +}; + +struct _virDomainGraphicsRDPDef { + int port; + bool autoport; + bool replaceUser; + bool multiUser; +}; + +struct _virDomainGraphicsDesktopDef { + char *display; + bool fullscreen; +}; + +struct _virDomainGraphicsSpiceDef { + int port; + int tlsPort; + bool portReserved; + bool tlsPortReserved; + virDomainGraphicsSpiceMouseMode mousemode; + char *keymap; + virDomainGraphicsAuthDef auth; + bool autoport; + int channels[VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST]; + virDomainGraphicsSpiceChannelMode defaultMode; + int image; + int jpeg; + int zlib; + int playback; + int streaming; + virTristateBool copypaste; + virTristateBool filetransfer; + virTristateBool gl; + char *rendernode; +}; + +struct _virDomainGraphicsEGLHeadlessDef { + char *rendernode; +}; + struct _virDomainGraphicsDef { virObjectPtr privateData; @@ -1682,56 +1738,12 @@ struct _virDomainGraphicsDef { */ virDomainGraphicsType type; union { - struct { - int port; - bool portReserved; - int websocket; - bool websocketGenerated; - bool autoport; - char *keymap; - virDomainGraphicsAuthDef auth; - int sharePolicy; - } vnc; - struct { - char *display; - char *xauth; - bool fullscreen; - virTristateBool gl; - } sdl; - struct { - int port; - bool autoport; - bool replaceUser; - bool multiUser; - } rdp; - struct { - char *display; - bool fullscreen; - } desktop; - struct { - int port; - int tlsPort; - bool portReserved; - bool tlsPortReserved; - virDomainGraphicsSpiceMouseMode mousemode; - char *keymap; - virDomainGraphicsAuthDef auth; - bool autoport; - int channels[VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST]; - virDomainGraphicsSpiceChannelMode defaultMode; - int image; - int jpeg; - int zlib; - int playback; - int streaming; - virTristateBool copypaste; - virTristateBool filetransfer; - virTristateBool gl; - char *rendernode; - } spice; - struct { - char *rendernode; - } egl_headless; + virDomainGraphicsSDLDef sdl; + virDomainGraphicsVNCDef vnc; + virDomainGraphicsRDPDef rdp; + virDomainGraphicsDesktopDef desktop; + virDomainGraphicsSpiceDef spice; + virDomainGraphicsEGLHeadlessDef egl_headless; } data; /* nListens, listens, and *port are only useful if type is vnc, * rdp, or spice. They've been extracted from the union only to diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h index 9042a2b..ae7c18d 100644 --- a/src/conf/virconftypes.h +++ b/src/conf/virconftypes.h @@ -362,3 +362,21 @@ typedef virDomainXMLPrivateDataCallbacks *virDomainXMLPrivateDataCallbacksPtr; typedef struct _virDomainXenbusControllerOpts virDomainXenbusControllerOpts; typedef virDomainXenbusControllerOpts *virDomainXenbusControllerOptsPtr; + +typedef struct _virDomainGraphicsSDLDef virDomainGraphicsSDLDef; +typedef virDomainGraphicsSDLDef *virDomainGraphicsSDLDefPtr; + +typedef struct _virDomainGraphicsVNCDef virDomainGraphicsVNCDef; +typedef virDomainGraphicsVNCDef *virDomainGraphicsVNCDefPtr; + +typedef struct _virDomainGraphicsRDPDef virDomainGraphicsRDPDef; +typedef virDomainGraphicsRDPDef *virDomainGraphicsRDPDefPtr; + +typedef struct _virDomainGraphicsDesktopDef virDomainGraphicsDesktopDef; +typedef virDomainGraphicsDesktopDef *virDomainGraphicsDesktopDefPtr; + +typedef struct _virDomainGraphicsSpiceDef virDomainGraphicsSpiceDef; +typedef virDomainGraphicsSpiceDef *virDomainGraphicsSpiceDefPtr; + +typedef struct _virDomainGraphicsEGLHeadlessDef virDomainGraphicsEGLHeadlessDef; +typedef virDomainGraphicsEGLHeadlessDef *virDomainGraphicsEGLHeadlessDefPtr; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- po/POTFILES.in | 1 + src/conf/domain_conf.c | 53 ++---------------------------------------- src/conf/domain_conf.h | 12 ++++++---- src/conf/meson.build | 1 + 4 files changed, 11 insertions(+), 56 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 0356f1e..4de0d14 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -3,6 +3,7 @@ @BUILDDIR@src/access/viraccessapicheckqemu.c @BUILDDIR@src/admin/admin_client.h @BUILDDIR@src/admin/admin_server_dispatch_stubs.h +@BUILDDIR@src/conf/domain_conf.generated.c @BUILDDIR@src/conf/network_conf.generated.c @BUILDDIR@src/remote/remote_client_bodies.h @BUILDDIR@src/remote/remote_daemon_dispatch_stubs.h diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 72ac4f4..c1f8847 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1826,8 +1826,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - VIR_FREE(def->data.sdl.display); - VIR_FREE(def->data.sdl.xauth); + virDomainGraphicsSDLDefClear(&def->data.sdl); break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: @@ -14514,54 +14513,6 @@ virDomainGraphicsDefParseXMLVNC(virDomainGraphicsDefPtr def, } -static int -virDomainGraphicsDefParseXMLSDL(virDomainGraphicsDefPtr def, - xmlNodePtr node, - xmlXPathContextPtr ctxt) -{ - VIR_XPATH_NODE_AUTORESTORE(ctxt) - int enableVal; - xmlNodePtr glNode; - g_autofree char *fullscreen = virXMLPropString(node, "fullscreen"); - g_autofree char *enable = NULL; - - ctxt->node = node; - - if (fullscreen != NULL) { - if (virStringParseYesNo(fullscreen, &def->data.sdl.fullscreen) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown fullscreen value '%s'"), fullscreen); - return -1; - } - } else { - def->data.sdl.fullscreen = false; - } - - def->data.sdl.xauth = virXMLPropString(node, "xauth"); - def->data.sdl.display = virXMLPropString(node, "display"); - - glNode = virXPathNode("./gl", ctxt); - if (glNode) { - enable = virXMLPropString(glNode, "enable"); - if (!enable) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("sdl gl element missing enable")); - return -1; - } - - enableVal = virTristateBoolTypeFromString(enable); - if (enableVal < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown enable value '%s'"), enable); - return -1; - } - def->data.sdl.gl = enableVal; - } - - return 0; -} - - static int virDomainGraphicsDefParseXMLRDP(virDomainGraphicsDefPtr def, xmlNodePtr node, @@ -14990,7 +14941,7 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - if (virDomainGraphicsDefParseXMLSDL(def, node, ctxt) < 0) + if (virDomainGraphicsSDLDefParseXML(node, &def->data.sdl, NULL, def, NULL) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3d14aa1..13d2d4f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1672,11 +1672,11 @@ struct _virDomainGraphicsListenDef { bool autoGenerated; }; -struct _virDomainGraphicsSDLDef { - char *display; - char *xauth; - bool fullscreen; - virTristateBool gl; +struct _virDomainGraphicsSDLDef { /* genparse */ + char *display; /* xmlattr */ + char *xauth; /* xmlattr */ + bool fullscreen; /* xmlattr */ + virTristateBool gl; /* xmlattr:gl/enable */ }; struct _virDomainGraphicsVNCDef { @@ -3916,3 +3916,5 @@ virHostdevIsMdevDevice(const virDomainHostdevDef *hostdev) bool virHostdevIsVFIODevice(const virDomainHostdevDef *hostdev) ATTRIBUTE_NONNULL(1); + +#include "domain_conf.generated.h" diff --git a/src/conf/meson.build b/src/conf/meson.build index 0619ca6..2796520 100644 --- a/src/conf/meson.build +++ b/src/conf/meson.build @@ -1,5 +1,6 @@ xmlgen_input = [ 'network_conf.h', + 'domain_conf.h', ] xmlgen_output = [] -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 20 +++++--------------- src/conf/domain_conf.h | 2 +- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c1f8847..544b984 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -28266,27 +28266,17 @@ virDomainGraphicsDefFormat(virBufferPtr buf, break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - if (def->data.sdl.display) - virBufferEscapeString(buf, " display='%s'", - def->data.sdl.display); - - if (def->data.sdl.xauth) - virBufferEscapeString(buf, " xauth='%s'", - def->data.sdl.xauth); - if (def->data.sdl.fullscreen) - virBufferAddLit(buf, " fullscreen='yes'"); + if (virDomainGraphicsSDLDefFormatAttr(buf, &def->data.sdl, def, NULL) < 0) + return -1; - if (!children && def->data.sdl.gl != VIR_TRISTATE_BOOL_ABSENT) { + if (!children && virDomainGraphicsSDLDefCheckElem(&def->data.sdl, def, NULL)) { virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); children = true; } - if (def->data.sdl.gl != VIR_TRISTATE_BOOL_ABSENT) { - virBufferAsprintf(buf, "<gl enable='%s'", - virTristateBoolTypeToString(def->data.sdl.gl)); - virBufferAddLit(buf, "/>\n"); - } + if (virDomainGraphicsSDLDefFormatElem(buf, &def->data.sdl, def, NULL) < 0) + return -1; break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 13d2d4f..e64a284 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1672,7 +1672,7 @@ struct _virDomainGraphicsListenDef { bool autoGenerated; }; -struct _virDomainGraphicsSDLDef { /* genparse */ +struct _virDomainGraphicsSDLDef { /* genparse, genformat:separate */ char *display; /* xmlattr */ char *xauth; /* xmlattr */ bool fullscreen; /* xmlattr */ -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 92 +++++------------------------------------- src/conf/domain_conf.h | 13 +++--- 2 files changed, 18 insertions(+), 87 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 544b984..1fd2a06 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1788,17 +1788,6 @@ bool virDomainObjTaint(virDomainObjPtr obj, } -static void -virDomainGraphicsAuthDefClear(virDomainGraphicsAuthDefPtr def) -{ - if (!def) - return; - - VIR_FREE(def->passwd); - - /* Don't free def */ -} - static void virDomainGraphicsListenDefClear(virDomainGraphicsListenDefPtr def) { @@ -14129,75 +14118,6 @@ virDomainTimerDefParseXML(xmlNodePtr node, } -static int -virDomainGraphicsAuthDefParseXML(xmlNodePtr node, - virDomainGraphicsAuthDefPtr def, - int type) -{ - g_autofree char *validTo = NULL; - g_autofree char *connected = virXMLPropString(node, "connected"); - - def->passwd = virXMLPropString(node, "passwd"); - - if (!def->passwd) - return 0; - - validTo = virXMLPropString(node, "passwdValidTo"); - if (validTo) { - g_autoptr(GDateTime) then = NULL; - g_autoptr(GTimeZone) tz = g_time_zone_new_utc(); - char *tmp; - int year, mon, mday, hour, min, sec; - - /* Expect: YYYY-MM-DDTHH:MM:SS (%d-%d-%dT%d:%d:%d) eg 2010-11-28T14:29:01 */ - if (/* year */ - virStrToLong_i(validTo, &tmp, 10, &year) < 0 || *tmp != '-' || - /* month */ - virStrToLong_i(tmp+1, &tmp, 10, &mon) < 0 || *tmp != '-' || - /* day */ - virStrToLong_i(tmp+1, &tmp, 10, &mday) < 0 || *tmp != 'T' || - /* hour */ - virStrToLong_i(tmp+1, &tmp, 10, &hour) < 0 || *tmp != ':' || - /* minute */ - virStrToLong_i(tmp+1, &tmp, 10, &min) < 0 || *tmp != ':' || - /* second */ - virStrToLong_i(tmp+1, &tmp, 10, &sec) < 0 || *tmp != '\0') { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse password validity time '%s', expect YYYY-MM-DDTHH:MM:SS"), - validTo); - VIR_FREE(def->passwd); - return -1; - } - - then = g_date_time_new(tz, year, mon, mday, hour, min, sec); - def->validTo = (time_t)g_date_time_to_unix(then); - def->expires = true; - } - - if (connected) { - int action = virDomainGraphicsAuthConnectedTypeFromString(connected); - if (action <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown connected value %s"), - connected); - return -1; - } - - /* VNC supports connected='keep' only */ - if (type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - action != VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_KEEP) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("VNC supports connected='keep' only")); - return -1; - } - - def->connected = action; - } - - return 0; -} - - /** * virDomainGraphicsListenDefParseXML: * @def: listen def pointer to be filled @@ -14506,9 +14426,17 @@ virDomainGraphicsDefParseXMLVNC(virDomainGraphicsDefPtr def, def->data.vnc.keymap = virXMLPropString(node, "keymap"); if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth, - def->type) < 0) + NULL, def, NULL) < 0) return -1; + /* VNC supports connected='keep' only */ + if (def->data.vnc.auth.connected && + def->data.vnc.auth.connected != VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_KEEP) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("VNC supports connected='keep' only")); + return -1; + } + return 0; } @@ -14644,7 +14572,7 @@ virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def, def->data.spice.keymap = virXMLPropString(node, "keymap"); if (virDomainGraphicsAuthDefParseXML(node, &def->data.spice.auth, - def->type) < 0) + NULL, def, NULL) < 0) return -1; cur = node->children; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index e64a284..616cbf4 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1573,11 +1573,14 @@ typedef enum { VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_LAST } virDomainGraphicsAuthConnectedType; -struct _virDomainGraphicsAuthDef { - char *passwd; - bool expires; /* Whether there is an expiry time set */ - time_t validTo; /* seconds since epoch */ - int connected; /* action if connected */ +struct _virDomainGraphicsAuthDef { /* genparse */ + char *passwd; /* xmlattr */ + /* Whether there is an expiry time set */ + bool expires; + /* seconds since epoch */ + time_t validTo; /* xmlattr:passwdValidTo, specified:expires */ + /* action if connected */ + virDomainGraphicsAuthConnectedType connected; /* xmlattr */ }; typedef enum { -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 30 ++---------------------------- src/conf/domain_conf.h | 4 ++-- 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1fd2a06..be07b40 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -28000,32 +28000,6 @@ virDomainTimerDefFormat(virBufferPtr buf, return 0; } -static void -virDomainGraphicsAuthDefFormatAttr(virBufferPtr buf, - virDomainGraphicsAuthDefPtr def, - unsigned int flags) -{ - if (!def->passwd) - return; - - if (flags & VIR_DOMAIN_DEF_FORMAT_SECURE) - virBufferEscapeString(buf, " passwd='%s'", - def->passwd); - - if (def->expires) { - g_autoptr(GDateTime) then = NULL; - g_autofree char *thenstr = NULL; - - then = g_date_time_new_from_unix_utc(def->validTo); - thenstr = g_date_time_format(then, "%Y-%m-%dT%H:%M:%S"); - virBufferAsprintf(buf, " passwdValidTo='%s'", thenstr); - } - - if (def->connected) - virBufferEscapeString(buf, " connected='%s'", - virDomainGraphicsAuthConnectedTypeToString(def->connected)); -} - static void virDomainGraphicsListenDefFormat(virBufferPtr buf, @@ -28190,7 +28164,7 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virDomainGraphicsVNCSharePolicyTypeToString( def->data.vnc.sharePolicy)); - virDomainGraphicsAuthDefFormatAttr(buf, &def->data.vnc.auth, flags); + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.vnc.auth, def, &flags); break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: @@ -28292,7 +28266,7 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virBufferAsprintf(buf, " defaultMode='%s'", virDomainGraphicsSpiceChannelModeTypeToString(def->data.spice.defaultMode)); - virDomainGraphicsAuthDefFormatAttr(buf, &def->data.spice.auth, flags); + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.spice.auth, def, &flags); break; case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 616cbf4..9724b02 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1573,8 +1573,8 @@ typedef enum { VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_LAST } virDomainGraphicsAuthConnectedType; -struct _virDomainGraphicsAuthDef { /* genparse */ - char *passwd; /* xmlattr */ +struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ + char *passwd; /* xmlattr, formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE */ /* Whether there is an expiry time set */ bool expires; /* seconds since epoch */ -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 77 +++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index be07b40..aedf278 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14356,10 +14356,55 @@ virDomainGraphicsListensParseXML(virDomainGraphicsDefPtr def, } +static int +virDomainGraphicsVNCDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, + virDomainGraphicsVNCDefPtr def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque, + const char *port, + const char *websocket G_GNUC_UNUSED, + const char *websocketGenerated G_GNUC_UNUSED, + const char *autoport, + const char *sharePolicy G_GNUC_UNUSED) +{ + unsigned int flags = *((unsigned int *) opaque); + + /* Legacy compat syntax, used -1 for auto-port */ + if (port) { + if (def->port == -1) { + if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) + def->port = 0; + if (autoport == NULL) + def->autoport = true; + } + } else { + def->port = 0; + if (autoport == NULL) + def->autoport = true; + } + + if (autoport) { + if (def->autoport && flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) + def->port = 0; + } + + /* VNC supports connected='keep' only */ + if (def->auth.connected && + def->auth.connected != VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_KEEP) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("VNC supports connected='keep' only")); + return -1; + } + + return 0; +} + + static int virDomainGraphicsDefParseXMLVNC(virDomainGraphicsDefPtr def, xmlNodePtr node, - xmlXPathContextPtr ctxt, + xmlXPathContextPtr ctxt G_GNUC_UNUSED, unsigned int flags) { g_autofree char *port = virXMLPropString(node, "port"); @@ -14368,33 +14413,17 @@ virDomainGraphicsDefParseXMLVNC(virDomainGraphicsDefPtr def, g_autofree char *sharePolicy = virXMLPropString(node, "sharePolicy"); g_autofree char *autoport = virXMLPropString(node, "autoport"); - if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) - return -1; - if (port) { if (virStrToLong_i(port, NULL, 10, &def->data.vnc.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse vnc port %s"), port); return -1; } - /* Legacy compat syntax, used -1 for auto-port */ - if (def->data.vnc.port == -1) { - if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) - def->data.vnc.port = 0; - def->data.vnc.autoport = true; - } - } else { - def->data.vnc.port = 0; - def->data.vnc.autoport = true; } - if (autoport) { + if (autoport) ignore_value(virStringParseYesNo(autoport, &def->data.vnc.autoport)); - if (def->data.vnc.autoport && flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) - def->data.vnc.port = 0; - } - if (websocket) { if (virStrToLong_i(websocket, NULL, 10, @@ -14429,13 +14458,11 @@ virDomainGraphicsDefParseXMLVNC(virDomainGraphicsDefPtr def, NULL, def, NULL) < 0) return -1; - /* VNC supports connected='keep' only */ - if (def->data.vnc.auth.connected && - def->data.vnc.auth.connected != VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_KEEP) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("VNC supports connected='keep' only")); + if (virDomainGraphicsVNCDefParseXMLHook(node, &def->data.vnc, NULL, def, &flags, + port, websocket, + websocketGenerated, autoport, + sharePolicy) < 0) return -1; - } return 0; } @@ -14867,6 +14894,8 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, case VIR_DOMAIN_GRAPHICS_TYPE_VNC: if (virDomainGraphicsDefParseXMLVNC(def, node, ctxt, flags) < 0) goto error; + if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) + goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: if (virDomainGraphicsSDLDefParseXML(node, &def->data.sdl, NULL, def, NULL) < 0) -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 71 ++---------------------------------------- src/conf/domain_conf.h | 16 +++++----- 2 files changed, 10 insertions(+), 77 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index aedf278..54b4da9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14356,7 +14356,7 @@ virDomainGraphicsListensParseXML(virDomainGraphicsDefPtr def, } -static int +int virDomainGraphicsVNCDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virDomainGraphicsVNCDefPtr def, const char *instname G_GNUC_UNUSED, @@ -14401,73 +14401,6 @@ virDomainGraphicsVNCDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virDomainGraphicsDefParseXMLVNC(virDomainGraphicsDefPtr def, - xmlNodePtr node, - xmlXPathContextPtr ctxt G_GNUC_UNUSED, - unsigned int flags) -{ - g_autofree char *port = virXMLPropString(node, "port"); - g_autofree char *websocket = virXMLPropString(node, "websocket"); - g_autofree char *websocketGenerated = virXMLPropString(node, "websocketGenerated"); - g_autofree char *sharePolicy = virXMLPropString(node, "sharePolicy"); - g_autofree char *autoport = virXMLPropString(node, "autoport"); - - if (port) { - if (virStrToLong_i(port, NULL, 10, &def->data.vnc.port) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse vnc port %s"), port); - return -1; - } - } - - if (autoport) - ignore_value(virStringParseYesNo(autoport, &def->data.vnc.autoport)); - - if (websocket) { - if (virStrToLong_i(websocket, - NULL, 10, - &def->data.vnc.websocket) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse vnc WebSocket port %s"), websocket); - return -1; - } - } - - if (websocketGenerated) - ignore_value(virStringParseYesNo(websocketGenerated, - &def->data.vnc.websocketGenerated)); - - if (sharePolicy) { - int policy = - virDomainGraphicsVNCSharePolicyTypeFromString(sharePolicy); - - if (policy < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown vnc display sharing policy '%s'"), - sharePolicy); - return -1; - } else { - def->data.vnc.sharePolicy = policy; - } - } - - def->data.vnc.keymap = virXMLPropString(node, "keymap"); - - if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth, - NULL, def, NULL) < 0) - return -1; - - if (virDomainGraphicsVNCDefParseXMLHook(node, &def->data.vnc, NULL, def, &flags, - port, websocket, - websocketGenerated, autoport, - sharePolicy) < 0) - return -1; - - return 0; -} - - static int virDomainGraphicsDefParseXMLRDP(virDomainGraphicsDefPtr def, xmlNodePtr node, @@ -14892,7 +14825,7 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, switch (def->type) { case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - if (virDomainGraphicsDefParseXMLVNC(def, node, ctxt, flags) < 0) + if (virDomainGraphicsVNCDefParseXML(node, &def->data.vnc, NULL, def, &flags) < 0) goto error; if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) goto error; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 9724b02..e262246 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1682,15 +1682,15 @@ struct _virDomainGraphicsSDLDef { /* genparse, genformat:separate */ virTristateBool gl; /* xmlattr:gl/enable */ }; -struct _virDomainGraphicsVNCDef { - int port; +struct _virDomainGraphicsVNCDef { /* genparse:withhook */ + int port; /* xmlattr */ bool portReserved; - int websocket; - bool websocketGenerated; - bool autoport; - char *keymap; - virDomainGraphicsAuthDef auth; - int sharePolicy; + int websocket; /* xmlattr */ + bool websocketGenerated; /* xmlattr */ + bool autoport; /* xmlattr */ + char *keymap; /* xmlattr */ + virDomainGraphicsAuthDef auth; /* xmlgroup */ + virDomainGraphicsVNCSharePolicy sharePolicy; /* xmlattr */ }; struct _virDomainGraphicsRDPDef { -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 164 ++++++++++++++++++++++++++--------------- src/conf/domain_conf.h | 14 ++-- 2 files changed, 114 insertions(+), 64 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 54b4da9..ebd8ac3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14363,9 +14363,9 @@ virDomainGraphicsVNCDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, void *parent G_GNUC_UNUSED, void *opaque, const char *port, + const char *autoport, const char *websocket G_GNUC_UNUSED, const char *websocketGenerated G_GNUC_UNUSED, - const char *autoport, const char *sharePolicy G_GNUC_UNUSED) { unsigned int flags = *((unsigned int *) opaque); @@ -28049,6 +28049,110 @@ virDomainSpiceGLDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def) virBufferAddLit(buf, "/>\n"); } +bool +virDomainGraphicsVNCDefCheckAttrHook(const virDomainGraphicsVNCDef *def G_GNUC_UNUSED, + const void *parent, + void *opaque, + bool value) +{ + bool ret = false; + virDomainGraphicsDefPtr graphics = (virDomainGraphicsDefPtr) parent; + virDomainGraphicsListenDefPtr glisten = virDomainGraphicsGetListen(graphics, 0); + unsigned int flags = 0; + if (opaque) + flags = *((unsigned int *) opaque); + + if (!glisten) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing listen element for graphics")); + return false; + } + + switch (glisten->type) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: + if (glisten->socket && + !((glisten->autoGenerated || glisten->fromConfig) && + (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE))) { + ret = true; + } + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + /* Always output 'autoport' under these conditions */ + ret = true; + break; + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: + break; + } + + return ret || value; +} + +int +virDomainGraphicsVNCDefFormatAttrHook(const virDomainGraphicsVNCDef *def, + const void *parent, + const void *opaque, + virBufferPtr portBuf, + virBufferPtr autoportBuf, + virBufferPtr websocketBuf, + virBufferPtr listenBuf, + virBufferPtr socketBuf) +{ + virDomainGraphicsDefPtr graphics = (virDomainGraphicsDefPtr) parent; + virDomainGraphicsListenDefPtr glisten = virDomainGraphicsGetListen(graphics, 0); + unsigned int flags = 0; + if (opaque) + flags = *((unsigned int *) opaque); + + if (!glisten) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing listen element for graphics")); + return -1; + } + + switch (glisten->type) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: + /* To not break migration we shouldn't print the 'socket' attribute + * if it's auto-generated or if it's based on config option from + * qemu.conf. If the socket is provided by user we need to print it + * into migratable XML. */ + if (glisten->socket && + !((glisten->autoGenerated || glisten->fromConfig) && + (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE))) { + virBufferEscapeString(socketBuf, " socket='%s'", glisten->socket); + } + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + if (def->port && + (!def->autoport || !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE))) + virBufferAsprintf(portBuf, " port='%d'", + def->port); + else if (def->autoport) + virBufferAddLit(portBuf, " port='-1'"); + + virBufferAsprintf(autoportBuf, " autoport='%s'", + def->autoport ? "yes" : "no"); + + if (def->websocketGenerated && + (flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) + virBufferAddLit(websocketBuf, " websocket='-1'"); + else if (def->websocket) + virBufferAsprintf(websocketBuf, " websocket='%d'", def->websocket); + + virDomainGraphicsListenDefFormatAddr(listenBuf, glisten, flags); + break; + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: + break; + } + + return 0; +} + static int virDomainGraphicsDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def, @@ -28069,64 +28173,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, switch (def->type) { case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - if (!glisten) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing listen element for graphics")); + if (virDomainGraphicsVNCDefFormatAttr(buf, &def->data.vnc, def, &flags) < 0) return -1; - } - - switch (glisten->type) { - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: - /* To not break migration we shouldn't print the 'socket' attribute - * if it's auto-generated or if it's based on config option from - * qemu.conf. If the socket is provided by user we need to print it - * into migratable XML. */ - if (glisten->socket && - !((glisten->autoGenerated || glisten->fromConfig) && - (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE))) { - virBufferEscapeString(buf, " socket='%s'", glisten->socket); - } - break; - - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: - if (def->data.vnc.port && - (!def->data.vnc.autoport || !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE))) - virBufferAsprintf(buf, " port='%d'", - def->data.vnc.port); - else if (def->data.vnc.autoport) - virBufferAddLit(buf, " port='-1'"); - - virBufferAsprintf(buf, " autoport='%s'", - def->data.vnc.autoport ? "yes" : "no"); - - if (def->data.vnc.websocketGenerated && - (flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) - virBufferAddLit(buf, " websocket='-1'"); - else if (def->data.vnc.websocket) - virBufferAsprintf(buf, " websocket='%d'", def->data.vnc.websocket); - - if (flags & VIR_DOMAIN_DEF_FORMAT_STATUS) - virBufferAsprintf(buf, " websocketGenerated='%s'", - def->data.vnc.websocketGenerated ? "yes" : "no"); - - virDomainGraphicsListenDefFormatAddr(buf, glisten, flags); - break; - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: - break; - } - - if (def->data.vnc.keymap) - virBufferEscapeString(buf, " keymap='%s'", - def->data.vnc.keymap); - - if (def->data.vnc.sharePolicy) - virBufferAsprintf(buf, " sharePolicy='%s'", - virDomainGraphicsVNCSharePolicyTypeToString( - def->data.vnc.sharePolicy)); - - virDomainGraphicsAuthDefFormatAttr(buf, &def->data.vnc.auth, def, &flags); break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index e262246..f4cc504 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1682,15 +1682,17 @@ struct _virDomainGraphicsSDLDef { /* genparse, genformat:separate */ virTristateBool gl; /* xmlattr:gl/enable */ }; -struct _virDomainGraphicsVNCDef { /* genparse:withhook */ - int port; /* xmlattr */ +struct _virDomainGraphicsVNCDef { /* genparse:withhook, genformat:separate */ + int port; /* xmlattr, formathook */ bool portReserved; - int websocket; /* xmlattr */ - bool websocketGenerated; /* xmlattr */ - bool autoport; /* xmlattr */ + bool autoport; /* xmlattr, formathook */ + int websocket; /* xmlattr, formathook */ + bool websocketGenerated; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ + char *_listen; /* xmlattr:listen, formathook */ + char *_socket; /* xmlattr:socket, formathook */ char *keymap; /* xmlattr */ - virDomainGraphicsAuthDef auth; /* xmlgroup */ virDomainGraphicsVNCSharePolicy sharePolicy; /* xmlattr */ + virDomainGraphicsAuthDef auth; /* xmlgroup */ }; struct _virDomainGraphicsRDPDef { -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 52 ++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ebd8ac3..8842f2a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14401,10 +14401,40 @@ virDomainGraphicsVNCDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } +static int +virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, + virDomainGraphicsRDPDefPtr def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque, + const char *port, + const char *autoport G_GNUC_UNUSED, + const char *replaceUser G_GNUC_UNUSED, + const char *multiUser G_GNUC_UNUSED) +{ + unsigned int flags = *((unsigned int *) opaque); + + if (port) { + /* Legacy compat syntax, used -1 for auto-port */ + if (def->port == -1) + def->autoport = true; + + } else { + def->port = 0; + def->autoport = true; + } + + if (def->autoport && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) + def->port = 0; + + return 0; +} + + static int virDomainGraphicsDefParseXMLRDP(virDomainGraphicsDefPtr def, xmlNodePtr node, - xmlXPathContextPtr ctxt, + xmlXPathContextPtr ctxt G_GNUC_UNUSED, unsigned int flags) { g_autofree char *port = virXMLPropString(node, "port"); @@ -14412,36 +14442,28 @@ virDomainGraphicsDefParseXMLRDP(virDomainGraphicsDefPtr def, g_autofree char *replaceUser = virXMLPropString(node, "replaceUser"); g_autofree char *multiUser = virXMLPropString(node, "multiUser"); - if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) - return -1; - if (port) { if (virStrToLong_i(port, NULL, 10, &def->data.rdp.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse rdp port %s"), port); return -1; } - /* Legacy compat syntax, used -1 for auto-port */ - if (def->data.rdp.port == -1) - def->data.rdp.autoport = true; - - } else { - def->data.rdp.port = 0; - def->data.rdp.autoport = true; } if (STREQ_NULLABLE(autoport, "yes")) def->data.rdp.autoport = true; - if (def->data.rdp.autoport && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) - def->data.rdp.port = 0; - if (STREQ_NULLABLE(replaceUser, "yes")) def->data.rdp.replaceUser = true; if (STREQ_NULLABLE(multiUser, "yes")) def->data.rdp.multiUser = true; + if (virDomainGraphicsRDPDefParseXMLHook(node, &def->data.rdp, + NULL, def, &flags, port, autoport, + replaceUser, multiUser) < 0) + return -1; + return 0; } @@ -14835,6 +14857,8 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) + goto error; if (virDomainGraphicsDefParseXMLRDP(def, node, ctxt, flags) < 0) goto error; break; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 41 ++--------------------------------------- src/conf/domain_conf.h | 10 +++++----- 2 files changed, 7 insertions(+), 44 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8842f2a..14bb4d3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14401,7 +14401,7 @@ virDomainGraphicsVNCDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int +int virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virDomainGraphicsRDPDefPtr def, const char *instname G_GNUC_UNUSED, @@ -14431,43 +14431,6 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virDomainGraphicsDefParseXMLRDP(virDomainGraphicsDefPtr def, - xmlNodePtr node, - xmlXPathContextPtr ctxt G_GNUC_UNUSED, - unsigned int flags) -{ - g_autofree char *port = virXMLPropString(node, "port"); - g_autofree char *autoport = virXMLPropString(node, "autoport"); - g_autofree char *replaceUser = virXMLPropString(node, "replaceUser"); - g_autofree char *multiUser = virXMLPropString(node, "multiUser"); - - if (port) { - if (virStrToLong_i(port, NULL, 10, &def->data.rdp.port) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse rdp port %s"), port); - return -1; - } - } - - if (STREQ_NULLABLE(autoport, "yes")) - def->data.rdp.autoport = true; - - if (STREQ_NULLABLE(replaceUser, "yes")) - def->data.rdp.replaceUser = true; - - if (STREQ_NULLABLE(multiUser, "yes")) - def->data.rdp.multiUser = true; - - if (virDomainGraphicsRDPDefParseXMLHook(node, &def->data.rdp, - NULL, def, &flags, port, autoport, - replaceUser, multiUser) < 0) - return -1; - - return 0; -} - - static int virDomainGraphicsDefParseXMLDesktop(virDomainGraphicsDefPtr def, xmlNodePtr node) @@ -14859,7 +14822,7 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, case VIR_DOMAIN_GRAPHICS_TYPE_RDP: if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) goto error; - if (virDomainGraphicsDefParseXMLRDP(def, node, ctxt, flags) < 0) + if (virDomainGraphicsRDPDefParseXML(node, &def->data.rdp, NULL, def, &flags) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index f4cc504..3cdb5f8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1695,11 +1695,11 @@ struct _virDomainGraphicsVNCDef { /* genparse:withhook, genformat:separate */ virDomainGraphicsAuthDef auth; /* xmlgroup */ }; -struct _virDomainGraphicsRDPDef { - int port; - bool autoport; - bool replaceUser; - bool multiUser; +struct _virDomainGraphicsRDPDef { /* genparse:withhook */ + int port; /* xmlattr */ + bool autoport; /* xmlattr */ + bool replaceUser; /* xmlattr */ + bool multiUser; /* xmlattr */ }; struct _virDomainGraphicsDesktopDef { -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 86 +++++++++++++++++++++++++++++------------- src/conf/domain_conf.h | 3 +- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 14bb4d3..7709d71 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -27995,6 +27995,28 @@ virDomainGraphicsListenDefFormat(virBufferPtr buf, } +static bool +virDomainGraphicsListenDefCheckAddr(virDomainGraphicsListenDefPtr glisten, + unsigned int flags) +{ + if (!glisten) + return false; + + if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE && glisten->fromConfig) + return false; + + if (glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK && + flags & (VIR_DOMAIN_DEF_FORMAT_INACTIVE | + VIR_DOMAIN_DEF_FORMAT_MIGRATABLE)) + return false; + + if (glisten->address) + return true; + + return false; +} + + /** * virDomainGraphicsListenDefFormatAddr: * @buf: buffer where the output XML is written @@ -28009,19 +28031,10 @@ virDomainGraphicsListenDefFormatAddr(virBufferPtr buf, virDomainGraphicsListenDefPtr glisten, unsigned int flags) { - if (!glisten) + if (!virDomainGraphicsListenDefCheckAddr(glisten, flags)) return; - if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE && glisten->fromConfig) - return; - - if (glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK && - flags & (VIR_DOMAIN_DEF_FORMAT_INACTIVE | - VIR_DOMAIN_DEF_FORMAT_MIGRATABLE)) - return; - - if (glisten->address) - virBufferAsprintf(buf, " listen='%s'", glisten->address); + virBufferAsprintf(buf, " listen='%s'", glisten->address); } static void @@ -28180,22 +28193,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: - if (def->data.rdp.port) - virBufferAsprintf(buf, " port='%d'", - def->data.rdp.port); - else if (def->data.rdp.autoport) - virBufferAddLit(buf, " port='0'"); - - if (def->data.rdp.autoport) - virBufferAddLit(buf, " autoport='yes'"); - - if (def->data.rdp.replaceUser) - virBufferAddLit(buf, " replaceUser='yes'"); - - if (def->data.rdp.multiUser) - virBufferAddLit(buf, " multiUser='yes'"); - - virDomainGraphicsListenDefFormatAddr(buf, glisten, flags); + if (virDomainGraphicsRDPDefFormatAttr(buf, &def->data.rdp, def, NULL)) + return -1; break; @@ -32943,3 +32942,36 @@ virHostdevIsVFIODevice(const virDomainHostdevDef *hostdev) hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; } + + +bool +virDomainGraphicsRDPDefCheckAttrHook(const virDomainGraphicsRDPDef *def G_GNUC_UNUSED, + const void *parent, + void *opaque, + bool value) +{ + virDomainGraphicsDefPtr graphic = (virDomainGraphicsDefPtr) parent; + virDomainGraphicsListenDefPtr glisten = &graphic->listens[0]; + unsigned int flags = 0; + if (opaque) + flags = *((unsigned int *) opaque); + + return virDomainGraphicsListenDefCheckAddr(glisten, flags) || value; +} + + +int +virDomainGraphicsRDPDefFormatAttrHook(const virDomainGraphicsRDPDef *def G_GNUC_UNUSED, + const void *parent, + const void *opaque, + virBufferPtr listenBuf) +{ + virDomainGraphicsDefPtr graphic = (virDomainGraphicsDefPtr) parent; + virDomainGraphicsListenDefPtr glisten = &graphic->listens[0]; + unsigned int flags = 0; + if (opaque) + flags = *((unsigned int *) opaque); + + virDomainGraphicsListenDefFormatAddr(listenBuf, glisten, flags); + return 0; +} diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3cdb5f8..1444257 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1695,11 +1695,12 @@ struct _virDomainGraphicsVNCDef { /* genparse:withhook, genformat:separate */ virDomainGraphicsAuthDef auth; /* xmlgroup */ }; -struct _virDomainGraphicsRDPDef { /* genparse:withhook */ +struct _virDomainGraphicsRDPDef { /* genparse:withhook, genformat:separate */ int port; /* xmlattr */ bool autoport; /* xmlattr */ bool replaceUser; /* xmlattr */ bool multiUser; /* xmlattr */ + char *_listen; /* xmlattr:listen, formathook */ }; struct _virDomainGraphicsDesktopDef { -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 24 +----------------------- src/conf/domain_conf.h | 6 +++--- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7709d71..ad129e9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14431,28 +14431,6 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virDomainGraphicsDefParseXMLDesktop(virDomainGraphicsDefPtr def, - xmlNodePtr node) -{ - g_autofree char *fullscreen = virXMLPropString(node, "fullscreen"); - - if (fullscreen != NULL) { - if (virStringParseYesNo(fullscreen, &def->data.desktop.fullscreen) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown fullscreen value '%s'"), fullscreen); - return -1; - } - } else { - def->data.desktop.fullscreen = false; - } - - def->data.desktop.display = virXMLPropString(node, "display"); - - return 0; -} - - static int virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def, xmlNodePtr node, @@ -14826,7 +14804,7 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: - if (virDomainGraphicsDefParseXMLDesktop(def, node) < 0) + if (virDomainGraphicsDesktopDefParseXML(node, &def->data.desktop, NULL, def, NULL) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 1444257..ec08bdd 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1703,9 +1703,9 @@ struct _virDomainGraphicsRDPDef { /* genparse:withhook, genformat:separate */ char *_listen; /* xmlattr:listen, formathook */ }; -struct _virDomainGraphicsDesktopDef { - char *display; - bool fullscreen; +struct _virDomainGraphicsDesktopDef { /* genparse */ + char *display; /* xmlattr */ + bool fullscreen; /* xmlattr */ }; struct _virDomainGraphicsSpiceDef { -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 8 ++------ src/conf/domain_conf.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ad129e9..bc42068 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -28177,12 +28177,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: - if (def->data.desktop.display) - virBufferEscapeString(buf, " display='%s'", - def->data.desktop.display); - - if (def->data.desktop.fullscreen) - virBufferAddLit(buf, " fullscreen='yes'"); + if (virDomainGraphicsDesktopDefFormatAttr(buf, &def->data.desktop, def, NULL) < 0) + return -1; break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ec08bdd..08af9e1 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1703,7 +1703,7 @@ struct _virDomainGraphicsRDPDef { /* genparse:withhook, genformat:separate */ char *_listen; /* xmlattr:listen, formathook */ }; -struct _virDomainGraphicsDesktopDef { /* genparse */ +struct _virDomainGraphicsDesktopDef { /* genparse, genformat:separate */ char *display; /* xmlattr */ bool fullscreen; /* xmlattr */ }; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 08af9e1..df84763 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1708,6 +1708,13 @@ struct _virDomainGraphicsDesktopDef { /* genparse, genformat:separate */ bool fullscreen; /* xmlattr */ }; +typedef struct _virSpiceChannelDef virSpiceChannelDef; +typedef virSpiceChannelDef *virSpiceChannelDefPtr; +struct _virSpiceChannelDef { /* genparse, genformat */ + virDomainGraphicsSpiceChannelName name; /* xmlattr */ + virDomainGraphicsSpiceChannelMode mode; /* xmlattr */ +}; + struct _virDomainGraphicsSpiceDef { int port; int tlsPort; @@ -1717,7 +1724,12 @@ struct _virDomainGraphicsSpiceDef { char *keymap; virDomainGraphicsAuthDef auth; bool autoport; + + /* The shadow member _channels helps to parse channels. */ + size_t n_channels; + virSpiceChannelDefPtr _channels; int channels[VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST]; + virDomainGraphicsSpiceChannelMode defaultMode; int image; int jpeg; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 46 +++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index bc42068..b3ec111 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14431,10 +14431,35 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } +static int +virDomainGraphicsSpiceDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, + virDomainGraphicsSpiceDefPtr def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque) +{ + unsigned int flags = 0; + if (opaque) + flags = *((unsigned int *) opaque); + + if (def->port == -1 && def->tlsPort == -1) { + /* Legacy compat syntax, used -1 for auto-port */ + def->autoport = true; + } + + if (def->autoport && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) { + def->port = 0; + def->tlsPort = 0; + } + + return 0; +} + + static int virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def, xmlNodePtr node, - xmlXPathContextPtr ctxt, + xmlXPathContextPtr ctxt G_GNUC_UNUSED, unsigned int flags) { xmlNodePtr cur; @@ -14444,9 +14469,6 @@ virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def, g_autofree char *autoport = virXMLPropString(node, "autoport"); g_autofree char *defaultMode = virXMLPropString(node, "defaultMode"); - if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) - return -1; - if (port) { if (virStrToLong_i(port, NULL, 10, &def->data.spice.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -14482,16 +14504,6 @@ virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def, def->data.spice.defaultMode = defaultModeVal; } - if (def->data.spice.port == -1 && def->data.spice.tlsPort == -1) { - /* Legacy compat syntax, used -1 for auto-port */ - def->data.spice.autoport = true; - } - - if (def->data.spice.autoport && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) { - def->data.spice.port = 0; - def->data.spice.tlsPort = 0; - } - def->data.spice.keymap = virXMLPropString(node, "keymap"); if (virDomainGraphicsAuthDefParseXML(node, &def->data.spice.auth, @@ -14701,6 +14713,10 @@ virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def, cur = cur->next; } + if (virDomainGraphicsSpiceDefParseXMLHook(node, &def->data.spice, NULL, + def, &flags) < 0) + return -1; + return 0; } @@ -14808,6 +14824,8 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) + goto error; if (virDomainGraphicsDefParseXMLSpice(def, node, ctxt, flags) < 0) goto error; break; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 272 ++--------------------------------------- src/conf/domain_conf.h | 37 +++--- 2 files changed, 26 insertions(+), 283 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index b3ec111..20d731b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -877,6 +877,7 @@ VIR_ENUM_IMPL(virDomainGraphicsVNCSharePolicy, VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST, + "none", "main", "display", "inputs", @@ -14431,13 +14432,14 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int +int virDomainGraphicsSpiceDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virDomainGraphicsSpiceDefPtr def, const char *instname G_GNUC_UNUSED, void *parent G_GNUC_UNUSED, void *opaque) { + size_t i; unsigned int flags = 0; if (opaque) flags = *((unsigned int *) opaque); @@ -14452,271 +14454,11 @@ virDomainGraphicsSpiceDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, def->tlsPort = 0; } - return 0; -} - - -static int -virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def, - xmlNodePtr node, - xmlXPathContextPtr ctxt G_GNUC_UNUSED, - unsigned int flags) -{ - xmlNodePtr cur; - int defaultModeVal; - g_autofree char *port = virXMLPropString(node, "port"); - g_autofree char *tlsPort = virXMLPropString(node, "tlsPort"); - g_autofree char *autoport = virXMLPropString(node, "autoport"); - g_autofree char *defaultMode = virXMLPropString(node, "defaultMode"); - - if (port) { - if (virStrToLong_i(port, NULL, 10, &def->data.spice.port) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse spice port %s"), port); - return -1; - } - } else { - def->data.spice.port = 0; - } - - if (tlsPort) { - if (virStrToLong_i(tlsPort, NULL, 10, &def->data.spice.tlsPort) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse spice tlsPort %s"), tlsPort); - return -1; - } - } else { - def->data.spice.tlsPort = 0; - } - - if (STREQ_NULLABLE(autoport, "yes")) - def->data.spice.autoport = true; - - def->data.spice.defaultMode = VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY; - - if (defaultMode) { - if ((defaultModeVal = virDomainGraphicsSpiceChannelModeTypeFromString(defaultMode)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown default spice channel mode %s"), - defaultMode); - return -1; - } - def->data.spice.defaultMode = defaultModeVal; - } - - def->data.spice.keymap = virXMLPropString(node, "keymap"); - - if (virDomainGraphicsAuthDefParseXML(node, &def->data.spice.auth, - NULL, def, NULL) < 0) - return -1; - - cur = node->children; - while (cur != NULL) { - if (cur->type == XML_ELEMENT_NODE) { - if (virXMLNodeNameEqual(cur, "channel")) { - int nameval, modeval; - g_autofree char *name = NULL; - g_autofree char *mode = NULL; - - name = virXMLPropString(cur, "name"); - mode = virXMLPropString(cur, "mode"); - - if (!name || !mode) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("spice channel missing name/mode")); - return -1; - } - - if ((nameval = virDomainGraphicsSpiceChannelNameTypeFromString(name)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown spice channel name %s"), - name); - return -1; - } - if ((modeval = virDomainGraphicsSpiceChannelModeTypeFromString(mode)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown spice channel mode %s"), - mode); - return -1; - } - - def->data.spice.channels[nameval] = modeval; - } else if (virXMLNodeNameEqual(cur, "image")) { - int compressionVal; - g_autofree char *compression = virXMLPropString(cur, "compression"); - - if (!compression) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("spice image missing compression")); - return -1; - } - - if ((compressionVal = - virDomainGraphicsSpiceImageCompressionTypeFromString(compression)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown spice image compression %s"), - compression); - return -1; - } - - def->data.spice.image = compressionVal; - } else if (virXMLNodeNameEqual(cur, "jpeg")) { - int compressionVal; - g_autofree char *compression = virXMLPropString(cur, "compression"); - - if (!compression) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("spice jpeg missing compression")); - return -1; - } - - if ((compressionVal = - virDomainGraphicsSpiceJpegCompressionTypeFromString(compression)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown spice jpeg compression %s"), - compression); - return -1; - } - - def->data.spice.jpeg = compressionVal; - } else if (virXMLNodeNameEqual(cur, "zlib")) { - int compressionVal; - g_autofree char *compression = virXMLPropString(cur, "compression"); - - if (!compression) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("spice zlib missing compression")); - return -1; - } - - if ((compressionVal = - virDomainGraphicsSpiceZlibCompressionTypeFromString(compression)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown spice zlib compression %s"), - compression); - return -1; - } - - def->data.spice.zlib = compressionVal; - } else if (virXMLNodeNameEqual(cur, "playback")) { - int compressionVal; - g_autofree char *compression = virXMLPropString(cur, "compression"); - - if (!compression) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("spice playback missing compression")); - return -1; - } - - if ((compressionVal = - virTristateSwitchTypeFromString(compression)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("unknown spice playback compression")); - return -1; - } - - def->data.spice.playback = compressionVal; - } else if (virXMLNodeNameEqual(cur, "streaming")) { - int modeVal; - g_autofree char *mode = virXMLPropString(cur, "mode"); - - if (!mode) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("spice streaming missing mode")); - return -1; - } - if ((modeVal = - virDomainGraphicsSpiceStreamingModeTypeFromString(mode)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("unknown spice streaming mode")); - return -1; - } - - def->data.spice.streaming = modeVal; - } else if (virXMLNodeNameEqual(cur, "clipboard")) { - int copypasteVal; - g_autofree char *copypaste = virXMLPropString(cur, "copypaste"); - - if (!copypaste) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("spice clipboard missing copypaste")); - return -1; - } - - if ((copypasteVal = - virTristateBoolTypeFromString(copypaste)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown copypaste value '%s'"), copypaste); - return -1; - } - - def->data.spice.copypaste = copypasteVal; - } else if (virXMLNodeNameEqual(cur, "filetransfer")) { - int enableVal; - g_autofree char *enable = virXMLPropString(cur, "enable"); - - if (!enable) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("spice filetransfer missing enable")); - return -1; - } - - if ((enableVal = - virTristateBoolTypeFromString(enable)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown enable value '%s'"), enable); - return -1; - } - - def->data.spice.filetransfer = enableVal; - } else if (virXMLNodeNameEqual(cur, "gl")) { - int enableVal; - g_autofree char *enable = virXMLPropString(cur, "enable"); - g_autofree char *rendernode = virXMLPropString(cur, "rendernode"); - - if (!enable) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("spice gl element missing enable")); - return -1; - } - - if ((enableVal = - virTristateBoolTypeFromString(enable)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown enable value '%s'"), enable); - return -1; - } - - def->data.spice.gl = enableVal; - def->data.spice.rendernode = g_steal_pointer(&rendernode); - - } else if (virXMLNodeNameEqual(cur, "mouse")) { - int modeVal; - g_autofree char *mode = virXMLPropString(cur, "mode"); - - if (!mode) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("spice mouse missing mode")); - return -1; - } - - if ((modeVal = virDomainGraphicsSpiceMouseModeTypeFromString(mode)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown mouse mode value '%s'"), - mode); - return -1; - } - - def->data.spice.mousemode = modeVal; - } - } - cur = cur->next; + for (i = 0; i < def->n_channels; i++) { + int nameval = def->_channels[i].name; + def->channels[nameval] = def->_channels[i].mode; } - if (virDomainGraphicsSpiceDefParseXMLHook(node, &def->data.spice, NULL, - def, &flags) < 0) - return -1; - return 0; } @@ -14826,7 +14568,7 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) goto error; - if (virDomainGraphicsDefParseXMLSpice(def, node, ctxt, flags) < 0) + if (virDomainGraphicsSpiceDefParseXML(node, &def->data.spice, NULL, def, &flags) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index df84763..f27f429 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1584,6 +1584,7 @@ struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ }; typedef enum { + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_NONE = 0, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MAIN, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_DISPLAY, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_INPUT, @@ -1715,31 +1716,31 @@ struct _virSpiceChannelDef { /* genparse, genformat */ virDomainGraphicsSpiceChannelMode mode; /* xmlattr */ }; -struct _virDomainGraphicsSpiceDef { - int port; - int tlsPort; +struct _virDomainGraphicsSpiceDef { /* genparse:concisehook */ + int port; /* xmlattr */ + int tlsPort; /* xmlattr */ bool portReserved; bool tlsPortReserved; - virDomainGraphicsSpiceMouseMode mousemode; - char *keymap; - virDomainGraphicsAuthDef auth; - bool autoport; + virDomainGraphicsSpiceMouseMode mousemode; /* xmlattr:mouse/mode */ + char *keymap; /* xmlattr */ + virDomainGraphicsAuthDef auth; /* xmlgroup */ + bool autoport; /* xmlattr */ /* The shadow member _channels helps to parse channels. */ size_t n_channels; - virSpiceChannelDefPtr _channels; + virSpiceChannelDefPtr _channels; /* xmlelem:channel, array */ int channels[VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST]; - virDomainGraphicsSpiceChannelMode defaultMode; - int image; - int jpeg; - int zlib; - int playback; - int streaming; - virTristateBool copypaste; - virTristateBool filetransfer; - virTristateBool gl; - char *rendernode; + virDomainGraphicsSpiceChannelMode defaultMode; /* xmlattr */ + virDomainGraphicsSpiceImageCompression image; /* xmlattr:image/compression */ + virDomainGraphicsSpiceJpegCompression jpeg; /* xmlattr:jpeg/compression */ + virDomainGraphicsSpiceZlibCompression zlib; /* xmlattr:zlib/compression */ + virTristateSwitch playback; /* xmlattr:playback/compression */ + virDomainGraphicsSpiceStreamingMode streaming; /* xmlattr:streaming/mode */ + virTristateBool copypaste; /* xmlattr:clipboard/copypaste */ + virTristateBool filetransfer; /* xmlattr:filetransfer/enable */ + virTristateBool gl; /* xmlattr:gl/enable */ + char *rendernode; /* xmlattr:gl/rendernode */ }; struct _virDomainGraphicsEGLHeadlessDef { -- 2.25.1

On Fri, Sep 04, 2020 at 11:35:29AM +0800, Shi Lei wrote:
Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 272 ++--------------------------------------- src/conf/domain_conf.h | 37 +++--- 2 files changed, 26 insertions(+), 283 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index b3ec111..20d731b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -877,6 +877,7 @@ VIR_ENUM_IMPL(virDomainGraphicsVNCSharePolicy,
VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST, + "none", "main", "display", "inputs", @@ -14431,13 +14432,14 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index df84763..f27f429 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1584,6 +1584,7 @@ struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ };
typedef enum { + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_NONE = 0,
I'm not sure why this extra enum field needs to be added ? IMHO we don't really want to have such extra values except in a few special cases where we need to track some "default" explicitly.
VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MAIN, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_DISPLAY, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_INPUT,
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 2020-12-05 at 02:17, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:35:29AM +0800, Shi Lei wrote:
Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 272 ++--------------------------------------- src/conf/domain_conf.h | 37 +++--- 2 files changed, 26 insertions(+), 283 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index b3ec111..20d731b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -877,6 +877,7 @@ VIR_ENUM_IMPL(virDomainGraphicsVNCSharePolicy, VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST, + "none", "main", "display", "inputs", @@ -14431,13 +14432,14 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index df84763..f27f429 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1584,6 +1584,7 @@ struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ }; typedef enum { + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_NONE = 0,
I'm not sure why this extra enum field needs to be added ?
IMHO we don't really want to have such extra values except in a few special cases where we need to track some "default" explicitly.
There're two forms of enum in the code. (1) Start with a 'default' or 'none' item, e.g., virDomainDiskIo The virDomainDiskIo has VIR_DOMAIN_DISK_IO_DEFAULT which is ZERO-indexed. When parsing it, the code is like: if ((tmp = virXMLPropString(cur, "io")) && (def->iomode = virDomainDiskIoTypeFromString(tmp)) <= 0) { virReportError(...); return -1; } It checks XXXTypeFromString() ** <= ** 0, because the form <... io='default'> is illegal. (2) Without a 'default' or 'none' value, e.g., virDomainDiskDevice When parsing it, the code is like: if ((tmp = virXMLPropString(node, "device")) && (def->device = virDomainDiskDeviceTypeFromString(tmp)) < 0) { virReportError(...); return -1; } It just checks XXXTypeFromString() ** < ** 0, because the enum's ZERO-Value item is valid. To handle these two situations, I add extra "default" for case #2 and unify the form of enums, so that it's easier to implement the generator. Another solution is to add an extra directive to indicate whether a enum has a 'default' item, so that the generator can decide between '<=' and '<'. Perhaps it is more safe and reliable. Regards, Shi Lei
VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MAIN, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_DISPLAY, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_INPUT,
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On Mon, Dec 07, 2020 at 03:47:39PM +0800, Shi Lei wrote:
On 2020-12-05 at 02:17, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:35:29AM +0800, Shi Lei wrote:
Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 272 ++--------------------------------------- src/conf/domain_conf.h | 37 +++--- 2 files changed, 26 insertions(+), 283 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index b3ec111..20d731b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -877,6 +877,7 @@ VIR_ENUM_IMPL(virDomainGraphicsVNCSharePolicy, VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST, + "none", "main", "display", "inputs", @@ -14431,13 +14432,14 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index df84763..f27f429 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1584,6 +1584,7 @@ struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ }; typedef enum { + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_NONE = 0,
I'm not sure why this extra enum field needs to be added ?
IMHO we don't really want to have such extra values except in a few special cases where we need to track some "default" explicitly.
There're two forms of enum in the code.
(1) Start with a 'default' or 'none' item, e.g., virDomainDiskIo
The virDomainDiskIo has VIR_DOMAIN_DISK_IO_DEFAULT which is ZERO-indexed.
When parsing it, the code is like:
if ((tmp = virXMLPropString(cur, "io")) && (def->iomode = virDomainDiskIoTypeFromString(tmp)) <= 0) { virReportError(...); return -1; }
It checks XXXTypeFromString() ** <= ** 0, because the form <... io='default'> is illegal.
(2) Without a 'default' or 'none' value, e.g., virDomainDiskDevice
When parsing it, the code is like:
if ((tmp = virXMLPropString(node, "device")) && (def->device = virDomainDiskDeviceTypeFromString(tmp)) < 0) { virReportError(...); return -1; }
It just checks XXXTypeFromString() ** < ** 0, because the enum's ZERO-Value item is valid.
To handle these two situations, I add extra "default" for case #2 and unify the form of enums, so that it's easier to implement the generator.
Another solution is to add an extra directive to indicate whether a enum has a 'default' item, so that the generator can decide between '<=' and '<'. Perhaps it is more safe and reliable.
Looks like there are about 200 enums, 43 use _NONE and 56 use _DEFAULT, and the remaining 100 use neither. Maybe we can do without the need to add a directive, if we change all cases of NONE to DEFAULT, and just make the generator look at whether _DEFAULT is present or not ? Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 2020-12-07 at 17:38, DanielP. Berrangé wrote:
On Mon, Dec 07, 2020 at 03:47:39PM +0800, Shi Lei wrote:
On 2020-12-05 at 02:17, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:35:29AM +0800, Shi Lei wrote:
Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 272 ++--------------------------------------- src/conf/domain_conf.h | 37 +++--- 2 files changed, 26 insertions(+), 283 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index b3ec111..20d731b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -877,6 +877,7 @@ VIR_ENUM_IMPL(virDomainGraphicsVNCSharePolicy, VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName, VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST, + "none", "main", "display", "inputs", @@ -14431,13 +14432,14 @@ virDomainGraphicsRDPDefParseXMLHook(xmlNodePtr node diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index df84763..f27f429 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1584,6 +1584,7 @@ struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ }; typedef enum { + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_NONE = 0,
I'm not sure why this extra enum field needs to be added ?
IMHO we don't really want to have such extra values except in a few special cases where we need to track some "default" explicitly.
There're two forms of enum in the code.
(1) Start with a 'default' or 'none' item, e.g., virDomainDiskIo
The virDomainDiskIo has VIR_DOMAIN_DISK_IO_DEFAULT which is ZERO-indexed.
When parsing it, the code is like:
if ((tmp = virXMLPropString(cur, "io")) && (def->iomode = virDomainDiskIoTypeFromString(tmp)) <= 0) { virReportError(...); return -1; }
It checks XXXTypeFromString() ** <= ** 0, because the form <... io='default'> is illegal.
(2) Without a 'default' or 'none' value, e.g., virDomainDiskDevice
When parsing it, the code is like:
if ((tmp = virXMLPropString(node, "device")) && (def->device = virDomainDiskDeviceTypeFromString(tmp)) < 0) { virReportError(...); return -1; }
It just checks XXXTypeFromString() ** < ** 0, because the enum's ZERO-Value item is valid.
To handle these two situations, I add extra "default" for case #2 and unify the form of enums, so that it's easier to implement the generator.
Another solution is to add an extra directive to indicate whether a enum has a 'default' item, so that the generator can decide between '<=' and '<'. Perhaps it is more safe and reliable.
Looks like there are about 200 enums, 43 use _NONE and 56 use _DEFAULT, and the remaining 100 use neither.
Maybe we can do without the need to add a directive, if we change all cases of NONE to DEFAULT, and just make the generator look at whether _DEFAULT is present or not ?
Hmm. I try it. Shi Lei
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 206 ++++++++++++++++++----------------------- src/conf/domain_conf.h | 9 +- 2 files changed, 96 insertions(+), 119 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 20d731b..88e324e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -27775,18 +27775,6 @@ virDomainGraphicsListenDefFormatAddr(virBufferPtr buf, virBufferAsprintf(buf, " listen='%s'", glisten->address); } -static void -virDomainSpiceGLDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def) -{ - if (def->data.spice.gl == VIR_TRISTATE_BOOL_ABSENT) - return; - - virBufferAsprintf(buf, "<gl enable='%s'", - virTristateBoolTypeToString(def->data.spice.gl)); - virBufferEscapeString(buf, " rendernode='%s'", def->data.spice.rendernode); - virBufferAddLit(buf, "/>\n"); -} - bool virDomainGraphicsVNCDefCheckAttrHook(const virDomainGraphicsVNCDef *def G_GNUC_UNUSED, const void *parent, @@ -27896,7 +27884,6 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def, unsigned int flags) { - virDomainGraphicsListenDefPtr glisten = virDomainGraphicsGetListen(def, 0); const char *type = virDomainGraphicsTypeToString(def->type); bool children = false; size_t i; @@ -27943,60 +27930,9 @@ virDomainGraphicsDefFormat(virBufferPtr buf, break; case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: - if (!glisten) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("missing listen element for spice graphics")); + if (virDomainGraphicsSpiceDefFormatAttr(buf, &def->data.spice, def, &flags) < 0) return -1; - } - - switch (glisten->type) { - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: - if (def->data.spice.port) - virBufferAsprintf(buf, " port='%d'", - def->data.spice.port); - - if (def->data.spice.tlsPort) - virBufferAsprintf(buf, " tlsPort='%d'", - def->data.spice.tlsPort); - - virBufferAsprintf(buf, " autoport='%s'", - def->data.spice.autoport ? "yes" : "no"); - - virDomainGraphicsListenDefFormatAddr(buf, glisten, flags); - break; - - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: - if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) - virBufferAddStr(buf, " autoport='no'"); - break; - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: - /* If socket is auto-generated based on config option we don't - * add any listen element into migratable XML because the original - * listen type is "address". - * We need to set autoport to make sure that libvirt on destination - * will parse it as listen type "address", without autoport it is - * parsed as listen type "none". */ - if ((flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) && - glisten->fromConfig) { - virBufferAddStr(buf, " autoport='yes'"); - } - break; - - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: - break; - } - - if (def->data.spice.keymap) - virBufferEscapeString(buf, " keymap='%s'", - def->data.spice.keymap); - - if (def->data.spice.defaultMode != VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY) - virBufferAsprintf(buf, " defaultMode='%s'", - virDomainGraphicsSpiceChannelModeTypeToString(def->data.spice.defaultMode)); - - virDomainGraphicsAuthDefFormatAttr(buf, &def->data.spice.auth, def, &flags); break; case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: @@ -28053,56 +27989,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, } if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { - for (i = 0; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST; i++) { - int mode = def->data.spice.channels[i]; - if (mode == VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY) - continue; - - if (!children) { - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 2); - children = true; - } - - virBufferAsprintf(buf, "<channel name='%s' mode='%s'/>\n", - virDomainGraphicsSpiceChannelNameTypeToString(i), - virDomainGraphicsSpiceChannelModeTypeToString(mode)); - } - if (!children && (def->data.spice.image || def->data.spice.jpeg || - def->data.spice.zlib || def->data.spice.playback || - def->data.spice.streaming || def->data.spice.copypaste || - def->data.spice.mousemode || def->data.spice.filetransfer || - def->data.spice.gl)) { - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 2); - children = true; - } - if (def->data.spice.image) - virBufferAsprintf(buf, "<image compression='%s'/>\n", - virDomainGraphicsSpiceImageCompressionTypeToString(def->data.spice.image)); - if (def->data.spice.jpeg) - virBufferAsprintf(buf, "<jpeg compression='%s'/>\n", - virDomainGraphicsSpiceJpegCompressionTypeToString(def->data.spice.jpeg)); - if (def->data.spice.zlib) - virBufferAsprintf(buf, "<zlib compression='%s'/>\n", - virDomainGraphicsSpiceZlibCompressionTypeToString(def->data.spice.zlib)); - if (def->data.spice.playback) - virBufferAsprintf(buf, "<playback compression='%s'/>\n", - virTristateSwitchTypeToString(def->data.spice.playback)); - if (def->data.spice.streaming) - virBufferAsprintf(buf, "<streaming mode='%s'/>\n", - virDomainGraphicsSpiceStreamingModeTypeToString(def->data.spice.streaming)); - if (def->data.spice.mousemode) - virBufferAsprintf(buf, "<mouse mode='%s'/>\n", - virDomainGraphicsSpiceMouseModeTypeToString(def->data.spice.mousemode)); - if (def->data.spice.copypaste) - virBufferAsprintf(buf, "<clipboard copypaste='%s'/>\n", - virTristateBoolTypeToString(def->data.spice.copypaste)); - if (def->data.spice.filetransfer) - virBufferAsprintf(buf, "<filetransfer enable='%s'/>\n", - virTristateBoolTypeToString(def->data.spice.filetransfer)); - - virDomainSpiceGLDefFormat(buf, def); + if (virDomainGraphicsSpiceDefFormatElem(buf, &def->data.spice, def, NULL) < 0) + return -1; } if (children) { @@ -32694,6 +32582,45 @@ virDomainGraphicsRDPDefCheckAttrHook(const virDomainGraphicsRDPDef *def G_GNUC_U } +bool +virDomainGraphicsSpiceDefCheckAttrHook(const virDomainGraphicsSpiceDef *def G_GNUC_UNUSED, + const void *parent, + void *opaque, + bool value) +{ + bool ret = false; + virDomainGraphicsDefPtr graphic = (virDomainGraphicsDefPtr) parent; + virDomainGraphicsListenDefPtr glisten = &graphic->listens[0]; + unsigned int flags = 0; + if (opaque) + flags = *((unsigned int *) opaque); + + switch (glisten->type) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + ret = true; + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) + ret = true; + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: + if ((flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) && + glisten->fromConfig) { + ret = true; + } + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: + break; + } + + return ret || value; +} + + int virDomainGraphicsRDPDefFormatAttrHook(const virDomainGraphicsRDPDef *def G_GNUC_UNUSED, const void *parent, @@ -32707,5 +32634,54 @@ virDomainGraphicsRDPDefFormatAttrHook(const virDomainGraphicsRDPDef *def G_GNUC_ flags = *((unsigned int *) opaque); virDomainGraphicsListenDefFormatAddr(listenBuf, glisten, flags); + + return 0; +} + + +int +virDomainGraphicsSpiceDefFormatAttrHook(const virDomainGraphicsSpiceDef *def, + const void *parent, + const void *opaque, + virBufferPtr autoportBuf, + virBufferPtr listenBuf) +{ + virDomainGraphicsDefPtr graphic = (virDomainGraphicsDefPtr) parent; + virDomainGraphicsListenDefPtr glisten = &graphic->listens[0]; + unsigned int flags = 0; + if (opaque) + flags = *((unsigned int *) opaque); + + switch (glisten->type) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + virBufferAsprintf(autoportBuf, " autoport='%s'", + def->autoport ? "yes" : "no"); + + virDomainGraphicsListenDefFormatAddr(listenBuf, glisten, flags); + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) + virBufferAddStr(autoportBuf, " autoport='no'"); + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: + /* If socket is auto-generated based on config option we don't + * add any listen element into migratable XML because the original + * listen type is "address". + * We need to set autoport to make sure that libvirt on destination + * will parse it as listen type "address", without autoport it is + * parsed as listen type "none". */ + if ((flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) && + glisten->fromConfig) { + virBufferAddStr(autoportBuf, " autoport='yes'"); + } + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: + break; + } + return 0; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index f27f429..ad84fe3 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1716,27 +1716,28 @@ struct _virSpiceChannelDef { /* genparse, genformat */ virDomainGraphicsSpiceChannelMode mode; /* xmlattr */ }; -struct _virDomainGraphicsSpiceDef { /* genparse:concisehook */ +struct _virDomainGraphicsSpiceDef { /* genparse:concisehook, genformat:separate */ int port; /* xmlattr */ int tlsPort; /* xmlattr */ bool portReserved; bool tlsPortReserved; - virDomainGraphicsSpiceMouseMode mousemode; /* xmlattr:mouse/mode */ char *keymap; /* xmlattr */ + bool autoport; /* xmlattr, formathook */ + char *_listen; /* xmlattr:listen, formathook */ + virDomainGraphicsSpiceChannelMode defaultMode; /* xmlattr */ virDomainGraphicsAuthDef auth; /* xmlgroup */ - bool autoport; /* xmlattr */ /* The shadow member _channels helps to parse channels. */ size_t n_channels; virSpiceChannelDefPtr _channels; /* xmlelem:channel, array */ int channels[VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST]; - virDomainGraphicsSpiceChannelMode defaultMode; /* xmlattr */ virDomainGraphicsSpiceImageCompression image; /* xmlattr:image/compression */ virDomainGraphicsSpiceJpegCompression jpeg; /* xmlattr:jpeg/compression */ virDomainGraphicsSpiceZlibCompression zlib; /* xmlattr:zlib/compression */ virTristateSwitch playback; /* xmlattr:playback/compression */ virDomainGraphicsSpiceStreamingMode streaming; /* xmlattr:streaming/mode */ + virDomainGraphicsSpiceMouseMode mousemode; /* xmlattr:mouse/mode */ virTristateBool copypaste; /* xmlattr:clipboard/copypaste */ virTristateBool filetransfer; /* xmlattr:filetransfer/enable */ virTristateBool gl; /* xmlattr:gl/enable */ -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 19 +------------------ src/conf/domain_conf.h | 4 ++-- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 88e324e..f3b59ed 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14463,23 +14463,6 @@ virDomainGraphicsSpiceDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virDomainGraphicsDefParseXMLEGLHeadless(virDomainGraphicsDefPtr def, - xmlNodePtr node, - xmlXPathContextPtr ctxt) -{ - VIR_XPATH_NODE_AUTORESTORE(ctxt) - xmlNodePtr glNode; - - ctxt->node = node; - - if ((glNode = virXPathNode("./gl", ctxt))) - def->data.egl_headless.rendernode = virXMLPropString(glNode, - "rendernode"); - return 0; -} - - virDomainGraphicsDefPtr virDomainGraphicsDefNew(virDomainXMLOptionPtr xmlopt) { @@ -14572,7 +14555,7 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: - if (virDomainGraphicsDefParseXMLEGLHeadless(def, node, ctxt) < 0) + if (virDomainGraphicsEGLHeadlessDefParseXML(node, &def->data.egl_headless, NULL, def, NULL) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_LAST: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ad84fe3..baa9166 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1744,8 +1744,8 @@ struct _virDomainGraphicsSpiceDef { /* genparse:concisehook, genformat:separ char *rendernode; /* xmlattr:gl/rendernode */ }; -struct _virDomainGraphicsEGLHeadlessDef { - char *rendernode; +struct _virDomainGraphicsEGLHeadlessDef { /* genparse */ + char *rendernode; /* xmlattr:gl/rendernode */ }; struct _virDomainGraphicsDef { -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 7 +++---- src/conf/domain_conf.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f3b59ed..0802c45 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -27928,10 +27928,9 @@ virDomainGraphicsDefFormat(virBufferPtr buf, children = true; } - virBufferAddLit(buf, "<gl"); - virBufferEscapeString(buf, " rendernode='%s'", - def->data.egl_headless.rendernode); - virBufferAddLit(buf, "/>\n"); + if (virDomainGraphicsEGLHeadlessDefFormatElem(buf, &def->data.egl_headless, def, NULL) < 0) + return -1; + break; case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index baa9166..6273c40 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1744,7 +1744,7 @@ struct _virDomainGraphicsSpiceDef { /* genparse:concisehook, genformat:separ char *rendernode; /* xmlattr:gl/rendernode */ }; -struct _virDomainGraphicsEGLHeadlessDef { /* genparse */ +struct _virDomainGraphicsEGLHeadlessDef { /* genparse, genformat:separate */ char *rendernode; /* xmlattr:gl/rendernode */ }; -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 207 ++++++++++++++++++++++++----------------- 1 file changed, 121 insertions(+), 86 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0802c45..d1602ab 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14119,55 +14119,19 @@ virDomainTimerDefParseXML(xmlNodePtr node, } -/** - * virDomainGraphicsListenDefParseXML: - * @def: listen def pointer to be filled - * @graphics: graphics def pointer - * @node: xml node of <listen/> element - * @parent: xml node of <graphics/> element - * @flags: bit-wise or of VIR_DOMAIN_DEF_PARSE_* - * - * Parses current <listen/> element from @node to @def. For backward - * compatibility the @parent element should contain node of <graphics/> element - * for the first <listen/> element in order to validate attributes from both - * elements. - */ static int -virDomainGraphicsListenDefParseXML(virDomainGraphicsListenDefPtr def, - virDomainGraphicsDefPtr graphics, - xmlNodePtr node, - xmlNodePtr parent, - unsigned int flags) +virDomainGraphicsListenDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, + virDomainGraphicsListenDefPtr def, + const char *instname G_GNUC_UNUSED, + void *parent, + void *opaque, + const char *type G_GNUC_UNUSED, + const char *fromConfig, + const char *autoGenerated) { - int ret = -1; + unsigned int flags = *((unsigned int *) opaque); + virDomainGraphicsDefPtr graphics = (virDomainGraphicsDefPtr) parent; const char *graphicsType = virDomainGraphicsTypeToString(graphics->type); - int tmp, typeVal; - g_autofree char *type = virXMLPropString(node, "type"); - g_autofree char *address = virXMLPropString(node, "address"); - g_autofree char *network = virXMLPropString(node, "network"); - g_autofree char *socketPath = virXMLPropString(node, "socket"); - g_autofree char *fromConfig = virXMLPropString(node, "fromConfig"); - g_autofree char *autoGenerated = virXMLPropString(node, "autoGenerated"); - g_autofree char *addressCompat = NULL; - g_autofree char *socketCompat = NULL; - - if (parent) { - addressCompat = virXMLPropString(parent, "listen"); - socketCompat = virXMLPropString(parent, "socket"); - } - - if (!type) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("graphics listen type must be specified")); - goto error; - } - - if ((typeVal = virDomainGraphicsListenTypeFromString(type)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown graphics listen type '%s'"), type); - goto error; - } - def->type = typeVal; switch (def->type) { case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: @@ -14194,61 +14158,94 @@ virDomainGraphicsListenDefParseXML(virDomainGraphicsListenDefPtr def, break; } - if (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS) { - if (address && addressCompat && STRNEQ(address, addressCompat)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("graphics 'listen' attribute '%s' must match " - "'address' attribute of first listen element " - "(found '%s')"), addressCompat, address); - goto error; - } - - if (!address) - address = g_steal_pointer(&addressCompat); - } - - if (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { - if (socketPath && socketCompat && STRNEQ(socketPath, socketCompat)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("graphics 'socket' attribute '%s' must match " - "'socket' attribute of first listen element " - "(found '%s')"), socketCompat, socketPath); + if (def->address && def->address[0]) { + if (def->type != VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS && + (def->type != VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK || + (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))) goto error; - } - - if (!socketPath) - socketPath = g_steal_pointer(&socketCompat); - } - - if (address && address[0] && - (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS || - (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK && - !(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)))) { - def->address = g_steal_pointer(&address); } - if (network && network[0]) { + if (def->network && def->network[0]) { if (def->type != VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'network' attribute is valid only for listen " "type 'network'")); goto error; } - def->network = g_steal_pointer(&network); } - if (socketPath && socketPath[0]) { + if (def->socket && def->socket[0]) { if (def->type != VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'socket' attribute is valid only for listen " "type 'socket'")); goto error; } - def->socket = g_steal_pointer(&socketPath); } - if (fromConfig && - flags & VIR_DOMAIN_DEF_PARSE_STATUS) { + if (fromConfig && !(flags & VIR_DOMAIN_DEF_PARSE_STATUS)) + def->fromConfig = 0; + + if (autoGenerated && !(flags & VIR_DOMAIN_DEF_PARSE_STATUS)) + def->autoGenerated = false; + + return 0; + + error: + return -1; +} + + +/** + * virDomainGraphicsListenDefParseXML: + * @def: listen def pointer to be filled + * @graphics: graphics def pointer + * @node: xml node of <listen/> element + * @flags: bit-wise or of VIR_DOMAIN_DEF_PARSE_* + * + * Parses current <listen/> element from @node to @def. For backward + * compatibility the @parent element should contain node of <graphics/> element + * for the first <listen/> element in order to validate attributes from both + * elements. + */ +static int +virDomainGraphicsListenDefParseXML(virDomainGraphicsListenDefPtr def, + virDomainGraphicsDefPtr graphics, + xmlNodePtr node, + unsigned int flags) +{ + int ret = -1; + int tmp, typeVal; + g_autofree char *type = virXMLPropString(node, "type"); + g_autofree char *address = virXMLPropString(node, "address"); + g_autofree char *network = virXMLPropString(node, "network"); + g_autofree char *socketPath = virXMLPropString(node, "socket"); + g_autofree char *fromConfig = virXMLPropString(node, "fromConfig"); + g_autofree char *autoGenerated = virXMLPropString(node, "autoGenerated"); + + if (!type) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("graphics listen type must be specified")); + goto error; + } + + if ((typeVal = virDomainGraphicsListenTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown graphics listen type '%s'"), type); + goto error; + } + def->type = typeVal; + + if (address && address[0]) + def->address = g_steal_pointer(&address); + + if (network && network[0]) + def->network = g_steal_pointer(&network); + + if (socketPath && socketPath[0]) + def->socket = g_steal_pointer(&socketPath); + + if (fromConfig) { if (virStrToLong_i(fromConfig, NULL, 10, &tmp) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid fromConfig value: %s"), @@ -14258,8 +14255,7 @@ virDomainGraphicsListenDefParseXML(virDomainGraphicsListenDefPtr def, def->fromConfig = tmp != 0; } - if (autoGenerated && - flags & VIR_DOMAIN_DEF_PARSE_STATUS) { + if (autoGenerated) { if (virStringParseYesNo(autoGenerated, &def->autoGenerated) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid autoGenerated value: %s"), @@ -14268,6 +14264,11 @@ virDomainGraphicsListenDefParseXML(virDomainGraphicsListenDefPtr def, } } + if (virDomainGraphicsListenDefParseXMLHook(node, def, NULL, + graphics, &flags, type, + fromConfig, autoGenerated) < 0) + goto error; + ret = 0; error: if (ret < 0) @@ -14303,12 +14304,46 @@ virDomainGraphicsListensParseXML(virDomainGraphicsDefPtr def, goto cleanup; for (i = 0; i < nListens; i++) { - if (virDomainGraphicsListenDefParseXML(&def->listens[i], def, + virDomainGraphicsListenDefPtr listen = &def->listens[i]; + if (virDomainGraphicsListenDefParseXML(listen, def, listenNodes[i], - i == 0 ? node : NULL, flags) < 0) goto cleanup; + if (i == 0) { + g_autofree char *addressCompat = NULL; + g_autofree char *socketCompat = NULL; + + addressCompat = virXMLPropString(node, "listen"); + socketCompat = virXMLPropString(node, "socket"); + + if (listen->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS) { + if (listen->address && addressCompat && STRNEQ(listen->address, addressCompat)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("graphics 'listen' attribute '%s' must match " + "'address' attribute of first listen element " + "(found '%s')"), addressCompat, listen->address); + goto cleanup; + } + + if (!listen->address) + listen->address = g_steal_pointer(&addressCompat); + } + + if (listen->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { + if (listen->socket && socketCompat && STRNEQ(listen->socket, socketCompat)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("graphics 'socket' attribute '%s' must match " + "'socket' attribute of first listen element " + "(found '%s')"), socketCompat, listen->socket); + goto cleanup; + } + + if (!listen->socket) + listen->socket = g_steal_pointer(&socketCompat); + } + } + def->nListens++; } } -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 109 ++++--------------------------- src/conf/domain_conf.h | 18 ++--- src/qemu/qemu_command.c | 2 + src/qemu/qemu_hotplug.c | 1 + src/qemu/qemu_migration_cookie.c | 1 + src/qemu/qemu_process.c | 1 + src/qemu/qemu_validate.c | 1 + 7 files changed, 27 insertions(+), 106 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d1602ab..507679b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -853,6 +853,7 @@ VIR_ENUM_IMPL(virDomainGraphics, VIR_ENUM_IMPL(virDomainGraphicsListen, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST, + "dummy", "none", "address", "network", @@ -1789,19 +1790,6 @@ bool virDomainObjTaint(virDomainObjPtr obj, } -static void -virDomainGraphicsListenDefClear(virDomainGraphicsListenDefPtr def) -{ - if (!def) - return; - - VIR_FREE(def->address); - VIR_FREE(def->network); - VIR_FREE(def->socket); - return; -} - - void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) { size_t i; @@ -14119,7 +14107,7 @@ virDomainTimerDefParseXML(xmlNodePtr node, } -static int +int virDomainGraphicsListenDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virDomainGraphicsListenDefPtr def, const char *instname G_GNUC_UNUSED, @@ -14154,6 +14142,7 @@ virDomainGraphicsListenDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, break; case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } @@ -14196,87 +14185,6 @@ virDomainGraphicsListenDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -/** - * virDomainGraphicsListenDefParseXML: - * @def: listen def pointer to be filled - * @graphics: graphics def pointer - * @node: xml node of <listen/> element - * @flags: bit-wise or of VIR_DOMAIN_DEF_PARSE_* - * - * Parses current <listen/> element from @node to @def. For backward - * compatibility the @parent element should contain node of <graphics/> element - * for the first <listen/> element in order to validate attributes from both - * elements. - */ -static int -virDomainGraphicsListenDefParseXML(virDomainGraphicsListenDefPtr def, - virDomainGraphicsDefPtr graphics, - xmlNodePtr node, - unsigned int flags) -{ - int ret = -1; - int tmp, typeVal; - g_autofree char *type = virXMLPropString(node, "type"); - g_autofree char *address = virXMLPropString(node, "address"); - g_autofree char *network = virXMLPropString(node, "network"); - g_autofree char *socketPath = virXMLPropString(node, "socket"); - g_autofree char *fromConfig = virXMLPropString(node, "fromConfig"); - g_autofree char *autoGenerated = virXMLPropString(node, "autoGenerated"); - - if (!type) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("graphics listen type must be specified")); - goto error; - } - - if ((typeVal = virDomainGraphicsListenTypeFromString(type)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown graphics listen type '%s'"), type); - goto error; - } - def->type = typeVal; - - if (address && address[0]) - def->address = g_steal_pointer(&address); - - if (network && network[0]) - def->network = g_steal_pointer(&network); - - if (socketPath && socketPath[0]) - def->socket = g_steal_pointer(&socketPath); - - if (fromConfig) { - if (virStrToLong_i(fromConfig, NULL, 10, &tmp) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid fromConfig value: %s"), - fromConfig); - goto error; - } - def->fromConfig = tmp != 0; - } - - if (autoGenerated) { - if (virStringParseYesNo(autoGenerated, &def->autoGenerated) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid autoGenerated value: %s"), - autoGenerated); - goto error; - } - } - - if (virDomainGraphicsListenDefParseXMLHook(node, def, NULL, - graphics, &flags, type, - fromConfig, autoGenerated) < 0) - goto error; - - ret = 0; - error: - if (ret < 0) - virDomainGraphicsListenDefClear(def); - return ret; -} - - static int virDomainGraphicsListensParseXML(virDomainGraphicsDefPtr def, xmlNodePtr node, @@ -14305,9 +14213,9 @@ virDomainGraphicsListensParseXML(virDomainGraphicsDefPtr def, for (i = 0; i < nListens; i++) { virDomainGraphicsListenDefPtr listen = &def->listens[i]; - if (virDomainGraphicsListenDefParseXML(listen, def, - listenNodes[i], - flags) < 0) + if (virDomainGraphicsListenDefParseXML(listenNodes[i], + listen, NULL, + def, &flags) < 0) goto cleanup; if (i == 0) { @@ -27827,6 +27735,7 @@ virDomainGraphicsVNCDefCheckAttrHook(const virDomainGraphicsVNCDef *def G_GNUC_U ret = true; break; case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } @@ -27890,6 +27799,7 @@ virDomainGraphicsVNCDefFormatAttrHook(const virDomainGraphicsVNCDef *def, virDomainGraphicsListenDefFormatAddr(listenBuf, glisten, flags); break; case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } @@ -27918,6 +27828,7 @@ virDomainGraphicsDefFormat(virBufferPtr buf, case VIR_DOMAIN_GRAPHICS_TYPE_VNC: if (virDomainGraphicsVNCDefFormatAttr(buf, &def->data.vnc, def, &flags) < 0) return -1; + break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: @@ -32630,6 +32541,7 @@ virDomainGraphicsSpiceDefCheckAttrHook(const virDomainGraphicsSpiceDef *def G_GN } break; + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } @@ -32696,6 +32608,7 @@ virDomainGraphicsSpiceDefFormatAttrHook(const virDomainGraphicsSpiceDef *def, } break; + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6273c40..fc4aae2 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1653,7 +1653,8 @@ typedef enum { } virDomainGraphicsSpiceStreamingMode; typedef enum { - VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE = 0, + VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY = 0, + VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET, @@ -1667,13 +1668,14 @@ typedef enum { VIR_DOMAIN_HUB_TYPE_LAST } virDomainHubType; -struct _virDomainGraphicsListenDef { - virDomainGraphicsListenType type; - char *address; - char *network; - char *socket; - bool fromConfig; /* true if the @address is config file originated */ - bool autoGenerated; +struct _virDomainGraphicsListenDef { /* genparse:withhook */ + virDomainGraphicsListenType type; /* xmlattr */ + char *address; /* xmlattr */ + char *network; /* xmlattr */ + char *socket; /* xmlattr */ + /* true if the @address is config file originated */ + int fromConfig; /* xmlattr */ + bool autoGenerated; /* xmlattr */ }; struct _virDomainGraphicsSDLDef { /* genparse, genformat:separate */ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index bd98b0a..f60f1e8 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7592,6 +7592,7 @@ qemuBuildGraphicsVNCCommandLine(virQEMUDriverConfigPtr cfg, virBufferAddLit(&opt, "none"); break; + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } @@ -7718,6 +7719,7 @@ qemuBuildGraphicsSPICECommandLine(virQEMUDriverConfigPtr cfg, virBufferAddLit(&opt, "port=0,"); hasInsecure = true; break; + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index e2c6e14..0588745 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4085,6 +4085,7 @@ qemuDomainChangeGraphics(virQEMUDriverPtr driver, break; case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: /* nada */ break; diff --git a/src/qemu/qemu_migration_cookie.c b/src/qemu/qemu_migration_cookie.c index cef2555..9a8e7ab 100644 --- a/src/qemu/qemu_migration_cookie.c +++ b/src/qemu/qemu_migration_cookie.c @@ -349,6 +349,7 @@ qemuMigrationCookieAddGraphics(qemuMigrationCookiePtr mig, case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index cfe09d6..db1b47e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4926,6 +4926,7 @@ qemuProcessGraphicsSetupListen(virQEMUDriverPtr driver, break; case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 070f1c9..167cdc2 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -3293,6 +3293,7 @@ qemuValidateDomainDeviceDefSPICEGraphics(const virDomainGraphicsDef *graphics, case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: break; + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } -- 2.25.1

On Fri, Sep 04, 2020 at 11:35:34AM +0800, Shi Lei wrote:
Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 109 ++++--------------------------- src/conf/domain_conf.h | 18 ++--- src/qemu/qemu_command.c | 2 + src/qemu/qemu_hotplug.c | 1 + src/qemu/qemu_migration_cookie.c | 1 + src/qemu/qemu_process.c | 1 + src/qemu/qemu_validate.c | 1 + 7 files changed, 27 insertions(+), 106 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d1602ab..507679b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -853,6 +853,7 @@ VIR_ENUM_IMPL(virDomainGraphics,
VIR_ENUM_IMPL(virDomainGraphicsListen, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST, + "dummy", "none", "address", "network",
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6273c40..fc4aae2 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1653,7 +1653,8 @@ typedef enum { } virDomainGraphicsSpiceStreamingMode;
typedef enum { - VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE = 0, + VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_DUMMY = 0, + VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE,
Hmm, another case of adding a dummy enum value. I think we really need to avoid these additions.
VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET, @@ -1667,13 +1668,14 @@ typedef enum { VIR_DOMAIN_HUB_TYPE_LAST } virDomainHubType;
-struct _virDomainGraphicsListenDef { - virDomainGraphicsListenType type; - char *address; - char *network; - char *socket; - bool fromConfig; /* true if the @address is config file originated */ - bool autoGenerated; +struct _virDomainGraphicsListenDef { /* genparse:withhook */ + virDomainGraphicsListenType type; /* xmlattr */ + char *address; /* xmlattr */ + char *network; /* xmlattr */ + char *socket; /* xmlattr */ + /* true if the @address is config file originated */ + int fromConfig; /* xmlattr */ + bool autoGenerated; /* xmlattr */ };
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 114 +++++++++++++++++++++++------------------ src/conf/domain_conf.h | 12 ++--- 2 files changed, 70 insertions(+), 56 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 507679b..007ee44 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -27614,19 +27614,64 @@ virDomainTimerDefFormat(virBufferPtr buf, } -static void -virDomainGraphicsListenDefFormat(virBufferPtr buf, - virDomainGraphicsListenDefPtr def, - unsigned int flags) +static bool +virDomainGraphicsListenDefValid(const virDomainGraphicsListenDef *def, + const virDomainGraphicsDef *graphics, + unsigned int flags) { - /* If generating migratable XML, skip listen address - * dragged in from config file */ - if ((flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) && def->fromConfig) - return; + if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) { + /* If the listen is based on config options from qemu.conf we need + * to skip it. It's up to user to properly configure both hosts for + * migration. */ + if (def->fromConfig) + return false; + + /* If the socket is provided by user in the XML we need to skip this + * listen type to support migration back to old libvirt since old + * libvirt supports specifying socket path inside graphics element + * as 'socket' attribute. Auto-generated socket is a new feature + * thus we can generate it in the migrateble XML. */ + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && + def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET && + def->socket && !def->autoGenerated) + return false; + + /* The new listen type none is in the migratable XML represented as + * port=0 and autoport=no because old libvirt support this + * configuration for spice. */ + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && + def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE) + return false; + } + + return true; +} + + +bool +virDomainGraphicsListenDefCheckHook(const virDomainGraphicsListenDef *def, + const void *parent, + void *opaque, + bool value G_GNUC_UNUSED) +{ + virDomainGraphicsDefPtr graphics = (virDomainGraphicsDefPtr) parent; + unsigned int flags = 0; + if (opaque) + flags = *((unsigned int *) opaque); + return virDomainGraphicsListenDefValid(def, graphics, flags); +} - virBufferAddLit(buf, "<listen"); - virBufferAsprintf(buf, " type='%s'", - virDomainGraphicsListenTypeToString(def->type)); + +int +virDomainGraphicsListenDefFormatHook(const virDomainGraphicsListenDef *def, + const void *parent G_GNUC_UNUSED, + const void *opaque, + virBufferPtr addressBuf, + virBufferPtr socketBuf) +{ + unsigned int flags = 0; + if (opaque) + flags = *((unsigned int *) opaque); if (def->address && (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS || @@ -27634,28 +27679,17 @@ virDomainGraphicsListenDefFormat(virBufferPtr buf, !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)))) { /* address may also be set to show current status when type='network', * but we don't want to print that if INACTIVE data is requested. */ - virBufferAsprintf(buf, " address='%s'", def->address); - } - - if (def->network && - (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK)) { - virBufferEscapeString(buf, " network='%s'", def->network); + virBufferAsprintf(addressBuf, " address='%s'", def->address); } if (def->socket && def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET && !(def->autoGenerated && (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE))) { - virBufferEscapeString(buf, " socket='%s'", def->socket); - } - - if (flags & VIR_DOMAIN_DEF_FORMAT_STATUS) { - virBufferAsprintf(buf, " fromConfig='%d'", def->fromConfig); - virBufferAsprintf(buf, " autoGenerated='%s'", - def->autoGenerated ? "yes" : "no"); + virBufferEscapeString(socketBuf, " socket='%s'", def->socket); } - virBufferAddLit(buf, "/>\n"); + return 0; } @@ -27883,37 +27917,17 @@ virDomainGraphicsDefFormat(virBufferPtr buf, } for (i = 0; i < def->nListens; i++) { - if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) { - /* If the listen is based on config options from qemu.conf we need - * to skip it. It's up to user to properly configure both hosts for - * migration. */ - if (def->listens[i].fromConfig) - continue; - - /* If the socket is provided by user in the XML we need to skip this - * listen type to support migration back to old libvirt since old - * libvirt supports specifying socket path inside graphics element - * as 'socket' attribute. Auto-generated socket is a new feature - * thus we can generate it in the migrateble XML. */ - if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - def->listens[i].type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET && - def->listens[i].socket && - !def->listens[i].autoGenerated) - continue; + if (!virDomainGraphicsListenDefValid(&def->listens[i], def, flags)) + continue; - /* The new listen type none is in the migratable XML represented as - * port=0 and autoport=no because old libvirt support this - * configuration for spice. */ - if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && - def->listens[i].type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE) - continue; - } if (!children) { virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); children = true; } - virDomainGraphicsListenDefFormat(buf, &def->listens[i], flags); + + virDomainGraphicsListenDefFormatBuf(buf, "listen", + &def->listens[i], def, &flags); } if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fc4aae2..d78cdbb 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1668,14 +1668,14 @@ typedef enum { VIR_DOMAIN_HUB_TYPE_LAST } virDomainHubType; -struct _virDomainGraphicsListenDef { /* genparse:withhook */ +struct _virDomainGraphicsListenDef { /* genparse:withhook, genformat */ virDomainGraphicsListenType type; /* xmlattr */ - char *address; /* xmlattr */ - char *network; /* xmlattr */ - char *socket; /* xmlattr */ + char *address; /* xmlattr, formathook */ + char *network; /* xmlattr, formatflag:VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK */ + char *socket; /* xmlattr, formathook */ /* true if the @address is config file originated */ - int fromConfig; /* xmlattr */ - bool autoGenerated; /* xmlattr */ + int fromConfig; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ + bool autoGenerated; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ }; struct _virDomainGraphicsSDLDef { /* genparse, genformat:separate */ -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 184 +++++++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 81 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 007ee44..60bea6f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14191,12 +14191,10 @@ virDomainGraphicsListensParseXML(virDomainGraphicsDefPtr def, xmlXPathContextPtr ctxt, unsigned int flags) { - VIR_XPATH_NODE_AUTORESTORE(ctxt) - virDomainGraphicsListenDef newListen = {0}; int nListens; int ret = -1; g_autofree xmlNodePtr *listenNodes = NULL; - g_autofree char *socketPath = NULL; + VIR_XPATH_NODE_AUTORESTORE(ctxt); ctxt->node = node; @@ -14218,84 +14216,12 @@ virDomainGraphicsListensParseXML(virDomainGraphicsDefPtr def, def, &flags) < 0) goto cleanup; - if (i == 0) { - g_autofree char *addressCompat = NULL; - g_autofree char *socketCompat = NULL; - - addressCompat = virXMLPropString(node, "listen"); - socketCompat = virXMLPropString(node, "socket"); - - if (listen->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS) { - if (listen->address && addressCompat && STRNEQ(listen->address, addressCompat)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("graphics 'listen' attribute '%s' must match " - "'address' attribute of first listen element " - "(found '%s')"), addressCompat, listen->address); - goto cleanup; - } - - if (!listen->address) - listen->address = g_steal_pointer(&addressCompat); - } - - if (listen->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { - if (listen->socket && socketCompat && STRNEQ(listen->socket, socketCompat)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("graphics 'socket' attribute '%s' must match " - "'socket' attribute of first listen element " - "(found '%s')"), socketCompat, listen->socket); - goto cleanup; - } - - if (!listen->socket) - listen->socket = g_steal_pointer(&socketCompat); - } - } - def->nListens++; } } - /* If no <listen/> element was found in XML for backward compatibility - * we should try to parse 'listen' or 'socket' attribute from <graphics/> - * element. */ - if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) - socketPath = virXMLPropString(node, "socket"); - - if (socketPath) { - newListen.type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET; - newListen.socket = g_steal_pointer(&socketPath); - } else { - newListen.type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS; - newListen.address = virXMLPropString(node, "listen"); - if (STREQ_NULLABLE(newListen.address, "")) - VIR_FREE(newListen.address); - } - - /* If no <listen/> element was found add a new one created by parsing - * <graphics/> element. */ - if (def->nListens == 0) { - if (VIR_APPEND_ELEMENT(def->listens, def->nListens, newListen) < 0) - goto cleanup; - } else { - virDomainGraphicsListenDefPtr glisten = &def->listens[0]; - - /* If the first <listen/> element is 'address' or 'network' and we found - * 'socket' attribute inside <graphics/> element for backward - * compatibility we need to replace the first listen by - * <listen type='socket' .../> element based on the 'socket' attribute. */ - if ((glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS || - glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK) && - newListen.type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { - virDomainGraphicsListenDefClear(glisten); - *glisten = newListen; - memset(&newListen, 0, sizeof(newListen)); - } - } - ret = 0; cleanup: - virDomainGraphicsListenDefClear(&newListen); return ret; } @@ -14442,6 +14368,102 @@ virDomainNetDefNew(virDomainXMLOptionPtr xmlopt) } +static int +virDomainGraphicsDefParseXMLHook(xmlNodePtr node, + virDomainGraphicsDefPtr def, + const char *instname G_GNUC_UNUSED, + void *parent G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + int ret = -1; + virDomainGraphicsListenDef newListen = {0}; + g_autofree char *socketPath = NULL; + + if (def->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC && + def->type != VIR_DOMAIN_GRAPHICS_TYPE_RDP && + def->type != VIR_DOMAIN_GRAPHICS_TYPE_SPICE) + return 0; + + if (def->nListens > 0) { + virDomainGraphicsListenDefPtr listen = &def->listens[0]; + g_autofree char *addressCompat = NULL; + g_autofree char *socketCompat = NULL; + + addressCompat = virXMLPropString(node, "listen"); + socketCompat = virXMLPropString(node, "socket"); + + if (listen->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS) { + if (listen->address && addressCompat && STRNEQ(listen->address, addressCompat)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("graphics 'listen' attribute '%s' must match " + "'address' attribute of first listen element " + "(found '%s')"), addressCompat, listen->address); + goto cleanup; + } + + if (!listen->address) + listen->address = g_steal_pointer(&addressCompat); + } + + if (listen->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { + if (listen->socket && socketCompat && STRNEQ(listen->socket, socketCompat)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("graphics 'socket' attribute '%s' must match " + "'socket' attribute of first listen element " + "(found '%s')"), socketCompat, listen->socket); + goto cleanup; + } + + if (!listen->socket) + listen->socket = g_steal_pointer(&socketCompat); + } + } + + /* If no <listen/> element was found in XML for backward compatibility + * we should try to parse 'listen' or 'socket' attribute from <graphics/> + * element. */ + if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) + socketPath = virXMLPropString(node, "socket"); + + if (socketPath) { + newListen.type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET; + newListen.socket = g_steal_pointer(&socketPath); + } else { + newListen.type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS; + newListen.address = virXMLPropString(node, "listen"); + if (STREQ_NULLABLE(newListen.address, "")) + VIR_FREE(newListen.address); + } + + /* If no <listen/> element was found add a new one created by parsing + * <graphics/> element. */ + if (def->nListens == 0) { + if (VIR_APPEND_ELEMENT(def->listens, def->nListens, newListen) < 0) + goto cleanup; + } else { + virDomainGraphicsListenDefPtr glisten = &def->listens[0]; + + /* If the first <listen/> element is 'address' or 'network' and we found + * 'socket' attribute inside <graphics/> element for backward + * compatibility we need to replace the first listen by + * <listen type='socket' .../> element based on the 'socket' attribute. */ + if ((glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS || + glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK) && + newListen.type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { + virDomainGraphicsListenDefClear(glisten); + *glisten = newListen; + memset(&newListen, 0, sizeof(newListen)); + } + } + + ret = 0; + + cleanup: + virDomainGraphicsListenDefClear(&newListen); + return ret; +} + + /* Parse the XML definition for a graphics device */ static virDomainGraphicsDefPtr virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, @@ -14470,20 +14492,19 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, } def->type = typeVal; + if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) + goto error; + switch (def->type) { case VIR_DOMAIN_GRAPHICS_TYPE_VNC: if (virDomainGraphicsVNCDefParseXML(node, &def->data.vnc, NULL, def, &flags) < 0) goto error; - if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) - goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: if (virDomainGraphicsSDLDefParseXML(node, &def->data.sdl, NULL, def, NULL) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: - if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) - goto error; if (virDomainGraphicsRDPDefParseXML(node, &def->data.rdp, NULL, def, &flags) < 0) goto error; break; @@ -14492,8 +14513,6 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: - if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) - goto error; if (virDomainGraphicsSpiceDefParseXML(node, &def->data.spice, NULL, def, &flags) < 0) goto error; break; @@ -14505,6 +14524,9 @@ virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, break; } + if (virDomainGraphicsDefParseXMLHook(node, def, NULL, NULL, &flags) < 0) + goto error; + return def; error: -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 140 ++++++--------------------------------- src/conf/domain_conf.h | 21 +++--- src/qemu/qemu_command.c | 2 + src/qemu/qemu_driver.c | 2 + src/qemu/qemu_hotplug.c | 1 + src/qemu/qemu_process.c | 4 ++ src/qemu/qemu_validate.c | 1 + src/vmx/vmx.c | 1 + 8 files changed, 42 insertions(+), 130 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 60bea6f..61dc7b3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -843,6 +843,7 @@ VIR_ENUM_IMPL(virDomainInputModel, VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST, + "none", "sdl", "vnc", "rdp", @@ -1824,6 +1825,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) VIR_FREE(def->data.egl_headless.rendernode); break; + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } @@ -14185,47 +14187,6 @@ virDomainGraphicsListenDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, } -static int -virDomainGraphicsListensParseXML(virDomainGraphicsDefPtr def, - xmlNodePtr node, - xmlXPathContextPtr ctxt, - unsigned int flags) -{ - int nListens; - int ret = -1; - g_autofree xmlNodePtr *listenNodes = NULL; - VIR_XPATH_NODE_AUTORESTORE(ctxt); - - ctxt->node = node; - - /* parse the <listen> subelements for graphics types that support it */ - nListens = virXPathNodeSet("./listen", ctxt, &listenNodes); - if (nListens < 0) - goto cleanup; - - if (nListens > 0) { - size_t i; - - if (VIR_ALLOC_N(def->listens, nListens) < 0) - goto cleanup; - - for (i = 0; i < nListens; i++) { - virDomainGraphicsListenDefPtr listen = &def->listens[i]; - if (virDomainGraphicsListenDefParseXML(listenNodes[i], - listen, NULL, - def, &flags) < 0) - goto cleanup; - - def->nListens++; - } - } - - ret = 0; - cleanup: - return ret; -} - - int virDomainGraphicsVNCDefParseXMLHook(xmlNodePtr node G_GNUC_UNUSED, virDomainGraphicsVNCDefPtr def, @@ -14368,7 +14329,7 @@ virDomainNetDefNew(virDomainXMLOptionPtr xmlopt) } -static int +int virDomainGraphicsDefParseXMLHook(xmlNodePtr node, virDomainGraphicsDefPtr def, const char *instname G_GNUC_UNUSED, @@ -14464,78 +14425,6 @@ virDomainGraphicsDefParseXMLHook(xmlNodePtr node, } -/* Parse the XML definition for a graphics device */ -static virDomainGraphicsDefPtr -virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, - xmlNodePtr node, - xmlXPathContextPtr ctxt, - unsigned int flags) -{ - virDomainGraphicsDefPtr def; - int typeVal; - g_autofree char *type = NULL; - - if (!(def = virDomainGraphicsDefNew(xmlopt))) - return NULL; - - type = virXMLPropString(node, "type"); - if (!type) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("missing graphics device type")); - goto error; - } - - if ((typeVal = virDomainGraphicsTypeFromString(type)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown graphics device type '%s'"), type); - goto error; - } - def->type = typeVal; - - if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) - goto error; - - switch (def->type) { - case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - if (virDomainGraphicsVNCDefParseXML(node, &def->data.vnc, NULL, def, &flags) < 0) - goto error; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - if (virDomainGraphicsSDLDefParseXML(node, &def->data.sdl, NULL, def, NULL) < 0) - goto error; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_RDP: - if (virDomainGraphicsRDPDefParseXML(node, &def->data.rdp, NULL, def, &flags) < 0) - goto error; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: - if (virDomainGraphicsDesktopDefParseXML(node, &def->data.desktop, NULL, def, NULL) < 0) - goto error; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: - if (virDomainGraphicsSpiceDefParseXML(node, &def->data.spice, NULL, def, &flags) < 0) - goto error; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: - if (virDomainGraphicsEGLHeadlessDefParseXML(node, &def->data.egl_headless, NULL, def, NULL) < 0) - goto error; - break; - case VIR_DOMAIN_GRAPHICS_TYPE_LAST: - break; - } - - if (virDomainGraphicsDefParseXMLHook(node, def, NULL, NULL, &flags) < 0) - goto error; - - return def; - - error: - virDomainGraphicsDefFree(def); - def = NULL; - return NULL; -} - - static virDomainSoundCodecDefPtr virDomainSoundCodecDefParseXML(xmlNodePtr node) { @@ -16714,9 +16603,14 @@ virDomainDeviceDefParse(const char *xmlStr, return NULL; break; case VIR_DOMAIN_DEVICE_GRAPHICS: - if (!(dev->data.graphics = virDomainGraphicsDefParseXML(xmlopt, node, - ctxt, flags))) + if (!(dev->data.graphics = virDomainGraphicsDefNew(xmlopt))) return NULL; + if (virDomainGraphicsDefParseXML(node, dev->data.graphics, NULL, + dev, &flags) < 0) { + virDomainGraphicsDefFree(dev->data.graphics); + dev->data.graphics = NULL; + return NULL; + } break; case VIR_DOMAIN_DEVICE_HUB: if (!(dev->data.hub = virDomainHubDefParseXML(xmlopt, node, flags))) @@ -21572,13 +21466,16 @@ virDomainDefParseXML(xmlDocPtr xml, if (n && VIR_ALLOC_N(def->graphics, n) < 0) goto error; for (i = 0; i < n; i++) { - virDomainGraphicsDefPtr graphics = virDomainGraphicsDefParseXML(xmlopt, - nodes[i], - ctxt, - flags); + virDomainGraphicsDefPtr graphics = virDomainGraphicsDefNew(xmlopt); if (!graphics) goto error; + if (virDomainGraphicsDefParseXML(nodes[i], graphics, NULL, + def, &flags) < 0) { + virDomainGraphicsDefFree(graphics); + goto error; + } + def->graphics[def->ngraphics++] = graphics; } VIR_FREE(nodes); @@ -27934,6 +27831,7 @@ virDomainGraphicsDefFormat(virBufferPtr buf, return -1; break; + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } @@ -32343,6 +32241,7 @@ virDomainGraphicsDefHasOpenGL(const virDomainDef *def) case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: return true; + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } @@ -32381,6 +32280,7 @@ virDomainGraphicsGetRenderNode(const virDomainGraphicsDef *graphics) case VIR_DOMAIN_GRAPHICS_TYPE_VNC: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d78cdbb..b6d69fa 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1545,6 +1545,7 @@ struct _virDomainVideoDef { /* graphics console modes */ typedef enum { + VIR_DOMAIN_GRAPHICS_TYPE_NONE = 0, VIR_DOMAIN_GRAPHICS_TYPE_SDL, VIR_DOMAIN_GRAPHICS_TYPE_VNC, VIR_DOMAIN_GRAPHICS_TYPE_RDP, @@ -1750,7 +1751,7 @@ struct _virDomainGraphicsEGLHeadlessDef { /* genparse, genformat:separate */ char *rendernode; /* xmlattr:gl/rendernode */ }; -struct _virDomainGraphicsDef { +struct _virDomainGraphicsDef { /* genparse:concisehook */ virObjectPtr privateData; /* Port value discipline: @@ -1758,20 +1759,20 @@ struct _virDomainGraphicsDef { * Value 0 means port wasn't specified in XML at all. * Positive value is actual port number given in XML. */ - virDomainGraphicsType type; + virDomainGraphicsType type; /* xmlattr */ union { - virDomainGraphicsSDLDef sdl; - virDomainGraphicsVNCDef vnc; - virDomainGraphicsRDPDef rdp; - virDomainGraphicsDesktopDef desktop; - virDomainGraphicsSpiceDef spice; - virDomainGraphicsEGLHeadlessDef egl_headless; - } data; + virDomainGraphicsSDLDef sdl; /* xmlgroup */ + virDomainGraphicsVNCDef vnc; /* xmlgroup */ + virDomainGraphicsRDPDef rdp; /* xmlgroup */ + virDomainGraphicsDesktopDef desktop; /* xmlgroup */ + virDomainGraphicsSpiceDef spice; /* xmlgroup */ + virDomainGraphicsEGLHeadlessDef egl_headless; /* xmlgroup */ + } data; /* xmlswitch:type */ /* nListens, listens, and *port are only useful if type is vnc, * rdp, or spice. They've been extracted from the union only to * simplify parsing code.*/ size_t nListens; - virDomainGraphicsListenDefPtr listens; + virDomainGraphicsListenDefPtr listens; /* xmlelem, array:nListens */ }; typedef enum { diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f60f1e8..272be28 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7938,6 +7938,7 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfigPtr cfg, case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: return -1; + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: virReportEnumRangeError(virDomainGraphicsType, graphics->type); @@ -9772,6 +9773,7 @@ qemuBuildCommandLineValidate(virQEMUDriverPtr driver, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0d06812..922b9bb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -15681,6 +15681,7 @@ qemuDomainOpenGraphics(virDomainPtr dom, _("Can only open VNC or SPICE graphics backends, not %s"), virDomainGraphicsTypeToString(vm->def->graphics[idx]->type)); goto endjob; + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: virReportEnumRangeError(virDomainGraphicsType, @@ -15750,6 +15751,7 @@ qemuDomainOpenGraphicsFD(virDomainPtr dom, _("Can only open VNC or SPICE graphics backends, not %s"), virDomainGraphicsTypeToString(vm->def->graphics[idx]->type)); goto cleanup; + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: virReportEnumRangeError(virDomainGraphicsType, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 0588745..d008035 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4191,6 +4191,7 @@ qemuDomainChangeGraphics(virQEMUDriverPtr driver, virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to change config on '%s' graphics type"), type); break; + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: virReportEnumRangeError(virDomainGraphicsType, dev->type); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index db1b47e..c20988f 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4687,6 +4687,7 @@ qemuProcessGraphicsReservePorts(virDomainGraphicsDefPtr graphics, case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } @@ -4726,6 +4727,7 @@ qemuProcessGraphicsAllocatePorts(virQEMUDriverPtr driver, case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } @@ -4882,6 +4884,7 @@ qemuProcessGraphicsSetupListen(virQEMUDriverPtr driver, case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } @@ -5188,6 +5191,7 @@ qemuProcessStartValidateGraphics(virDomainObjPtr vm) case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 167cdc2..1d7a492 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -3416,6 +3416,7 @@ qemuValidateDomainDeviceDefGraphics(const virDomainGraphicsDef *graphics, _("unsupported graphics type '%s'"), virDomainGraphicsTypeToString(graphics->type)); return -1; + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: return -1; diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 4b1b04c..cfc88aa 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -3314,6 +3314,7 @@ virVMXFormatConfig(virVMXContext *ctx, virDomainXMLOptionPtr xmlopt, virDomainDe virDomainGraphicsTypeToString(def->graphics[i]->type)); goto cleanup; + case VIR_DOMAIN_GRAPHICS_TYPE_NONE: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: virReportEnumRangeError(virDomainGraphicsType, def->graphics[i]->type); -- 2.25.1

Signed-off-by: Shi Lei <shi_lei@massclouds.com> --- src/conf/domain_conf.c | 152 ++--------------------------------------- src/conf/domain_conf.h | 14 ++-- 2 files changed, 15 insertions(+), 151 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 61dc7b3..64d6ae5 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1793,47 +1793,10 @@ bool virDomainObjTaint(virDomainObjPtr obj, void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) { - size_t i; - if (!def) return; - switch (def->type) { - case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - VIR_FREE(def->data.vnc.keymap); - virDomainGraphicsAuthDefClear(&def->data.vnc.auth); - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - virDomainGraphicsSDLDefClear(&def->data.sdl); - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_RDP: - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: - VIR_FREE(def->data.desktop.display); - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: - VIR_FREE(def->data.spice.rendernode); - VIR_FREE(def->data.spice.keymap); - virDomainGraphicsAuthDefClear(&def->data.spice.auth); - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: - VIR_FREE(def->data.egl_headless.rendernode); - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_NONE: - case VIR_DOMAIN_GRAPHICS_TYPE_LAST: - break; - } - - for (i = 0; i < def->nListens; i++) - virDomainGraphicsListenDefClear(&def->listens[i]); - VIR_FREE(def->listens); - + virDomainGraphicsDefClear(def); virObjectUnref(def->privateData); VIR_FREE(def); } @@ -27760,111 +27723,6 @@ virDomainGraphicsVNCDefFormatAttrHook(const virDomainGraphicsVNCDef *def, return 0; } -static int -virDomainGraphicsDefFormat(virBufferPtr buf, - virDomainGraphicsDefPtr def, - unsigned int flags) -{ - const char *type = virDomainGraphicsTypeToString(def->type); - bool children = false; - size_t i; - - if (!type) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected net type %d"), def->type); - return -1; - } - - virBufferAsprintf(buf, "<graphics type='%s'", type); - - switch (def->type) { - case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - if (virDomainGraphicsVNCDefFormatAttr(buf, &def->data.vnc, def, &flags) < 0) - return -1; - - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - if (virDomainGraphicsSDLDefFormatAttr(buf, &def->data.sdl, def, NULL) < 0) - return -1; - - if (!children && virDomainGraphicsSDLDefCheckElem(&def->data.sdl, def, NULL)) { - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 2); - children = true; - } - - if (virDomainGraphicsSDLDefFormatElem(buf, &def->data.sdl, def, NULL) < 0) - return -1; - - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_RDP: - if (virDomainGraphicsRDPDefFormatAttr(buf, &def->data.rdp, def, NULL)) - return -1; - - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: - if (virDomainGraphicsDesktopDefFormatAttr(buf, &def->data.desktop, def, NULL) < 0) - return -1; - - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: - if (virDomainGraphicsSpiceDefFormatAttr(buf, &def->data.spice, def, &flags) < 0) - return -1; - - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: - if (!def->data.egl_headless.rendernode) - break; - - if (!children) { - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 2); - children = true; - } - - if (virDomainGraphicsEGLHeadlessDefFormatElem(buf, &def->data.egl_headless, def, NULL) < 0) - return -1; - - break; - case VIR_DOMAIN_GRAPHICS_TYPE_NONE: - case VIR_DOMAIN_GRAPHICS_TYPE_LAST: - break; - } - - for (i = 0; i < def->nListens; i++) { - if (!virDomainGraphicsListenDefValid(&def->listens[i], def, flags)) - continue; - - if (!children) { - virBufferAddLit(buf, ">\n"); - virBufferAdjustIndent(buf, 2); - children = true; - } - - virDomainGraphicsListenDefFormatBuf(buf, "listen", - &def->listens[i], def, &flags); - } - - if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { - if (virDomainGraphicsSpiceDefFormatElem(buf, &def->data.spice, def, NULL) < 0) - return -1; - } - - if (children) { - virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "</graphics>\n"); - } else { - virBufferAddLit(buf, "/>\n"); - } - - return 0; -} - static int virDomainHostdevDefFormat(virBufferPtr buf, @@ -29539,7 +29397,9 @@ virDomainDefFormatInternalSetRootName(virDomainDefPtr def, } for (n = 0; n < def->ngraphics; n++) { - if (virDomainGraphicsDefFormat(buf, def->graphics[n], flags) < 0) + if (virDomainGraphicsDefFormatBuf(buf, "graphics", + def->graphics[n], + def, &flags) < 0) return -1; } @@ -30696,7 +30556,9 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src, rc = virDomainControllerDefFormat(&buf, src->data.controller, flags); break; case VIR_DOMAIN_DEVICE_GRAPHICS: - rc = virDomainGraphicsDefFormat(&buf, src->data.graphics, flags); + rc = virDomainGraphicsDefFormatBuf(&buf, "graphics", + src->data.graphics, + src, &flags); break; case VIR_DOMAIN_DEVICE_HUB: rc = virDomainHubDefFormat(&buf, src->data.hub, flags); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index b6d69fa..166ba2a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1751,7 +1751,7 @@ struct _virDomainGraphicsEGLHeadlessDef { /* genparse, genformat:separate */ char *rendernode; /* xmlattr:gl/rendernode */ }; -struct _virDomainGraphicsDef { /* genparse:concisehook */ +struct _virDomainGraphicsDef { /* genparse:concisehook, genformat */ virObjectPtr privateData; /* Port value discipline: @@ -1760,6 +1760,13 @@ struct _virDomainGraphicsDef { /* genparse:concisehook */ * Positive value is actual port number given in XML. */ virDomainGraphicsType type; /* xmlattr */ + + /* nListens, listens, and *port are only useful if type is vnc, + * rdp, or spice. They've been extracted from the union only to + * simplify parsing code.*/ + size_t nListens; + virDomainGraphicsListenDefPtr listens; /* xmlelem, array:nListens */ + union { virDomainGraphicsSDLDef sdl; /* xmlgroup */ virDomainGraphicsVNCDef vnc; /* xmlgroup */ @@ -1768,11 +1775,6 @@ struct _virDomainGraphicsDef { /* genparse:concisehook */ virDomainGraphicsSpiceDef spice; /* xmlgroup */ virDomainGraphicsEGLHeadlessDef egl_headless; /* xmlgroup */ } data; /* xmlswitch:type */ - /* nListens, listens, and *port are only useful if type is vnc, - * rdp, or spice. They've been extracted from the union only to - * simplify parsing code.*/ - size_t nListens; - virDomainGraphicsListenDefPtr listens; /* xmlelem, array:nListens */ }; typedef enum { -- 2.25.1

Polite ping. Regards, Shi Lei From: "Shi Lei" <shi_lei@massclouds.com> Date: 2020-09-04 11:34:52 To: libvir-list@redhat.com Subject: [RFCv2 00/46] RFC: Generate parsexml/formatbuf functions based on directives>V1 here: [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]
Differ from V1:
* Move the generator into scripts/xmlgen and rename it 'xmlgen'.
* Declare virXMLChildNode and virXMLChildNodeSet in libvirt_private.syms.
* Replace VIR_FREE with g_free and VIR_ALLOC[_N] with g_new0.
* Adjust virReportError to avoid unnecessary translation.
* Remove the macro VIR_USED and use G_GNUC_UNUSED to declare arguments.
* When parsing string member, assign value to it directly instead of using middle variable.
* Don't set libclang_path. Just use python-clang's default setting.
* Use virEscapeString for escaping xml characters.
* Enable directive 'genformat' with a parameter to support separation mode.
* Add directive 'xmlswitch' and 'xmlgroup' to support discriminated unions.
* Allow directive 'array' and 'specified' to carry with a parameter, which specifies its counterpart explicitly.
* Enable directive 'xmlattr' with path.
* Add directive 'formatflag' and 'formathook'.
For those new and changed directives, illustrate them by an example:
struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ char *passwd; /* xmlattr, formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE */ bool expires; time_t validTo; /* xmlattr:passwdValidTo, specified:expires */ virDomainGraphicsAuthConnectedType connected; /* xmlattr */ };
struct _virDomainGraphicsListenDef { /* genparse:withhook, genformat */ virDomainGraphicsListenType type; /* xmlattr */ char *address; /* xmlattr, formathook */ char *network; /* xmlattr, formatflag:VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK */ char *socket; /* xmlattr, formathook */ int fromConfig; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ bool autoGenerated; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ };
struct _virDomainGraphicsSDLDef { /* genparse, genformat:separate */ char *display; /* xmlattr */ char *xauth; /* xmlattr */ bool fullscreen; /* xmlattr */ virTristateBool gl; /* xmlattr:gl/enable */ };
struct _virDomainGraphicsDef { /* genparse:concisehook, genformat */ virObjectPtr privateData; virDomainGraphicsType type; /* xmlattr */
size_t nListens; virDomainGraphicsListenDefPtr listens; /* xmlelem, array:nListens */
union { virDomainGraphicsSDLDef sdl; /* xmlgroup */ virDomainGraphicsVNCDef vnc; /* xmlgroup */ virDomainGraphicsRDPDef rdp; /* xmlgroup */ virDomainGraphicsDesktopDef desktop; /* xmlgroup */ virDomainGraphicsSpiceDef spice; /* xmlgroup */ virDomainGraphicsEGLHeadlessDef egl_headless; /* xmlgroup */ } data; /* xmlswitch:type */ };
Explanation for these directives:
- genformat[:separate|onlyattrs|onlyelems]
Only work on a struct. Generate formatbuf function for this struct only if 'genformat' is specified. The function name is based on struct-name and suffixed with 'FormatBuf'.
When 'genformat:separate' is specified, generate two formatbuf functions rather than a single full-mode formatbuf function. One for formatting attributes and another for formatting elements. These function names are based on struct-name and suffixed with 'FormatAttr' and 'FormatElem' respectively.
The 'onlyattrs' and 'onlyelems' are just like 'separate', but only generate one of those two functions according to its denotation.
- xmlattr[:[parentname/]thename]
Parse/Format the field as an XML attribute or attribute wrapped by an XML element. If only 'thename' is specified, use it as the XML attribute name; or use the filed name. The 'parentname' is the name of the attribute's parent element. If 'parentname/thename' is specified, the corresponding form is <parentname thename='..' />.
- xmlgroup
The field is a struct, but its corresponding form in XML is a group rather than an element.
- xmlswitch:thename
Only for discriminated union. 'thename' is the name of its relative enum. The name of each union member should match a shortname of the enum.
- array[:countername]
Parse/Format the field as an array. Each array field must have an related counter field, which name is specified by 'countername'. If 'countername' is omitted, follow the pattern: n + 'field_name'.
- specified[:thename]
This field has an related field to indicate its existence, and 'thename' specifies the name of this related field. When 'thename' is omitted, follow the pattern: 'field_name' + '_specified'.
- formatflag:[!|%]flag
This field will be formatted and written out to XML only if the 'flag' hits a target flagset. The target flagset is passed into the formatbuf function through the argument 'opaque'.
Adding a '!' before 'flag' means NOT hitting.
Adding a '%' before 'flag' means that flag hitting-check is the unique condition for formatting this field. For example, for 'passwd' in 'virDomainGraphicsAuthDef', the directive is:
formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE
then the generated code:
if (def->passwd && (virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE)) virBufferEscapeString(buf, " passwd='%s'", def->passwd);
If '%' is inserted like this:
formatflag:%VIR_DOMAIN_DEF_FORMAT_SECURE
then the generated code:
if ((virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE)) virBufferEscapeString(buf, " passwd='%s'", def->passwd);
- formathook Introduce hooks to handle the field if xmlgen can't deal with it now.
E.g., virDomainGraphicsListenDef have two fields with 'formathook', which are 'address' and 'socket'. The xmlgen will generate the declaration of some hooks for formatting these fields and developers should implement them.
1) Check the declaration of hook by a commandline.
# ./scripts/xmlgen/go show virDomainGraphicsListenDef -kf
int virDomainGraphicsListenDefFormatHook(const virDomainGraphicsListenDef *def, const void *parent, const void *opaque, virBufferPtr addressBuf, virBufferPtr socketBuf);
bool virDomainGraphicsListenDefCheckHook(const virDomainGraphicsListenDef *def, const void *parent, void *opaque, bool result);
2) Implement these two hooks in src/conf/domain_conf.c.
2.1) virXXXFormatHook It is the hook for formatting field 'address' and 'socket'. The 'addressBuf' and 'socketBuf' are used for output destinations respectively.
2.2) virXXXCheckHook For structs, the xmlgen generates virXXXCheck function to come with the virXXXFormatBuf. The virXXXCheck reports whether the corresponding XML element is null.
The virXXXCheckHook intercepts the 'result' of virXXXCheck. It changes 'result' or just forwards it according to those fields with 'formathook'.
Thanks!
Shi Lei (46): scripts: Add a tool to generate xml parse/format functions maint: Check python3-clang maint: Call xmlgen automatically when c-head-files change util: Add some xml-helper-functions to cooperate with xmlgen util: Add helper functions for 'bool' and 'time_t' and cooperate with xmlgen util: Add parsexml/formatbuf helper functions for virSocketAddr conf: Extract error-checking code from virNetworkDNSTxtDefParseXML conf: Replace virNetworkDNSTxtDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSTxtDefFormatBuf conf: Extract error-checking code from virNetworkDNSSrvDefParseXML conf: Replace virNetworkDNSSrvDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSSrvDefFormatBuf conf: Extract error-checking code from virNetworkDNSHostDefParseXML conf: Replace virNetworkDNSHostDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSHostDefFormatBuf conf: Extract virNetworkDNSForwarderParseXML from virNetworkDNSParseXML conf: Replace virNetworkDNSForwarderParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSForwarderFormatBuf conf: Extract error-checking code from virNetworkDNSDefParseXML conf: Replace virNetworkDNSDefParseXML(hardcoded) with namesake(generated) conf: Generate virNetworkDNSDefFormatBuf conf: Extract embedded structs from virDomainGraphicsDef as standalone structs conf: Replace virDomainGraphicsDefParseXMLSDL(hardcoded) with virDomainGraphicsSDLDefParseXML(generated) conf: Generate format functions for virDomainGraphicsSDLDef conf: Replace virDomainGraphicsAuthDefParseXML(hardcoded) with namesake(generated) conf: Generate format functions for virDomainGraphicsAuthDef conf: Extract error-checking code from virDomainGraphicsDefParseXMLVNC conf: Replace virDomainGraphicsDefParseXMLVNC(hardcoded) with virDomainGraphicsVNCDefParseXML(generated) conf: Generate virDomainGraphicsVNCDefFormatAttr conf: Extract error-checking code from virDomainGraphicsDefParseXMLRDP conf: Replace virDomainGraphicsDefParseXMLRDP(hardcoded) with virDomainGraphicsRDPDefParseXML(generated) conf: Generate virDomainGraphicsRDPDefFormatAttr conf: Replace virDomainGraphicsDefParseXMLDesktop(hardcoded) with virDomainGraphicsDesktopDefParseXML(generated) conf: Generate virDomainGraphicsDesktopDefFormatAttr conf: Add virSpiceChannelDef to help to parse the member 'channels' of virDomainGraphicsSpiceDef conf: Extract error-checking code from virDomainGraphicsDefParseXMLSpice conf: Replace virDomainGraphicsDefParseXMLSpice(hardcoded) with virDomainGraphicsSpiceDefParseXML(generated) conf: Generate virDomainGraphicsSpiceDefFormatElem and virDomainGraphicsSpiceDefFormatAttr conf: Replace virDomainGraphicsDefParseXMLEGLHeadless(hardcoded) with virDomainGraphicsEGLHeadlessDefParseXML(generated) conf: Generate virDomainGraphicsEGLHeadlessDefFormatElem conf: Extract error-checking code from virDomainGraphicsListenDefParseXML conf: Replace virDomainGraphicsListenDefParseXML(hardcoded) with namesake(generated) conf: Generate virDomainGraphicsListenDefFormatBuf conf: Extract error-checking code from virDomainGraphicsDefParseXML conf: Replace virDomainGraphicsDefParseXML(hardcoded) with namesake(generated) conf: Replace virDomainGraphicsDefFormat(hardcoded) with virDomainGraphicsDefFormatBuf(generated)
meson.build | 5 + po/POTFILES.in | 3 + scripts/meson.build | 8 + scripts/xmlgen/directive.py | 1115 ++++++++++++++++++++ scripts/xmlgen/go | 7 + scripts/xmlgen/main.py | 439 ++++++++ scripts/xmlgen/utils.py | 121 +++ src/conf/domain_conf.c | 1650 +++++++++--------------------- src/conf/domain_conf.h | 179 ++-- src/conf/meson.build | 41 + src/conf/network_conf.c | 467 ++------- src/conf/network_conf.h | 54 +- src/conf/virconftypes.h | 18 + src/libvirt_private.syms | 9 + src/meson.build | 6 + src/qemu/qemu_command.c | 4 + src/qemu/qemu_driver.c | 2 + src/qemu/qemu_hotplug.c | 2 + src/qemu/qemu_migration_cookie.c | 1 + src/qemu/qemu_process.c | 5 + src/qemu/qemu_validate.c | 2 + src/util/virsocketaddr.c | 42 + src/util/virsocketaddr.h | 26 +- src/util/virstring.c | 57 ++ src/util/virstring.h | 9 + src/util/virxml.c | 105 ++ src/util/virxml.h | 6 + src/vmx/vmx.c | 1 + tests/meson.build | 1 + tools/meson.build | 2 + 30 files changed, 2738 insertions(+), 1649 deletions(-) create mode 100644 scripts/xmlgen/directive.py create mode 100755 scripts/xmlgen/go create mode 100755 scripts/xmlgen/main.py create mode 100644 scripts/xmlgen/utils.py
-- 2.25.1

On Fri, Sep 04, 2020 at 11:34:52AM +0800, Shi Lei wrote:
V1 here: [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]
For those new and changed directives, illustrate them by an example:
struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ char *passwd; /* xmlattr, formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE */ bool expires; time_t validTo; /* xmlattr:passwdValidTo, specified:expires */ virDomainGraphicsAuthConnectedType connected; /* xmlattr */ };
struct _virDomainGraphicsListenDef { /* genparse:withhook, genformat */ virDomainGraphicsListenType type; /* xmlattr */ char *address; /* xmlattr, formathook */ char *network; /* xmlattr, formatflag:VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK */ char *socket; /* xmlattr, formathook */ int fromConfig; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ bool autoGenerated; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ };
struct _virDomainGraphicsSDLDef { /* genparse, genformat:separate */ char *display; /* xmlattr */ char *xauth; /* xmlattr */ bool fullscreen; /* xmlattr */ virTristateBool gl; /* xmlattr:gl/enable */ };
struct _virDomainGraphicsDef { /* genparse:concisehook, genformat */ virObjectPtr privateData; virDomainGraphicsType type; /* xmlattr */
size_t nListens; virDomainGraphicsListenDefPtr listens; /* xmlelem, array:nListens */
union { virDomainGraphicsSDLDef sdl; /* xmlgroup */ virDomainGraphicsVNCDef vnc; /* xmlgroup */ virDomainGraphicsRDPDef rdp; /* xmlgroup */ virDomainGraphicsDesktopDef desktop; /* xmlgroup */ virDomainGraphicsSpiceDef spice; /* xmlgroup */ virDomainGraphicsEGLHeadlessDef egl_headless; /* xmlgroup */ } data; /* xmlswitch:type */ };
Explanation for these directives:
- genformat[:separate|onlyattrs|onlyelems]
Only work on a struct. Generate formatbuf function for this struct only if 'genformat' is specified. The function name is based on struct-name and suffixed with 'FormatBuf'.
When 'genformat:separate' is specified, generate two formatbuf functions rather than a single full-mode formatbuf function. One for formatting attributes and another for formatting elements. These function names are based on struct-name and suffixed with 'FormatAttr' and 'FormatElem' respectively.
The 'onlyattrs' and 'onlyelems' are just like 'separate', but only generate one of those two functions according to its denotation.
- xmlattr[:[parentname/]thename]
Parse/Format the field as an XML attribute or attribute wrapped by an XML element. If only 'thename' is specified, use it as the XML attribute name; or use the filed name. The 'parentname' is the name of the attribute's parent element. If 'parentname/thename' is specified, the corresponding form is <parentname thename='..' />.
- xmlgroup
The field is a struct, but its corresponding form in XML is a group rather than an element.
- xmlswitch:thename
Only for discriminated union. 'thename' is the name of its relative enum. The name of each union member should match a shortname of the enum.
- array[:countername]
Parse/Format the field as an array. Each array field must have an related counter field, which name is specified by 'countername'. If 'countername' is omitted, follow the pattern: n + 'field_name'.
- specified[:thename]
This field has an related field to indicate its existence, and 'thename' specifies the name of this related field. When 'thename' is omitted, follow the pattern: 'field_name' + '_specified'.
- formatflag:[!|%]flag
This field will be formatted and written out to XML only if the 'flag' hits a target flagset. The target flagset is passed into the formatbuf function through the argument 'opaque'.
Adding a '!' before 'flag' means NOT hitting.
Adding a '%' before 'flag' means that flag hitting-check is the unique condition for formatting this field. For example, for 'passwd' in 'virDomainGraphicsAuthDef', the directive is:
formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE
then the generated code:
if (def->passwd && (virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE)) virBufferEscapeString(buf, " passwd='%s'", def->passwd);
If '%' is inserted like this:
formatflag:%VIR_DOMAIN_DEF_FORMAT_SECURE
then the generated code:
if ((virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE)) virBufferEscapeString(buf, " passwd='%s'", def->passwd);
- formathook Introduce hooks to handle the field if xmlgen can't deal with it now.
E.g., virDomainGraphicsListenDef have two fields with 'formathook', which are 'address' and 'socket'. The xmlgen will generate the declaration of some hooks for formatting these fields and developers should implement them.
1) Check the declaration of hook by a commandline.
# ./scripts/xmlgen/go show virDomainGraphicsListenDef -kf
int virDomainGraphicsListenDefFormatHook(const virDomainGraphicsListenDef *def, const void *parent, const void *opaque, virBufferPtr addressBuf, virBufferPtr socketBuf);
bool virDomainGraphicsListenDefCheckHook(const virDomainGraphicsListenDef *def, const void *parent, void *opaque, bool result);
2) Implement these two hooks in src/conf/domain_conf.c.
2.1) virXXXFormatHook It is the hook for formatting field 'address' and 'socket'. The 'addressBuf' and 'socketBuf' are used for output destinations respectively.
2.2) virXXXCheckHook For structs, the xmlgen generates virXXXCheck function to come with the virXXXFormatBuf. The virXXXCheck reports whether the corresponding XML element is null.
The virXXXCheckHook intercepts the 'result' of virXXXCheck. It changes 'result' or just forwards it according to those fields with 'formathook'.
It is probably a good idea to put all this doucmentation from the cover letter into a docs/xmlgenerator.rst file as it'll be useful reference for developers in future.
meson.build | 5 + po/POTFILES.in | 3 + scripts/meson.build | 8 + scripts/xmlgen/directive.py | 1115 ++++++++++++++++++++ scripts/xmlgen/go | 7 + scripts/xmlgen/main.py | 439 ++++++++ scripts/xmlgen/utils.py | 121 +++ src/conf/domain_conf.c | 1650 +++++++++--------------------- src/conf/domain_conf.h | 179 ++-- src/conf/meson.build | 41 + src/conf/network_conf.c | 467 ++------- src/conf/network_conf.h | 54 +- src/conf/virconftypes.h | 18 + src/libvirt_private.syms | 9 + src/meson.build | 6 + src/qemu/qemu_command.c | 4 + src/qemu/qemu_driver.c | 2 + src/qemu/qemu_hotplug.c | 2 + src/qemu/qemu_migration_cookie.c | 1 + src/qemu/qemu_process.c | 5 + src/qemu/qemu_validate.c | 2 + src/util/virsocketaddr.c | 42 + src/util/virsocketaddr.h | 26 +- src/util/virstring.c | 57 ++ src/util/virstring.h | 9 + src/util/virxml.c | 105 ++ src/util/virxml.h | 6 + src/vmx/vmx.c | 1 + tests/meson.build | 1 + tools/meson.build | 2 +
I think it'd be a good idea to have a test case to validate the XML generator. For example create a simpe tests/xmlgen/demo.h that illustrates all the different features we can use. Then have tests/xmlgen/demo.generated.{c,h}, and the write a test case that generates a new copy of demo.generated.{c,h} and compares to what we have in git. This will help us validate that changes to the xmlgenerator in future don't result in unexpected changes to the generated code. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 2020-12-05 at 01:41, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:34:52AM +0800, Shi Lei wrote:
V1 here: [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]
For those new and changed directives, illustrate them by an example:
struct _virDomainGraphicsAuthDef { /* genparse, genformat:separate */ char *passwd; /* xmlattr, formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE */ bool expires; time_t validTo; /* xmlattr:passwdValidTo, specified:expires */ virDomainGraphicsAuthConnectedType connected; /* xmlattr */ };
struct _virDomainGraphicsListenDef { /* genparse:withhook, genformat */ virDomainGraphicsListenType type; /* xmlattr */ char *address; /* xmlattr, formathook */ char *network; /* xmlattr, formatflag:VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK */ char *socket; /* xmlattr, formathook */ int fromConfig; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ bool autoGenerated; /* xmlattr, formatflag:%VIR_DOMAIN_DEF_FORMAT_STATUS */ };
struct _virDomainGraphicsSDLDef { /* genparse, genformat:separate */ char *display; /* xmlattr */ char *xauth; /* xmlattr */ bool fullscreen; /* xmlattr */ virTristateBool gl; /* xmlattr:gl/enable */ };
struct _virDomainGraphicsDef { /* genparse:concisehook, genformat */ virObjectPtr privateData; virDomainGraphicsType type; /* xmlattr */
size_t nListens; virDomainGraphicsListenDefPtr listens; /* xmlelem, array:nListens */
union { virDomainGraphicsSDLDef sdl; /* xmlgroup */ virDomainGraphicsVNCDef vnc; /* xmlgroup */ virDomainGraphicsRDPDef rdp; /* xmlgroup */ virDomainGraphicsDesktopDef desktop; /* xmlgroup */ virDomainGraphicsSpiceDef spice; /* xmlgroup */ virDomainGraphicsEGLHeadlessDef egl_headless; /* xmlgroup */ } data; /* xmlswitch:type */ };
Explanation for these directives:
- genformat[:separate|onlyattrs|onlyelems]
Only work on a struct. Generate formatbuf function for this struct only if 'genformat' is specified. The function name is based on struct-name and suffixed with 'FormatBuf'.
When 'genformat:separate' is specified, generate two formatbuf functions rather than a single full-mode formatbuf function. One for formatting attributes and another for formatting elements. These function names are based on struct-name and suffixed with 'FormatAttr' and 'FormatElem' respectively.
The 'onlyattrs' and 'onlyelems' are just like 'separate', but only generate one of those two functions according to its denotation.
- xmlattr[:[parentname/]thename]
Parse/Format the field as an XML attribute or attribute wrapped by an XML element. If only 'thename' is specified, use it as the XML attribute name; or use the filed name. The 'parentname' is the name of the attribute's parent element. If 'parentname/thename' is specified, the corresponding form is <parentname thename='..' />.
- xmlgroup
The field is a struct, but its corresponding form in XML is a group rather than an element.
- xmlswitch:thename
Only for discriminated union. 'thename' is the name of its relative enum. The name of each union member should match a shortname of the enum.
- array[:countername]
Parse/Format the field as an array. Each array field must have an related counter field, which name is specified by 'countername'. If 'countername' is omitted, follow the pattern: n + 'field_name'.
- specified[:thename]
This field has an related field to indicate its existence, and 'thename' specifies the name of this related field. When 'thename' is omitted, follow the pattern: 'field_name' + '_specified'.
- formatflag:[!|%]flag
This field will be formatted and written out to XML only if the 'flag' hits a target flagset. The target flagset is passed into the formatbuf function through the argument 'opaque'.
Adding a '!' before 'flag' means NOT hitting.
Adding a '%' before 'flag' means that flag hitting-check is the unique condition for formatting this field. For example, for 'passwd' in 'virDomainGraphicsAuthDef', the directive is:
formatflag:VIR_DOMAIN_DEF_FORMAT_SECURE
then the generated code:
if (def->passwd && (virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE)) virBufferEscapeString(buf, " passwd='%s'", def->passwd);
If '%' is inserted like this:
formatflag:%VIR_DOMAIN_DEF_FORMAT_SECURE
then the generated code:
if ((virXMLFlag(opaque) & VIR_DOMAIN_DEF_FORMAT_SECURE)) virBufferEscapeString(buf, " passwd='%s'", def->passwd);
- formathook Introduce hooks to handle the field if xmlgen can't deal with it now.
E.g., virDomainGraphicsListenDef have two fields with 'formathook', which are 'address' and 'socket'. The xmlgen will generate the declaration of some hooks for formatting these fields and developers should implement them.
1) Check the declaration of hook by a commandline.
# ./scripts/xmlgen/go show virDomainGraphicsListenDef -kf
int virDomainGraphicsListenDefFormatHook(const virDomainGraphicsListenDef *def, const void *parent, const void *opaque, virBufferPtr addressBuf, virBufferPtr socketBuf);
bool virDomainGraphicsListenDefCheckHook(const virDomainGraphicsListenDef *def, const void *parent, void *opaque, bool result);
2) Implement these two hooks in src/conf/domain_conf.c.
2.1) virXXXFormatHook It is the hook for formatting field 'address' and 'socket'. The 'addressBuf' and 'socketBuf' are used for output destinations respectively.
2.2) virXXXCheckHook For structs, the xmlgen generates virXXXCheck function to come with the virXXXFormatBuf. The virXXXCheck reports whether the corresponding XML element is null.
The virXXXCheckHook intercepts the 'result' of virXXXCheck. It changes 'result' or just forwards it according to those fields with 'formathook'.
It is probably a good idea to put all this doucmentation from the cover letter into a docs/xmlgenerator.rst file as it'll be useful reference for developers in future.
Okay. I'll do it.
meson.build | 5 + po/POTFILES.in | 3 + scripts/meson.build | 8 + scripts/xmlgen/directive.py | 1115 ++++++++++++++++++++ scripts/xmlgen/go | 7 + scripts/xmlgen/main.py | 439 ++++++++ scripts/xmlgen/utils.py | 121 +++ src/conf/domain_conf.c | 1650 +++++++++--------------------- src/conf/domain_conf.h | 179 ++-- src/conf/meson.build | 41 + src/conf/network_conf.c | 467 ++------- src/conf/network_conf.h | 54 +- src/conf/virconftypes.h | 18 + src/libvirt_private.syms | 9 + src/meson.build | 6 + src/qemu/qemu_command.c | 4 + src/qemu/qemu_driver.c | 2 + src/qemu/qemu_hotplug.c | 2 + src/qemu/qemu_migration_cookie.c | 1 + src/qemu/qemu_process.c | 5 + src/qemu/qemu_validate.c | 2 + src/util/virsocketaddr.c | 42 + src/util/virsocketaddr.h | 26 +- src/util/virstring.c | 57 ++ src/util/virstring.h | 9 + src/util/virxml.c | 105 ++ src/util/virxml.h | 6 + src/vmx/vmx.c | 1 + tests/meson.build | 1 + tools/meson.build | 2 +
I think it'd be a good idea to have a test case to validate the XML generator. For example create a simpe tests/xmlgen/demo.h that illustrates all the different features we can use.
Then have tests/xmlgen/demo.generated.{c,h}, and the write a test case that generates a new copy of demo.generated.{c,h} and compares to what we have in git.
This will help us validate that changes to the xmlgenerator in future don't result in unexpected changes to the generated code.
Yes. I agree. Regards, Shi Lei
Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On Fri, Sep 04, 2020 at 11:34:52AM +0800, Shi Lei wrote:
V1 here: [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]
Differ from V1:
* Move the generator into scripts/xmlgen and rename it 'xmlgen'.
* Declare virXMLChildNode and virXMLChildNodeSet in libvirt_private.syms.
* Replace VIR_FREE with g_free and VIR_ALLOC[_N] with g_new0.
* Adjust virReportError to avoid unnecessary translation.
* Remove the macro VIR_USED and use G_GNUC_UNUSED to declare arguments.
* When parsing string member, assign value to it directly instead of using middle variable.
* Don't set libclang_path. Just use python-clang's default setting.
* Use virEscapeString for escaping xml characters.
* Enable directive 'genformat' with a parameter to support separation mode.
* Add directive 'xmlswitch' and 'xmlgroup' to support discriminated unions.
* Allow directive 'array' and 'specified' to carry with a parameter, which specifies its counterpart explicitly.
* Enable directive 'xmlattr' with path.
* Add directive 'formatflag' and 'formathook'.
I'm very sorry for the extremely long delay in reviewing this new series. Overall I like this series and would like to see how we can focus on getting at least part of it merged. It is interesting to see how it is forcing a separation of the parsing and validation of data. I also like that it is making the code more consistent in style. The main blocker I think is that we need to temporarily commit the generated files to git, and not run the generator during normal builds, until we ditch RHEL-7 support in April 2021. This should be quite easy to deal with. The next thing is that wierd need to insert a "_NONE" field in to all the enums. I'm not understanding what reqiures this, but I think we need to come up with a different solution to whatever the problem is. It looks like this only affects the DomainGraphics conversion in your series. So we could start by only merging the Network conversion patches, until we figure out a solution to avoid adding _NONE fields. That's it for the main blockers to merge from my POV. After that I think a priority is to get some test coverage of the generator script, and to add the docs about the how the generator annotations work. This can be done after the initial merge. Then there's a large ongoing work to actually convert everything, but there's no rush to finish that, as you've shown that we can do it incrementally. So if you want to re-post an update of the series with the few blocking items addressed, then personally I'd look at trying to merge that unless other people have objections they want to raise that can't be solved after merging the initial support. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 2020-12-05 at 02:55, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:34:52AM +0800, Shi Lei wrote:
V1 here: [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]
Differ from V1:
* Move the generator into scripts/xmlgen and rename it 'xmlgen'.
* Declare virXMLChildNode and virXMLChildNodeSet in libvirt_private.syms.
* Replace VIR_FREE with g_free and VIR_ALLOC[_N] with g_new0.
* Adjust virReportError to avoid unnecessary translation.
* Remove the macro VIR_USED and use G_GNUC_UNUSED to declare arguments.
* When parsing string member, assign value to it directly instead of using middle variable.
* Don't set libclang_path. Just use python-clang's default setting.
* Use virEscapeString for escaping xml characters.
* Enable directive 'genformat' with a parameter to support separation mode.
* Add directive 'xmlswitch' and 'xmlgroup' to support discriminated unions.
* Allow directive 'array' and 'specified' to carry with a parameter, which specifies its counterpart explicitly.
* Enable directive 'xmlattr' with path.
* Add directive 'formatflag' and 'formathook'.
I'm very sorry for the extremely long delay in reviewing this new series.
Overall I like this series and would like to see how we can focus on getting at least part of it merged. It is interesting to see how it is forcing a separation of the parsing and validation of data. I also like that it is making the code more consistent in style.
The main blocker I think is that we need to temporarily commit the generated files to git, and not run the generator during normal builds, until we ditch RHEL-7 support in April 2021. This should be quite easy to deal with.
The next thing is that wierd need to insert a "_NONE" field in to all the enums. I'm not understanding what reqiures this, but I think we need to come up with a different solution to whatever the problem is. It looks like this only affects the DomainGraphics conversion in your series. So we could start by only merging the Network conversion patches, until we figure out a solution to avoid adding _NONE fields.
That's it for the main blockers to merge from my POV.
After that I think a priority is to get some test coverage of the generator script, and to add the docs about the how the generator annotations work. This can be done after the initial merge.
Then there's a large ongoing work to actually convert everything, but there's no rush to finish that, as you've shown that we can do it incrementally.
So if you want to re-post an update of the series with the few blocking items addressed, then personally I'd look at trying to merge that unless other people have objections they want to raise that can't be solved after merging the initial support.
Regards, Daniel --
Thanks for reviewing this series. For the first blocker, I think we can wait until April 2021, since we have spent a lot time on it. According to your comment, I think I can make some preparations for the initial merge during these 4~5 months. (1) Implement some testcases for the generator to make sure its proper functions in the future. (2) Add some docs to explain and demonstrate this generator. My English is not well, so I will try my best to do it and I may need a bit long time. (3) For the 'default' item of enums, I think we can add an extra directive to direct the generator how to generate code for enum, as I mentioned in the last email. Or we can continue to talk about other solutions. (4) Next series will only include conversion about network, so that we can focus on the docs, testcases and this generator itself. I plan to finish these jobs and post the next series in the next month, which will be still a RFC. So you and other people can have sufficent time to review and comment it and I will have sufficent time to improve it :-) Regards, Shi Lei

On Mon, Dec 07, 2020 at 05:11:42PM +0800, Shi Lei wrote:
On 2020-12-05 at 02:55, DanielP. Berrangé wrote:
On Fri, Sep 04, 2020 at 11:34:52AM +0800, Shi Lei wrote:
V1 here: [https://www.redhat.com/archives/libvir-list/2020-June/msg00357.html]
Differ from V1:
* Move the generator into scripts/xmlgen and rename it 'xmlgen'.
* Declare virXMLChildNode and virXMLChildNodeSet in libvirt_private.syms.
* Replace VIR_FREE with g_free and VIR_ALLOC[_N] with g_new0.
* Adjust virReportError to avoid unnecessary translation.
* Remove the macro VIR_USED and use G_GNUC_UNUSED to declare arguments.
* When parsing string member, assign value to it directly instead of using middle variable.
* Don't set libclang_path. Just use python-clang's default setting.
* Use virEscapeString for escaping xml characters.
* Enable directive 'genformat' with a parameter to support separation mode.
* Add directive 'xmlswitch' and 'xmlgroup' to support discriminated unions.
* Allow directive 'array' and 'specified' to carry with a parameter, which specifies its counterpart explicitly.
* Enable directive 'xmlattr' with path.
* Add directive 'formatflag' and 'formathook'.
I'm very sorry for the extremely long delay in reviewing this new series.
Overall I like this series and would like to see how we can focus on getting at least part of it merged. It is interesting to see how it is forcing a separation of the parsing and validation of data. I also like that it is making the code more consistent in style.
The main blocker I think is that we need to temporarily commit the generated files to git, and not run the generator during normal builds, until we ditch RHEL-7 support in April 2021. This should be quite easy to deal with.
The next thing is that wierd need to insert a "_NONE" field in to all the enums. I'm not understanding what reqiures this, but I think we need to come up with a different solution to whatever the problem is. It looks like this only affects the DomainGraphics conversion in your series. So we could start by only merging the Network conversion patches, until we figure out a solution to avoid adding _NONE fields.
That's it for the main blockers to merge from my POV.
After that I think a priority is to get some test coverage of the generator script, and to add the docs about the how the generator annotations work. This can be done after the initial merge.
Then there's a large ongoing work to actually convert everything, but there's no rush to finish that, as you've shown that we can do it incrementally.
So if you want to re-post an update of the series with the few blocking items addressed, then personally I'd look at trying to merge that unless other people have objections they want to raise that can't be solved after merging the initial support.
Regards, Daniel --
Thanks for reviewing this series.
For the first blocker, I think we can wait until April 2021, since we have spent a lot time on it.
According to your comment, I think I can make some preparations for the initial merge during these 4~5 months.
(1) Implement some testcases for the generator to make sure its proper functions in the future.
(2) Add some docs to explain and demonstrate this generator. My English is not well, so I will try my best to do it and I may need a bit long time.
(3) For the 'default' item of enums, I think we can add an extra directive to direct the generator how to generate code for enum, as I mentioned in the last email. Or we can continue to talk about other solutions.
(4) Next series will only include conversion about network, so that we can focus on the docs, testcases and this generator itself.
I plan to finish these jobs and post the next series in the next month, which will be still a RFC. So you and other people can have sufficent time to review and comment it and I will have sufficent time to improve it :-)
Ok, if you're happy to take the time until April, that's fine with me too. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
participants (3)
-
Daniel P. Berrangé
-
Shi Lei
-
石磊