diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/sbus/codegen | |
parent | Initial commit. (diff) | |
download | sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip |
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/sbus/codegen')
22 files changed, 4630 insertions, 0 deletions
diff --git a/src/sbus/codegen/dbus.xml b/src/sbus/codegen/dbus.xml new file mode 100644 index 0000000..e5bec2f --- /dev/null +++ b/src/sbus/codegen/dbus.xml @@ -0,0 +1,100 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node name="/org/freedesktop/sssd"> + <interface name="org.freedesktop.DBus"> + <annotation name="codegen.Name" value="DBus" /> + <annotation name="codegen.Caller" value="false" /> + <method name="Hello"> + <annotation name="codegen.AsyncCaller" value="true" /> + <arg type="s" name="name" direction="out" /> + </method> + <method name="RequestName"> + <annotation name="codegen.AsyncCaller" value="true" /> + <arg type="s" name="name" direction="in" /> + <arg type="u" name="flags" direction="in" /> + <arg type="u" name="result" direction="out" /> + </method> + <method name="ReleaseName"> + <arg type="s" name="name" direction="in" /> + <arg type="u" name="result" direction="out" /> + </method> + <method name="NameHasOwner"> + <arg type="s" name="name" direction="in" key="1" /> + <arg type="b" name="result" direction="out" /> + </method> + <method name="ListNames" key="True"> + <arg type="as" name="names" direction="out" /> + </method> + <method name="ListActivatableNames" key="True"> + <arg type="as" name="names" direction="out" /> + </method> + <method name="AddMatch"> + <arg type="s" name="rule" direction="in" /> + </method> + <method name="RemoveMatch"> + <arg type="s" name="rule" direction="in" /> + </method> + <method name="GetNameOwner"> + <arg type="s" name="name" direction="in" key="1" /> + <arg type="s" name="unique_name" direction="out" /> + </method> + <method name="ListQueuedOwners"> + <arg type="s" name="name" direction="in" key="1" /> + <arg type="as" name="unique_names" direction="out" /> + </method> + <method name="GetConnectionUnixUser"> + <annotation name="codegen.AsyncCaller" value="true" /> + <arg type="s" name="name" direction="in" key="1" /> + <arg type="u" name="uid" direction="out" /> + </method> + <method name="GetConnectionUnixProcessID"> + <arg type="s" name="name" direction="in" key="1" /> + <arg type="u" name="pid" direction="out" /> + </method> + <method name="GetId" key="True"> + <arg type="s" name="bus_name" direction="out" /> + </method> + <method name="StartServiceByName"> + <arg type="s" name="name" direction="in" key="1" /> + <arg type="u" name="flags" direction="in" /> + <arg type="u" name="result" direction="out" /> + </method> + <signal name="NameOwnerChanged"> + <arg type="s" name="name" /> + <arg type="s" name="new_owner" /> + <arg type="s" name="old_owner" /> + </signal> + <signal name="NameAcquired"> + <arg type="s" name="name" /> + </signal> + <signal name="NameLost"> + <arg type="s" name="name" /> + </signal> + </interface> + <interface name="org.freedesktop.DBus.Introspectable"> + <annotation name="codegen.Caller" value="false" /> + <method name="Introspect"> + <arg type="s" name="introspection" direction="out" key="1" /> + </method> + </interface> + <interface name="org.freedesktop.DBus.Properties"> + <annotation name="codegen.Name" value="DBusProperties" /> + <method name="Get"> + <annotation name="codegen.CustomOutputHandler" value="true" /> + <arg type="s" name="interface_name" direction="in" key="1" /> + <arg type="s" name="property_name" direction="in" key="2" /> + <arg type="v" name="property_value" direction="out" /> + </method> + <method name="Set"> + <annotation name="codegen.CustomInputHandler" value="true" /> + <arg type="s" name="interface_name" direction="in" /> + <arg type="s" name="property_name" direction="in" /> + <arg type="v" name="new_value" direction="in" /> + </method> + <method name="GetAll"> + <annotation name="codegen.CustomOutputHandler" value="true" /> + <arg type="s" name="interface_name" direction="in" key="1" /> + <arg type="a{sv}" name="properties" direction="out" /> + </method> + </interface> +</node> diff --git a/src/sbus/codegen/sbus_CodeGen.py b/src/sbus/codegen/sbus_CodeGen.py new file mode 100644 index 0000000..b799da9 --- /dev/null +++ b/src/sbus/codegen/sbus_CodeGen.py @@ -0,0 +1,323 @@ +# +# Authors: +# Pavel Brezina <pbrezina@redhat.com> +# +# Copyright (C) 2017 Red Hat +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import os +import argparse +from collections import OrderedDict +from sbus_Introspection import Introspectable +from sbus_Template import TemplateFile +from sbus_Generator import Generator +from sbus_DataType import DataType + + +class CodeGen: + """ + Code generator support multiple introspection annotations that can + modify the generator behavior per interface, method or property. + + * Annotations on methods: + - codegen.CustomHandler + - boolean, default is false + - custom input and output handler + - codegen.CustomInputHandler + - boolean, default is false + - handler parses its input parameters manually + - codegen.CustomOutputHandler + - boolean, default is false + - handler parses its output parameters manually + + * Annotations on interfaces, methods or properties: + - codegen.Name + - string, default is not set + - Name used to override member name when generating caller + - codegen.Caller + - boolean, default is true + - Generate both synchronous and asynchronous callers + - codegen.SyncCaller + - boolean, default is true + - Generate synchronous callers + - codegen.AsyncCaller + - boolean, default is true + - Generate asynchronous callers + """ + def __init__(self, options): + # Temporarily change working directory so we can load the templates + self.options = options + self.templates = CodeGen.Templates(options) + self.interfaces = OrderedDict() + return + + def add(self, introspection_file): + interfaces = Introspectable.Introspect(introspection_file) + merged = self.interfaces + + for name, interface in interfaces.items(): + if name in self.interfaces: + raise ValueError("Interface %s already exist!" % name) + merged[name] = interface + + self.interfaces = OrderedDict(sorted(merged.items())) + return + + def generate(self): + Generator.GenerateCode(self.templates, self.interfaces) + + class Options: + def __init__(self, + SbusHeadersPath, + UtilHeadersPath, + GeneratedHeadersPath, + WritePath, + FilePrefix, + SymbolPrefix, + IncludeHeaders): + self.SbusHeadersPath = SbusHeadersPath + self.UtilHeadersPath = UtilHeadersPath + self.GeneratedHeadersPath = GeneratedHeadersPath + self.WritePath = WritePath + self.FilePrefix = FilePrefix + self.SymbolPrefix = SymbolPrefix + self.IncludeHeaders = [] + + if IncludeHeaders is not None: + self.IncludeHeaders = IncludeHeaders + + self.AbsolutePath = os.path.dirname(os.path.realpath(__file__)) + return + + def path(self, relative_path): + return "%s/%s" % (self.AbsolutePath, relative_path) + + class Templates: + GeneratedFiles = [ + "interface.h", + "symbols.c", + "symbols.h", + "arguments.c", + "arguments.h", + "invokers.c", + "invokers.h", + "keygens.c", + "keygens.h", + "client_properties.h", + "client_async.c", + "client_async.h", + "client_sync.c", + "client_sync.h", + "server.h" + ] + + def __init__(self, options): + self.files = {} + for file in self.GeneratedFiles: + self.files[file] = self.File(file, options) + + def get(self, name): + return self.files[name].template + + def write(self): + for file in self.files.values(): + file.write() + + class File: + def __init__(self, name, options): + self.options = options + + self.name = name + self.outputFile = self.getAbsFilePath(name) + + self.template = TemplateFile( + options.path('./templates/' + name + '.tpl') + ) + self.setHeader() + + def write(self): + self.setFooter() + self.template.write(self.outputFile, self.postprocess) + + def setHeader(self): + if not self.template.has("file-header"): + return + + tpl = self.template.get("file-header") + + for header in self.options.IncludeHeaders: + tpl.add("custom-type-header", + {'custom-type-header': header}) + + keys = {'sbus-path': self.options.SbusHeadersPath, + 'util-path': self.options.UtilHeadersPath, + 'file-guard': self.getHeaderGuard(self.name)} + + for file in CodeGen.Templates.GeneratedFiles: + if not file.endswith(".h"): + continue + + name = file.replace(".h", "") + keys["header:" + name] = self.getRelFilePath(file) + + tpl.set(keys) + + def setFooter(self): + if not self.template.has("file-footer"): + return + + keys = {'file-guard': self.getHeaderGuard(self.name)} + + self.template.get("file-footer").set(keys) + + def getHeaderGuard(self, name): + guard = "_%s%s_" % (self.options.FilePrefix, name) + return guard.replace('.', '_').upper() + + def getAbsFilePath(self, name): + return "%s/%s%s" % \ + (self.options.WritePath, + self.options.FilePrefix, + name) + + def getRelFilePath(self, name): + return "%s/%s%s" % \ + (self.options.GeneratedHeadersPath, + self.options.FilePrefix, + name) + + def postprocess(self, output): + if self.options.SymbolPrefix is None: + return output + + return output.replace("_sbus_", + "_sbus_%s_" % self.options.SymbolPrefix) + + +def InitializeDataTypes(): + """Define which D-Bus types are supported by code generator.""" + + # Standard types + DataType.Create("y", "uint8_t", '" PRIu8 "') + DataType.Create("b", "bool", "d") + DataType.Create("n", "int16_t", '" PRId16 "') + DataType.Create("q", "uint16_t", '" PRIu16 "') + DataType.Create("i", "int32_t", '" PRId32 "') + DataType.Create("u", "uint32_t", '" PRIu32 "') + DataType.Create("x", "int64_t", '" PRId64 "') + DataType.Create("t", "uint64_t", '" PRIu64 "') + DataType.Create("d", "double", "f") + + # String types + DataType.Create("s", "const char *", "s", DBusType="s", RequireTalloc=True) + DataType.Create("S", "char *", "s", DBusType="s", RequireTalloc=True) + DataType.Create("o", "const char *", "s", DBusType="o", RequireTalloc=True) + DataType.Create("O", "char *", "s", DBusType="o", RequireTalloc=True) + + # Array types + DataType.Create("ay", "uint8_t *", RequireTalloc=True) + DataType.Create("ab", "bool *", RequireTalloc=True) + DataType.Create("an", "int16_t *", RequireTalloc=True) + DataType.Create("aq", "uint16_t *", RequireTalloc=True) + DataType.Create("ai", "int32_t *", RequireTalloc=True) + DataType.Create("au", "uint32_t *", RequireTalloc=True) + DataType.Create("ax", "int64_t *", RequireTalloc=True) + DataType.Create("at", "uint64_t *", RequireTalloc=True) + DataType.Create("ad", "double *", RequireTalloc=True) + + # String arrays + DataType.Create("as", "const char **", DBusType="as", RequireTalloc=True) + DataType.Create("aS", "char **", DBusType="as", RequireTalloc=True) + DataType.Create("ao", "const char **", DBusType="ao", RequireTalloc=True) + DataType.Create("aO", "char **", DBusType="ao", RequireTalloc=True) + + # Custom types + DataType.Create("pam_data", "struct pam_data *", + DBusType="issssssuayuayiu", RequireTalloc=True) + DataType.Create("pam_response", "struct pam_data *", + DBusType="uua(uay)", RequireTalloc=True) + DataType.Create("ifp_extra", "hash_table_t *", + DBusType="a{sas}", RequireTalloc=True) + + +def main(): + InitializeDataTypes() + + parser = argparse.ArgumentParser( + description='Generate sbus server and client code.' + ) + + parser.add_argument( + 'introspection', nargs='+', + help="Path to introspection file" + ) + required = parser.add_argument_group('Required arguments') + required.add_argument( + '--sbus', action="store", dest="sbuspath", + help="Path to sbus header files as used in #include", + required=True + ) + required.add_argument( + '--util', action="store", dest="utilpath", + help="Path to util header files as used in #include", + required=True + ) + required.add_argument( + '--headers', action="store", dest="headerpath", + help="Path to generated header files as used in #include", + required=True + ) + required.add_argument( + '--dest', action="store", dest="destpath", + help="Path where the generated code will be stored", + required=True + ) + required.add_argument( + '--fileprefix', action="store", dest="fileprefix", + help="Name prefix for generated files", + required=True + ) + optional = parser.add_argument_group('Optional generator arguments') + optional.add_argument( + '--symbolprefix', action="store", dest="symbolprefix", + help="Name prefix for generated global symbols", + required=False + ) + optional.add_argument( + '-i', '--include', action='append', dest="include", + help="Include header with definition of custom types", + required=False + ) + cmdline = parser.parse_args() + + opts = CodeGen.Options( + SbusHeadersPath=cmdline.sbuspath, + UtilHeadersPath=cmdline.utilpath, + GeneratedHeadersPath=cmdline.headerpath, + WritePath=cmdline.destpath, + FilePrefix=cmdline.fileprefix, + SymbolPrefix=cmdline.symbolprefix, + IncludeHeaders=cmdline.include + ) + + codegen = CodeGen(opts) + for file in cmdline.introspection: + codegen.add(file) + codegen.generate() + + +if __name__ == "__main__": + main() diff --git a/src/sbus/codegen/sbus_DataType.py b/src/sbus/codegen/sbus_DataType.py new file mode 100644 index 0000000..5a90ca1 --- /dev/null +++ b/src/sbus/codegen/sbus_DataType.py @@ -0,0 +1,87 @@ +# +# Authors: +# Pavel Brezina <pbrezina@redhat.com> +# +# Copyright (C) 2017 Red Hat +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + + +class DataType: + """ Represents an SBus data type and its corresponding equivalents + in D-Bus and C code. + + SBus supports also custom types that can be parsed complex C types such + as hash tables or structures. In this case the SBus type may differ + from D-Bus type. + """ + available = {} + + def __init__(self, sbus_type, dbus_type, c_type, key_format, + require_talloc): + self.sbus_type = sbus_type + self.dbus_type = dbus_type + self.RequireTalloc = require_talloc + + # Printf formatter (without leading %) if the type supports keying + self.keyFormat = key_format + + # Input and output C types. For example 'int' and 'int*' + self.CType = c_type + self.inputCType = c_type + self.outputCType = c_type + "*" + + def __del__(self): + del DataType.available[self.sbus_type] + + def __str__(self): + return "%s == %s == %s" % (self.sbus_type, self.dbus_type, self.c_type) + + def __repr__(self): + return self.__str__() + + @staticmethod + def Find(sbus_type): + """ Find DataType object of given @sbus_type. + """ + if not (sbus_type in DataType.available): + raise ValueError(('Data type "%s" is not currently ' + 'supported by code generator') % sbus_type) + + return DataType.available[sbus_type] + + @staticmethod + def Create(sbus_type, c_type, KeyFormat=None, DBusType=None, + RequireTalloc=False): + """ Create a new SBus type. Specify DBusType if it differs from + the SBus type. Specify printf formatter KeyFormat if this type + can be used as a key. + """ + dbus_type = DBusType if DBusType is not None else sbus_type + + type = DataType(sbus_type, dbus_type, c_type, KeyFormat, RequireTalloc) + DataType.available[sbus_type] = type + + return type + + @staticmethod + def SBusToDBusType(sbus_type): + """ If possible convert SBus data type into D-Bus type. Otherwise + return unchanged input. + """ + if not (sbus_type in DataType.available): + return sbus_type + + return DataType.available[sbus_type].dbus_type diff --git a/src/sbus/codegen/sbus_Generator.py b/src/sbus/codegen/sbus_Generator.py new file mode 100644 index 0000000..b80bff1 --- /dev/null +++ b/src/sbus/codegen/sbus_Generator.py @@ -0,0 +1,868 @@ +# +# Authors: +# Pavel Brezina <pbrezina@redhat.com> +# +# Copyright (C) 2017 Red Hat +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +from collections import OrderedDict +from sbus_Invoker import Invoker, InvokerArgumentType, InvokerCaller, InvokerKeygen +from sbus_Introspection import SBus +from sbus_DataType import DataType + + +class Generator: + @staticmethod + def GenerateCode(templates, interfaces): + """ + Generate asynchronous code for given interfaces. + """ + + class Callers: + def __init__(self, interfaces, type): + self.Methods = InvokerCaller.GatherMethodInvokers(interfaces, + type) + self.Signals = InvokerCaller.GatherSignalInvokers(interfaces, + type) + self.Getters = InvokerCaller.GatherGetInvokers(interfaces, + type) + self.Setters = InvokerCaller.GatherSetInvokers(interfaces, + type) + + invokers = Invoker.GatherInvokers(interfaces) + arguments = InvokerArgumentType.GatherArgumentTypes(interfaces) + keygens = InvokerKeygen.GatherKeygens(interfaces) + sync_callers = Callers(interfaces, "sync") + async_callers = Callers(interfaces, "async") + + generators = [ + Generator.Interfaces(templates.get("interface.h"), + interfaces), + + Generator.Symbols(templates.get("symbols.c"), + templates.get("symbols.h"), + interfaces), + + Generator.Arguments(templates.get("arguments.c"), + templates.get("arguments.h"), + arguments), + + Generator.Invokers(templates.get("invokers.c"), + templates.get("invokers.h"), + invokers), + + Generator.Keygens(templates.get("keygens.c"), + templates.get("keygens.h"), + keygens), + + Generator.Properties(templates.get("client_properties.h"), + interfaces), + + Generator.MethodCalls(templates.get("client_async.c"), + templates.get("client_async.h"), + interfaces, "async", async_callers.Methods), + + Generator.SignalCalls(templates.get("client_async.c"), + templates.get("client_async.h"), + interfaces, "async", async_callers.Signals), + + Generator.PropertyCalls(templates.get("client_async.c"), + templates.get("client_async.h"), + interfaces, "async", + async_callers.Getters, + async_callers.Setters), + + Generator.MethodCalls(templates.get("client_sync.c"), + templates.get("client_sync.h"), + interfaces, "sync", sync_callers.Methods), + + Generator.SignalCalls(templates.get("client_sync.c"), + templates.get("client_sync.h"), + interfaces, "sync", sync_callers.Signals), + + Generator.PropertyCalls(templates.get("client_sync.c"), + templates.get("client_sync.h"), + interfaces, "sync", + sync_callers.Getters, + sync_callers.Setters) + ] + + for generator in generators: + generator.generate() + + templates.write() + + @staticmethod + def FilterAnnotations(annotations): + dict = OrderedDict() + if annotations is None or not annotations: + return dict + + for name, annotation in annotations.items(): + if not name.startswith("codegen."): + dict[name] = annotation + + return dict + + class Base(object): + """ + Base object for code generators. + + Children must implement generate() method. + """ + + def __init__(self): + pass + + def generate(self): + """ Make sure generate() method is implemented by children. """ + raise NotImplementedError("Method generate() is not implemented!") + + def tokenizeValue(self, *values): + """ Concatenate valus into C token. """ + name = "" + for value in values: + name += '.' + value + + return name.strip('.').replace('.', '_') + + def tokenizeName(self, *values): + """ Concatenate value.name into C token. """ + name = "" + for value in values: + name += '.' + value.name + + return name.strip('.').replace('.', '_') + + def setInputArguments(self, tpl, sbus_signature): + """ + Set input arguments in template. + """ + if Invoker.IsCustomInputHandler(sbus_signature): + if tpl.hasLoop("in-raw"): + tpl.add("in-raw", {'type': "DBusMessageIter *"}) + return + + for idx, arg in enumerate(sbus_signature.arguments.values()): + tpl.add("in", {'type': DataType.Find(arg.signature).inputCType, + 'name': arg.name, + 'index': idx}) + + def setOutputArguments(self, tpl, sbus_signature): + """ + Set output arguments in template. + """ + if Invoker.IsCustomOutputHandler(sbus_signature): + if tpl.hasLoop("out-raw"): + tpl.add("out-raw", {'type': "DBusMessageIter *"}) + return + + if tpl.hasLoop("out-static"): + for idx, arg in enumerate(sbus_signature.arguments.values()): + type = DataType.Find(arg.signature) + if type.RequireTalloc: + continue + + tpl.add("out-static", {'type': type.outputCType, + 'name': arg.name, + 'index': idx}) + + if tpl.hasLoop("out-talloc"): + for idx, arg in enumerate(sbus_signature.arguments.values()): + type = DataType.Find(arg.signature) + if not type.RequireTalloc: + continue + + tpl.add("out-talloc", {'type': type.outputCType, + 'name': arg.name, + 'index': idx}) + + if tpl.hasLoop("out"): + for idx, arg in enumerate(sbus_signature.arguments.values()): + tpl.add("out", { + 'type': DataType.Find(arg.signature).outputCType, + 'name': arg.name, + 'index': idx + }) + + def getInterfaceName(self, iface): + """ + If a codegen.Name annotation is specified, this name is used + returned of a full D-Bus name. + """ + annotation = "codegen.Name" + iface_name = SBus.Annotation.Find(iface.annotations, annotation, + iface.name) + + return self.tokenizeValue(iface_name) + + def getMemberName(self, iface, member): + """ + If a codegen.Name annotation is specified, this name is used + returned of a full D-Bus name. + """ + annotation = "codegen.Name" + member_name = SBus.Annotation.Find(member.annotations, annotation, + member.name) + + return self.tokenizeValue(self.getInterfaceName(iface), + member_name) + + def hasEmpty(self, invoker_signature): + """ + Return true if the invoker signature has empty argument list. + """ + return invoker_signature.invokerSignature == "" + + def hasRaw(self, invoker_signature): + """ + Return true if the invoker signature has a custom handler. + """ + return invoker_signature.invokerSignature == "raw" + + def hasAny(self, invoker_signature): + """ + Return true if the invoker signature has any arguments. + """ + return bool(invoker_signature.arguments) or \ + self.hasRaw(invoker_signature) + + def hasParsable(self, invoker_signature): + """ + Return true if the invoker signature has any parsable + arguments. + """ + return invoker_signature.invokerSignature not in ["", "raw"] + + def outputNeedTalloc(self, invoker_signature): + if self.hasRaw(invoker_signature): + return True + + for idx, arg in enumerate(invoker_signature.arguments.values()): + type = DataType.Find(arg.signature) + if type.RequireTalloc: + return True + + return False + + class Interfaces(Base): + """ + Generator for: + + - interfaces.h + """ + + def __init__(self, header, interfaces): + super(Generator.Interfaces, self).__init__() + + self.header = header + self.interfaces = interfaces + + def toggleKeygen(self, tpl, keys, member, sbus_signature): + """ + Insert a keygen information if this member supports keying. + """ + + if not tpl.hasToggle("keygen"): + return + + args = InvokerKeygen.GatherKeyArguments(member, sbus_signature) + + if args is None: + tpl.show("keygen", False) + return + + tpl.show('keygen', True) + + for idx, arg in args.items(): + type = DataType.Find(arg.signature) + + if type.keyFormat is None: + raise ValueError( + ('Data type "%s" does not ' + 'support key generator') % type.sbus_type + ) + + tpl.add('key-argument', { + 'key-index': idx, + 'key-format': type.keyFormat + }) + + keys['key-signature'] = sbus_signature.signature + + def toggleAnnotations(self, tpl, annotations): + """ + Make annotations section visible if there are any to show. + """ + + # We do not include codegen annotations. + filtered = Generator.FilterAnnotations(annotations) + + show = True + if filtered is None or not filtered: + show = False + + tpl.show("annotations", show) + + def setMember(self, template_name, interface, member): + tpl = self.header.get(template_name) + + invoker = Invoker(member.input, member.output) + + keys = { + 'interface': interface.name, + 'name': member.name, + 'token': self.tokenizeName(interface, member), + 'input-signature': invoker.input.invokerSignature, + 'output-signature': invoker.output.invokerSignature + } + + if hasattr(member, 'type'): + keys['type'] = DataType.SBusToDBusType(member.type) + + self.toggleKeygen(tpl, keys, member, member.input) + self.toggleAnnotations(tpl, member.annotations) + self.setInputArguments(tpl, member.input) + self.setOutputArguments(tpl, member.output) + + tpl.set(keys) + + def setInterface(self, template_name, interface): + tpl = self.header.get(template_name) + + self.toggleAnnotations(tpl, interface.annotations) + + keys = { + 'name': interface.name, + 'token': self.tokenizeName(interface), + } + + tpl.set(keys) + + def generate(self): + for interface in self.interfaces.values(): + self.setInterface('interface', interface) + + for method in interface.methods.values(): + self.setMember('method', interface, method) + + for signal in interface.signals.values(): + self.setMember('signal', interface, signal) + + for property in interface.properties.values(): + self.setMember('property', interface, property) + + class Symbols(Base): + """ + Generator for: + + - symbols.c + - symbols.h + """ + + def __init__(self, source, header, interfaces): + super(Generator.Symbols, self).__init__() + + self.source = source + self.header = header + self.interfaces = interfaces + + def annotation(self, annotation): + value = "NULL" + if annotation.value is not None: + value = '"%s"' % annotation.value + + return { + 'annotation-name': annotation.name, + 'annotation-value': value + } + + def argument(self, argument): + return { + 'arg-type': DataType.SBusToDBusType(argument.signature), + 'arg-name': argument.name + } + + def generateAnnotations(self, token, annotations): + # We do not include codegen annotations. + filtered = Generator.FilterAnnotations(annotations) + + if filtered is None or not filtered: + return + + tpl = self.source.get('annotations') + + for annotation in filtered.values(): + tpl.add('annotation', self.annotation(annotation)) + + keys = {'token': token} + tpl.set(keys) + self.header.get('annotations').set(keys) + + def generateMember(self, interface, member, type): + token = self.tokenizeName(interface, member) + keys = {'token': token} + + tpl = self.source.get(type) + for arg in member.input.arguments.values(): + tpl.add('input', self.argument(arg)) + + for arg in member.output.arguments.values(): + tpl.add('output', self.argument(arg)) + + tpl.set(keys) + self.header.get(type).set(keys) + self.generateAnnotations(token, member.annotations) + + def generate(self): + for interface in self.interfaces.values(): + self.generateAnnotations(self.tokenizeName(interface), + interface.annotations) + + for method in interface.methods.values(): + self.generateMember(interface, method, 'method') + + for signal in interface.signals.values(): + self.generateMember(interface, signal, 'signal') + + class Arguments(Base): + """ + Generator for: + + - arguments.c + - arguments.h + """ + + def __init__(self, source, header, invoker_arguments): + super(Generator.Arguments, self).__init__() + + self.source = source + self.header = header + self.invoker_arguments = invoker_arguments + + def generate(self): + self.generateSource() + self.generateHeader() + + def generateSource(self): + tpl = self.source.get("arguments") + for signature, args in self.invoker_arguments.items(): + for idx, arg in enumerate(args.values()): + type = DataType.Find(arg.signature) + memctx = "mem_ctx, " if type.RequireTalloc else "" + keys = {"arg-signature": arg.signature, + "talloc-context": memctx, + "index": idx} + tpl.add('read-argument', keys) + tpl.add('write-argument', keys) + + keys = {"signature": signature} + tpl.set(keys) + + def generateHeader(self): + tpl = self.header.get("arguments") + for signature, args in self.invoker_arguments.items(): + for idx, arg in enumerate(args.values()): + keys = {"type": DataType.Find(arg.signature).inputCType, + "index": idx} + tpl.add('args', keys) + + keys = {"signature": signature} + tpl.set(keys) + + class Invokers(Base): + """ + Generator for: + + - invokers.c + - invokers.h + """ + + def __init__(self, source, header, invokers): + super(Generator.Invokers, self).__init__() + + self.source = source + self.header = header + self.invokers = invokers + + def generate(self): + self.generateSource() + self.generateHeader() + + def generateSource(self): + tpl = self.source.get("invoker") + for invoker in self.invokers.values(): + tpl.show("if-input-arguments", + self.hasParsable(invoker.input)) + tpl.show("if-output-arguments", + self.hasParsable(invoker.output)) + + self.setInputArguments(tpl, invoker.input) + self.setOutputArguments(tpl, invoker.output) + + keys = {"input-signature": invoker.input.invokerSignature, + "output-signature": invoker.output.invokerSignature} + + tpl.set(keys) + + def generateHeader(self): + tpl = self.header.get("invoker") + for invoker in self.invokers.values(): + keys = {"input-signature": invoker.input.invokerSignature, + "output-signature": invoker.output.invokerSignature} + tpl.set(keys) + + class Keygens(Base): + """ + Generator for: + + - keygens.c + - keygens.h + """ + + def __init__(self, source, header, keygens): + super(Generator.Keygens, self).__init__() + + self.source = source + self.header = header + self.keygens = keygens + + def generate(self): + for pair in self.keygens.values(): + name = 'key' if pair.arguments else 'key-no-arguments' + + self.set(self.source.get(name), pair.signature, pair.arguments) + self.set(self.header.get(name), pair.signature, pair.arguments) + + def set(self, tpl, signature, args): + if args is None: + return + + for idx, arg in args.items(): + type = DataType.Find(arg.signature) + + if type.keyFormat is None: + raise ValueError( + ('Data type "%s" does not ' + 'support key generator') % type.sbus_type + ) + + tpl.add('key-argument', { + 'key-index': idx, + 'key-format': type.keyFormat + }) + + tpl.set({'key-signature': signature}) + + class Properties(Base): + """ + Generator for: + + - client_properties.h + """ + + def __init__(self, header, interfaces): + super(Generator.Properties, self).__init__() + + self.header = header + self.interfaces = interfaces + + def generate(self): + tpl = self.header.get("properties") + for iface in self.interfaces.values(): + if not iface.properties: + continue + + added = False + + for property in iface.properties.values(): + if not property.isReadable(): + continue + + if not InvokerCaller.IsWanted(iface, property, "either"): + continue + + added = True + + type = DataType.Find(property.type) + tpl.add("property", {'name': property.name, + 'input-type': type.inputCType}) + + # Do not generate GetAll caller if we are not interested in + # properties callers. + if not added: + tpl.clear() + continue + + keys = {"token": self.getInterfaceName(iface)} + + tpl.set(keys) + + class MethodCalls(Base): + """ + Generator for sync and async method callers. + """ + + def __init__(self, source, header, interfaces, type, invokers): + super(Generator.MethodCalls, self).__init__() + + self.source = source + self.header = header + self.interfaces = interfaces + self.type = type + self.invokers = invokers + + def generate(self): + self.generateInvokers() + self.generateCallers(self.source.get("method-caller")) + self.generateCallers(self.header.get("method-caller")) + + def generateInvokers(self): + tpl = self.source.get("method-invoker") + for invoker in self.invokers.values(): + tpl.show("if-input-arguments", + self.hasParsable(invoker.input)) + tpl.show("if-output-arguments", + self.hasParsable(invoker.output)) + tpl.show("if-raw-input", + self.hasRaw(invoker.input)) + tpl.show("if-raw-output", + self.hasRaw(invoker.output)) + tpl.show("if-empty-input", + self.hasEmpty(invoker.input)) + tpl.show("if-has-output", + self.hasAny(invoker.output)) + tpl.show("if-use-talloc", + self.outputNeedTalloc(invoker.output)) + tpl.show("if-show-dummy", + self.showDummy(invoker)) + + self.setInputArguments(tpl, invoker.input) + self.setOutputArguments(tpl, invoker.output) + + keys = {"input-signature": invoker.input.invokerSignature, + "output-signature": invoker.output.invokerSignature} + + tpl.set(keys) + + def generateCallers(self, tpl): + for iface in self.interfaces.values(): + for method in iface.methods.values(): + if not InvokerCaller.IsWanted(iface, method, self.type): + continue + + invoker = Invoker(method.input, method.output) + tpl.show("if-raw-input", + self.hasRaw(invoker.input)) + tpl.show("if-raw-output", + self.hasRaw(invoker.output)) + tpl.show("if-has-output", + self.hasAny(invoker.output)) + tpl.show("if-use-talloc", + self.outputNeedTalloc(invoker.output)) + + self.setInputArguments(tpl, invoker.input) + self.setOutputArguments(tpl, invoker.output) + + keygen = InvokerKeygen.BuildKeygenName(method, + method.input) + keys = { + "token": self.getMemberName(iface, method), + "iface": iface.name, + "method": method.name, + "keygen": keygen, + "input-signature": invoker.input.invokerSignature, + "output-signature": invoker.output.invokerSignature + } + + tpl.set(keys) + + def showDummy(self, invoker): + return self.hasEmpty(invoker.output) and not \ + self.hasParsable(invoker.input) + + class SignalCalls(Base): + """ + Generator for sync and async signal callers. + """ + + def __init__(self, source, header, interfaces, type, invokers): + super(Generator.SignalCalls, self).__init__() + + self.source = source + self.header = header + self.interfaces = interfaces + self.type = type + self.invokers = invokers + + def generate(self): + self.generateInvokers() + self.generateCallers(self.source.get("signal-caller")) + self.generateCallers(self.header.get("signal-caller")) + + def generateInvokers(self): + tpl = self.source.get("signal-invoker") + for invoker in self.invokers.values(): + tpl.show("if-input-arguments", self.hasParsable(invoker.input)) + tpl.show("if-empty-input", self.hasEmpty(invoker.input)) + tpl.show("if-raw-input", self.hasRaw(invoker.input)) + + self.setInputArguments(tpl, invoker.input) + + keys = {"input-signature": invoker.input.invokerSignature} + + tpl.set(keys) + + def generateCallers(self, tpl): + for iface in self.interfaces.values(): + for signal in iface.signals.values(): + if not InvokerCaller.IsWanted(iface, signal, self.type): + continue + + invoker = Invoker(signal.input, signal.output) + tpl.show("if-raw-input", self.hasRaw(invoker.input)) + + self.setInputArguments(tpl, invoker.input) + + keys = {"token": self.getMemberName(iface, signal), + "iface": iface.name, + "signal": signal.name, + "input-signature": invoker.input.invokerSignature} + + tpl.set(keys) + + class PropertyCalls(Base): + """ + Generator for sync and async property callers. + """ + + def __init__(self, source, header, interfaces, type, + get_invokers, set_invokers): + super(Generator.PropertyCalls, self).__init__() + + self.source = source + self.header = header + self.interfaces = interfaces + self.type = type + self.get_invokers = get_invokers + self.set_invokers = set_invokers + + def generate(self): + self.generateInvokers(self.source.get("get-invoker"), + self.get_invokers) + self.generateInvokers(self.source.get("set-invoker"), + self.set_invokers) + self.generateCallers(self.source.get("property-caller")) + self.generateCallers(self.header.get("property-caller")) + self.generateGetAll(self.source.get("getall-caller")) + self.generateGetAll(self.header.get("getall-caller")) + + def generateInvokers(self, tpl, invokers): + for invoker in invokers.values(): + type = None + if self.hasAny(invoker.input): + type = DataType.Find( + invoker.input.arguments['value'].signature + ) + elif self.hasAny(invoker.output): + type = DataType.Find( + invoker.output.arguments['value'].signature + ) + else: + raise ValueError( + 'Invoker has no input nor output argument\n' + ) + + tpl.show("if-use-talloc", type.RequireTalloc) + + keys = {"input-signature": invoker.input.invokerSignature, + "output-signature": invoker.output.invokerSignature, + "input-type": type.inputCType, + "output-type": type.outputCType, + "dbus-type": type.dbus_type} + + tpl.set(keys) + + def generateCallers(self, tpl): + for iface in self.interfaces.values(): + for property in iface.properties.values(): + if not InvokerCaller.IsWanted(iface, property, self.type): + continue + + if property.isReadable(): + tpl.show("get", True) + + if property.isWritable(): + tpl.show("set", True) + + invoker = Invoker(property.input, property.output) + type = DataType.Find(property.type) + + tpl.show("get-static", not type.RequireTalloc) + tpl.show("get-talloc", type.RequireTalloc) + + keys = { + "token": self.getMemberName(iface, property), + "iface": iface.name, + "property": property.name, + "input-signature": invoker.input.invokerSignature, + "output-signature": invoker.output.invokerSignature, + "input-type": type.inputCType, + "output-type": type.outputCType + } + + tpl.set(keys) + + def generateGetAll(self, tpl): + for iface in self.interfaces.values(): + if not iface.properties: + continue + + added = False + + for property in iface.properties.values(): + if not property.isReadable(): + continue + + if not InvokerCaller.IsWanted(iface, property, self.type): + continue + + added = True + + type = DataType.Find(property.type) + invoker = Invoker(property.input, property.output) + + loop_name = 'property-static' + if type.RequireTalloc: + loop_name = 'property-talloc' + + tpl.add(loop_name, { + 'name': property.name, + 'input-type': type.inputCType, + 'output-type': type.outputCType, + 'output-signature': invoker.output.invokerSignature + }) + + # Do not generate GetAll caller if we are not interested in + # properties callers. + if not added: + tpl.clear() + continue + + keys = {"token": self.getInterfaceName(iface), + "iface": iface.name} + + tpl.set(keys) diff --git a/src/sbus/codegen/sbus_Introspection.py b/src/sbus/codegen/sbus_Introspection.py new file mode 100644 index 0000000..17a255a --- /dev/null +++ b/src/sbus/codegen/sbus_Introspection.py @@ -0,0 +1,287 @@ +# +# Authors: +# Pavel Brezina <pbrezina@redhat.com> +# +# Copyright (C) 2017 Red Hat +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +from collections import OrderedDict +import xml.etree.ElementTree as etree + + +class Introspectable: + class Element(object): + """ This is a basic introspectable object. This class will make + sure that the given xml element is of correct type and provide + some helper functions to simplify work of the children. + + Children objects must implement TagName attribute, which contains + the name of the expected xml tag. + + All introspectable objects contain the following properties: + - name : str -- name of the object + - annotations : OrderedDict -- available annotations + """ + def __init__(self, element): + self.check(element, self.TagName) + + self.element = element + self.name = element.attrib["name"] + self.annotations = self.find(SBus.Annotation) + + def find(self, object_class): + return Introspectable.FindElements(self.element, object_class) + + def check(self, element, tagname): + if element.tag != tagname: + raise ValueError('Unexpected tag name "%s" (%s expected)!' + % (element.tag, tagname)) + if "name" not in element.attrib: + raise ValueError('Missing attribute name!') + + def getAttr(self, name, default_value): + return self.element.attrib.get(name, default_value) + + def getExistingAttr(self, name): + if name not in self.element.attrib: + raise ValueError('Element %s name="%s" is missing attribute %s' + % (self.TagName, self.name, name)) + + return self.element.attrib[name] + + class Invokable(Element): + """ This is a base class for invokable objects -- methods and signals. + Invokable objects has available additional attributes: + + - input OrderedDict -- input signature and arguments + - output : OrderedDict -- output signature and arguments + """ + def __init__(self, element): + super(Introspectable.Invokable, self).__init__(element) + + self.key = self.getAttr("key", None) + + self.arguments = self.find(SBus.Argument) + input = self.getInputArguments() + output = self.getOutputArguments() + + self.input = SBus.Signature(input, self.annotations) + self.output = SBus.Signature(output, self.annotations) + return + + def getInputArguments(self): + return self.getArguments("in") + + def getOutputArguments(self): + return self.getArguments("out") + + def getArguments(self, type): + args = OrderedDict() + + for name, arg in self.arguments.items(): + if type == "in" and arg.isInput(): + args[name] = arg + continue + if type == "out" and arg.isOutput(): + args[name] = arg + continue + + return args + + @staticmethod + def Introspect(path): + root = etree.parse(path).getroot() + return Introspectable.FindElements(root, SBus.Interface) + + @staticmethod + def FindElements(parent, object_class): + dict = OrderedDict() + for child in parent: + if child.tag != object_class.TagName: + continue + object = object_class(child) + + if object.name in dict: + raise ValueError('%s name="%s" is already present ' + 'in the same parent element\n' + % (object_class.TagName, object.name)) + + dict[object.name] = object + + """ + Arguments can't be sorted and annotations order should be left on + the author of introspection. Otherwise we want to sort the dictionary + alphabetically based on keys. + """ + if object_class in [SBus.Argument, SBus.Annotation]: + return dict + + return OrderedDict(sorted(dict.items())) + + +class SBus: + class Interface(Introspectable.Element): + TagName = "interface" + + def __init__(self, element): + super(SBus.Interface, self).__init__(element) + + self.methods = self.find(SBus.Method) + self.signals = self.find(SBus.Signal) + self.properties = self.find(SBus.Property) + return + + class Method(Introspectable.Invokable): + TagName = "method" + + def __init__(self, element): + super(SBus.Method, self).__init__(element) + + class Signal(Introspectable.Invokable): + TagName = "signal" + + def __init__(self, element): + super(SBus.Signal, self).__init__(element) + + class Property(Introspectable.Invokable): + TagName = "property" + + def __init__(self, element): + self.name = element.attrib["name"] + self.element = element + self.access = self.getExistingAttr("access") + self.type = self.getExistingAttr("type") + + super(SBus.Property, self).__init__(element) + + if self.key is not None: + raise ValueError('Keying is not supported on properties: %s ' + % self.name) + + def getInputArguments(self): + if not self.isWritable(): + return {} + + return {"value": SBus.Argument.Create("value", self.type, "in")} + + def getOutputArguments(self): + if not self.isReadable(): + return {} + + return {"value": SBus.Argument.Create("value", self.type, "out")} + + def isReadable(self): + return self.access == "read" or self.access == "readwrite" + + def isWritable(self): + return self.access == "write" or self.access == "readwrite" + + class Annotation(Introspectable.Element): + TagName = "annotation" + + def __init__(self, element): + super(SBus.Annotation, self).__init__(element) + + self.value = self.getAttr("value", None) + return + + @staticmethod + def Find(annotations, name, default_value): + if name in annotations: + annotation = annotations[name] + if annotation.value is None: + return default_value + return annotation.value + return default_value + + @staticmethod + def FindBool(annotations, name, Assume=False): + assume = "true" if Assume else "false" + value = SBus.Annotation.Find(annotations, name, assume) + if value.lower() == "true": + return True + else: + return False + + @staticmethod + def CheckIfTrue(names, annotations): + for name in names: + if SBus.Annotation.FindBool(annotations, name, False): + return True + + return False + + @staticmethod + def CheckIfFalse(names, annotations): + for name in names: + if not SBus.Annotation.FindBool(annotations, name, True): + return False + + return True + + @staticmethod + def AtleastOneIsSet(names, annotations): + for name in names: + value = SBus.Annotation.Find(annotations, name, None) + if value is not None: + return True + + return False + + class Argument(Introspectable.Element): + TagName = "arg" + + def __init__(self, element, Name=None, Type=None, Direction=None, + Key=None): + if element is None: + self.element = None + self.name = Name + self.signature = Type + self.direction = Direction + self.key = Key + return + + super(SBus.Argument, self).__init__(element) + + self.signature = self.getExistingAttr("type") + self.direction = self.getAttr("direction", "in") + self.key = self.getAttr("key", None) + + def isInput(self): + return self.direction == "in" + + def isOutput(self): + return not self.isInput() + + @staticmethod + def Create(name, type, direction): + return SBus.Argument(element=None, + Name=name, + Type=type, + Direction=direction) + + class Signature: + def __init__(self, args, annotations): + self.annotations = annotations + self.signature = self.getSignature(args) + self.arguments = args + + def getSignature(self, args): + signature = "" + for arg in args.values(): + signature += arg.signature + + return signature diff --git a/src/sbus/codegen/sbus_Invoker.py b/src/sbus/codegen/sbus_Invoker.py new file mode 100644 index 0000000..abbdc17 --- /dev/null +++ b/src/sbus/codegen/sbus_Invoker.py @@ -0,0 +1,399 @@ +# +# Authors: +# Pavel Brezina <pbrezina@redhat.com> +# +# Copyright (C) 2017 Red Hat +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +from collections import OrderedDict +from sbus_Introspection import SBus + + +class Invoker: + """ Invoker is a piece of C code that takes care of executing a method, + signal and property handlers and returning their output values. + + SBus code generator tries to reduce amount of generated code by + reusing invokers whenever possible. Therefore we must ensure that + invoker for each input and output signature type is generated only + once. + + Each invoker is associated with its input and output SBus signatures + extended by a reserved keyword "raw" that says that the invoker input + or output parameters are handled by its handler and caller respectively + and are passed through as D-Bus iterators. + """ + def __init__(self, sbus_input, sbus_output): + self.input = self.getSignature(sbus_input, + self.IsCustomInputHandler(sbus_input)) + + self.output = self.getSignature( + sbus_output, + self.IsCustomOutputHandler(sbus_output) + ) + + def getSignature(self, sbus_signature, is_custom_handler): + if sbus_signature is None: + return InvokerSignature("", {}, {}) + + invoker_signature = sbus_signature.signature + if is_custom_handler: + invoker_signature = "raw" + + return InvokerSignature(invoker_signature, + sbus_signature.arguments, + sbus_signature.annotations) + + @staticmethod + def GatherInvokers(interfaces): + """ + Gather all required invokers for given interfaces. + """ + dict = {} + for iface in interfaces.values(): + for method in iface.methods.values(): + Invoker.Add(dict, method.input, method.output) + + for signal in iface.signals.values(): + Invoker.Add(dict, signal.input, signal.output) + + for property in iface.properties.values(): + if property.isReadable(): + Invoker.Add(dict, None, property.output) + if property.isWritable(): + Invoker.Add(dict, property.input, None) + + return OrderedDict(sorted(dict.items())) + + @staticmethod + def Add(dict, input, output): + """ + Add a new invoker to dictionary if possible. + """ + invoker = Invoker(input, output) + key = "in:%s, out:%s" % (invoker.input.invokerSignature, + invoker.output.invokerSignature) + if key in dict: + return + + dict[key] = invoker + + @staticmethod + def IsCustomHandler(type, sbus_signature): + if type == "input": + return Invoker.IsCustomInputHandler(sbus_signature) + elif type == "output": + return Invoker.IsCustomOutputHandler(sbus_signature) + else: + raise ValueError("Invalid type: %s" % type) + + @staticmethod + def IsCustomInputHandler(sbus_signature): + names = ["codegen.CustomHandler", + "codegen.CustomInputHandler"] + + if sbus_signature is None: + return False + + return SBus.Annotation.CheckIfTrue(names, sbus_signature.annotations) + + @staticmethod + def IsCustomOutputHandler(sbus_signature): + names = ["codegen.CustomHandler", + "codegen.CustomOutputHandler"] + + if sbus_signature is None: + return False + + return SBus.Annotation.CheckIfTrue(names, sbus_signature.annotations) + + +class InvokerSignature: + """ Contains information about Invoker signature and SBus arguments + and annotations. Do not confuse with SBus.Signature. + """ + def __init__(self, invoker_signature, sbus_arguments, sbus_annotations): + self.invokerSignature = invoker_signature + self.arguments = sbus_arguments + self.annotations = sbus_annotations + + +class InvokerArgumentType: + """ Argument reader/writer is a piece of C code that takes care of + parsing D-Bus methods into C types. + + SBus code generator tries to reduce amount of generated code by + reusing reades and writers whenever possible. Therefore we must ensure + that only one reader and writer is generated for each input and output + signature. + """ + @staticmethod + def GatherArgumentTypes(interfaces): + """ + Gather all invoker argument types for given interfaces. + """ + dict = {} + for iface in interfaces.values(): + InvokerArgumentType.AddObjects(dict, iface.methods) + InvokerArgumentType.AddObjects(dict, iface.signals) + InvokerArgumentType.AddObjects(dict, iface.properties) + + return OrderedDict(sorted(dict.items())) + + @staticmethod + def AddObjects(dict, objects): + for object in objects.values(): + InvokerArgumentType.AddType(dict, "input", object.input) + InvokerArgumentType.AddType(dict, "output", object.output) + + @staticmethod + def AddType(dict, type, sbus_signature): + """ + Add a new argument type to dictionary if possible. + """ + # We don't generate readers and writers for empty arguments + if sbus_signature is None or not sbus_signature.arguments: + return + + # We don't generate readers and writers for custom handlers + if Invoker.IsCustomHandler(type, sbus_signature): + return + + # We generate each reader and writer only once + if sbus_signature.signature in dict: + return + + dict[sbus_signature.signature] = sbus_signature.arguments + + +class InvokerKeygen: + """ Invoker Keygen is a piece of C code that takes care of + chaining same request into one. + + SBus code generator tries to reduce amount of generated code by + reusing keygens whenever possible. Therefore we must ensure + that only one keygen is generated for each signature. + """ + @staticmethod + def BuildKey(sbus_member, sbus_signature, Args=None): + """ + Return dictionary key for given SBUS member and signature or None + if no keying is supported for this member. + """ + args = Args if not None else \ + InvokerKeygen.GatherKeyArguments(sbus_member, sbus_signature) + + if args is None: + return None + + key = sbus_signature.signature if args else "<no-arguments>" + + for idx, arg in args.items(): + key += ',%d' % idx + + return key + + @staticmethod + def BuildKeygenName(sbus_member, sbus_signature): + args = InvokerKeygen.GatherKeyArguments(sbus_member, sbus_signature) + + if args is None: + return "NULL" + + keygen = "_sbus_key_%s" % sbus_signature.signature + + for idx, arg in args.items(): + keygen += '_%d' % idx + + return keygen + + @staticmethod + def GatherKeyArguments(sbus_member, sbus_signature): + """ + Gather list of key arguments for an SBus member with given + signature. + + Return dictionary of <argument-index, argument> sorted by + its key index or an empty dictionary if no arguments are + necessary to construct a key. + + Return None for SBus members that do not allow keying. + """ + keys = {} + + if sbus_signature is not None: + for idx, arg in enumerate(sbus_signature.arguments.values()): + if arg.key is not None: + keys[idx] = arg + + if not keys and sbus_member.key is None: + return None + + return OrderedDict(sorted(keys.items(), + key=lambda p: p[1].key)) + + @staticmethod + def GatherKeygens(interfaces): + """ + Gather all keygens needed to implement given interfaces. + """ + dict = {} + for iface in interfaces.values(): + for method in iface.methods.values(): + InvokerKeygen.Add(dict, method, method.input) + + for signal in iface.signals.values(): + InvokerKeygen.Add(dict, signal, signal.input) + + return OrderedDict(sorted(dict.items())) + + @staticmethod + def Add(dict, sbus_member, sbus_signature): + """ + Add a new keygen to dictionary if possible. + """ + args = InvokerKeygen.GatherKeyArguments(sbus_member, sbus_signature) + + if args is None: + return + + key = InvokerKeygen.BuildKey(sbus_member, sbus_signature, Args=args) + dict[key] = InvokerKeygen.KeygenPair(sbus_signature, args) + + class KeygenPair: + def __init__(self, sbus_signature, arguments): + self.signature = sbus_signature.signature + self.arguments = arguments + + +class InvokerCaller: + """ Caller invoker is a piece of C code that takes care of executing + an outgoing method, signal or property and returning their output. + + SBus code generator tries to reduce amount of generated code by + reusing invokers whenever possible. Therefore we must ensure that + invoker for each input and output signature type is generated only + once. + """ + @staticmethod + def GatherMethodInvokers(interfaces, type): + """ + Gather all required method invokers for given interfaces. + """ + dict = {} + for iface in interfaces.values(): + for method in iface.methods.values(): + if not InvokerCaller.IsWanted(iface, method, type): + continue + + InvokerCaller.Add(dict, method.input, method.output) + + return OrderedDict(sorted(dict.items())) + + @staticmethod + def GatherSignalInvokers(interfaces, type): + """ + Gather all required signal invokers for given interfaces. + """ + dict = {} + for iface in interfaces.values(): + for signal in iface.signals.values(): + if not InvokerCaller.IsWanted(iface, signal, type): + continue + + InvokerCaller.Add(dict, signal.input, signal.output) + + return OrderedDict(sorted(dict.items())) + + @staticmethod + def GatherGetInvokers(interfaces, type): + """ + Gather all required property getters for given interfaces. + """ + dict = {} + for iface in interfaces.values(): + for property in iface.properties.values(): + if not InvokerCaller.IsWanted(iface, property, type): + continue + + if not property.isReadable(): + continue + + InvokerCaller.Add(dict, None, property.output) + + return OrderedDict(sorted(dict.items())) + + @staticmethod + def GatherSetInvokers(interfaces, type): + """ + Gather all required property setters for given interfaces. + """ + dict = {} + for iface in interfaces.values(): + for property in iface.properties.values(): + if not InvokerCaller.IsWanted(iface, property, type): + continue + + if not property.isWritable(): + continue + + InvokerCaller.Add(dict, property.input, None) + + return OrderedDict(sorted(dict.items())) + + @staticmethod + def Add(dict, input, output): + """ + Add a new invoker to dictionary if possible. + """ + invoker = Invoker(input, output) + key = "in:%s, out:%s" % (invoker.input.invokerSignature, + invoker.output.invokerSignature) + if key in dict: + return + + dict[key] = invoker + + @staticmethod + def IsWantedSync(interface, member): + names = ["codegen.Caller", "codegen.SyncCaller"] + # First see if the member has one of these annotations + if SBus.Annotation.AtleastOneIsSet(names, member.annotations): + return SBus.Annotation.CheckIfFalse(names, member.annotations) + + return SBus.Annotation.CheckIfFalse(names, interface.annotations) + + @staticmethod + def IsWantedAsync(interface, member): + names = ["codegen.Caller", "codegen.AsyncCaller"] + + # First see if the member has one of these annotations + if SBus.Annotation.AtleastOneIsSet(names, member.annotations): + return SBus.Annotation.CheckIfFalse(names, member.annotations) + + return SBus.Annotation.CheckIfFalse(names, interface.annotations) + + @staticmethod + def IsWanted(interface, member, type): + if type == "sync": + return InvokerCaller.IsWantedSync(interface, member) + elif type == "async": + return InvokerCaller.IsWantedAsync(interface, member) + + wanted_sync = InvokerCaller.IsWantedSync(interface, member) + wanted_async = InvokerCaller.IsWantedAsync(interface, member) + + return wanted_sync or wanted_async diff --git a/src/sbus/codegen/sbus_Template.py b/src/sbus/codegen/sbus_Template.py new file mode 100644 index 0000000..51016c3 --- /dev/null +++ b/src/sbus/codegen/sbus_Template.py @@ -0,0 +1,322 @@ +# +# Authors: +# Pavel Brezina <pbrezina@redhat.com> +# +# Copyright (C) 2017 Red Hat +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import os +import re +import errno +import textwrap +import os.path + + +class Template: + def __init__(self, name, templateFile, template): + template = self.removeLines(template) + + self.templateFile = templateFile + self.name = name + self.loops = {} + self.toggles = {} + self.template = self.parse(template) + self.output = "" + + def parse(self, template): + template = self.parseLoops(template) + template = self.parseToggles(template) + return template + + def parseLoops(self, template): + template = self.Pattern.Loop.sub(self.processLoops, template) + return self.Pattern.LoopLine.sub(self.processLoops, template) + + def processLoops(self, match): + name = match.group(1) + template = self.removeLines(match.group(2)) + index = 0 + + if name not in self.loops: + self.loops[name] = self.Loop() + + index = self.loops[name].addTemplate(template) + return '$${loop:%s:%d}' % (name, index) + + def parseToggles(self, template): + template = self.Pattern.Toggle.sub(self.processToggles, template) + return self.Pattern.ToggleLine.sub(self.processToggles, template) + + def processToggles(self, match): + name = match.group(1) + if_visible = self.removeLines(match.group(2)) + if_hidden = self.removeLines(match.group(4)) + index = 0 + + if name not in self.toggles: + self.toggles[name] = self.Toggle() + + index = self.toggles[name].addTemplate(if_visible, if_hidden) + return '$${toggle:%s:%d}' % (name, index) + + def add(self, loop_name, values): + """Add new item into <loop name="$loop_name"> template. + Setting its attributes to $values. + """ + if loop_name not in self.loops: + return self + self.loops[loop_name].set(values) + return self + + def show(self, toggle_name, isVisible): + """Make <toggle name="$toggle_name"> either visible or hidden + within the template. + """ + if not self.hasToggle(toggle_name): + return + + self.toggles[toggle_name].show(isVisible) + + def hasToggle(self, name): + return name in self.toggles + + def hasLoop(self, name): + return name in self.loops + + def set(self, values): + """Set template attributes to $values, push generated content into + the output file and reset this template. + """ + template = self.template + for key, toggle in self.toggles.items(): + for idx, toggletpl in enumerate(toggle.templates): + pattern = "$${toggle:%s:%d}" % (key, idx) + template = template.replace(pattern, toggletpl.generate()) + + self.output = self.Set(template, values) + self.templateFile.push(self.generate()) + self.clear() + + def pushOriginal(self): + """Push original template into the output file + """ + self.templateFile.push(self.template) + + def clear(self): + for loop in self.loops.values(): + loop.clear() + + for toggle in self.toggles.values(): + toggle.show(False) + + self.output = "" + + def generate(self): + output = self.output + for key, loop in self.loops.items(): + for idx, content in enumerate(loop.templates): + pattern = "$${loop:%s:%d}" % (key, idx) + output = output.replace(pattern, loop.get(idx), 1) + return output + + @staticmethod + def Set(content, values): + output = content + for key, value in values.items(): + output = output.replace("${" + key + "}", str(value)) + return output + + def removeLines(self, content): + """Remove unneeded lines and spaces. There are some additional lines + and spaces that may end up in the template after parsing. This + method will remove new line after <@template-tag> and spaces + from otherwise empty lines. + """ + if content is None: + return content + + content = self.Pattern.NewLine.sub('', content, 1) + content = self.Pattern.EmptyLine.sub('', content) + return content + + class Pattern: + Template = re.compile( + r' *<template name="(\S+)">(.*?)</template>\r?\n?', + re.MULTILINE | re.DOTALL + ) + + Loop = re.compile( + r' *<loop name="(\S+)">(.*?)</loop>\r?\n?', + re.MULTILINE | re.DOTALL + ) + + LoopLine = re.compile( + r'<loop line name="(\S+)">(.*?)</loop>', + re.MULTILINE | re.DOTALL + ) + + Toggle = re.compile( + r' *<toggle name="(\S+)">(.*?)(<or>(.*?))?</toggle>\r?\n?', + re.MULTILINE | re.DOTALL + ) + + ToggleLine = re.compile( + r'<toggle line name="(\S+)">(.*?)(<or>(.*?))?</toggle>', + re.MULTILINE | re.DOTALL + ) + + NewLine = re.compile('^\r?\n') + + EmptyLine = re.compile('^ *$', re.MULTILINE) + + class Loop: + def __init__(self): + self.templates = [] + self.num_templates = 0 + + def addTemplate(self, template): + self.templates.append(self.LoopTemplate(template)) + self.num_templates += 1 + return self.num_templates - 1 + + def set(self, values): + for template in self.templates: + template.set(values) + + def clear(self): + for template in self.templates: + template.clear() + + def get(self, index): + return self.templates[index].generate() + + class LoopTemplate: + def __init__(self, template): + self.template = template + self.output = "" + + def set(self, values): + self.output += Template.Set(self.template, values) + + def clear(self): + self.output = "" + + def generate(self): + return self.output + + class Toggle: + def __init__(self): + self.templates = [] + self.num_templates = 0 + self.visible = False + + def addTemplate(self, if_visible, if_hidden): + toggletpl = self.ToggleTemplate(self, if_visible, if_hidden) + self.templates.append(toggletpl) + self.num_templates += 1 + return self.num_templates - 1 + + def show(self, isVisible): + self.visible = isVisible + + class ToggleTemplate: + def __init__(self, toggle, if_visible, if_hidden): + self.toggle = toggle + self.if_visible = if_visible + self.if_hidden = if_hidden + + def generate(self): + if self.toggle.visible: + return self.if_visible + elif self.if_hidden is not None: + return self.if_hidden + + return '' + + +class TemplateFile: + """Parse file contents into templates. + + Obtain template with .get and set its content. When all the content is + set, you can call .generate to obtain generated content or .write + to write it to a file. + """ + + def __init__(self, path): + with open(path, "r") as file: + contents = file.read() + + self.templates = {} + self.output = "" + self.parse(contents) + + def parse(self, template): + for (name, content) in Template.Pattern.Template.findall(template): + content = textwrap.dedent(content) + self.templates[name] = Template(name, self, content) + + def get(self, name): + return self.templates[name] + + def has(self, name): + return name in self.templates + + def push(self, content): + self.output += content + + def generate(self): + return self.output + + def write(self, filename, postprocess=None): + dirname = os.path.dirname(filename) + if not os.path.exists(dirname): + try: + os.makedirs(dirname) + except OSError as exception: + if exception.errno == errno.EEXIST and os.path.isdir(filename): + pass + else: + raise + + output = self.generate().rstrip() + '\n' + if postprocess is not None: + output = postprocess(output) + + if not self.needsOverride(filename, output): + return + + with open(filename, "w") as file: + file.write(output) + + def needsOverride(self, filename, content): + """ + Do not override the file unless it is not yet present or its + current content differs from the generated one. This ensure + that the file is in correct state and yet it is not rebuild + during make unless necessary. + """ + if not os.path.isfile(filename): + return True + + with open(filename, "r") as file: + current_content = file.read() + if current_content != content: + return True + + return False + + def __str__(self): + return self.generate() diff --git a/src/sbus/codegen/templates/arguments.c.tpl b/src/sbus/codegen/templates/arguments.c.tpl new file mode 100644 index 0000000..5d28fa0 --- /dev/null +++ b/src/sbus/codegen/templates/arguments.c.tpl @@ -0,0 +1,67 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #include <errno.h> + #include <stdint.h> + #include <talloc.h> + #include <stdbool.h> + #include <dbus/dbus.h> + + #include "${sbus-path}/interface/sbus_iterator_readers.h" + #include "${sbus-path}/interface/sbus_iterator_writers.h" + #include "${header:arguments}" + +</template> + +<template name="arguments"> + errno_t _sbus_invoker_read_${signature} + (TALLOC_CTX *mem_ctx, + DBusMessageIter *iter, + struct _sbus_invoker_args_${signature} *args) + { + errno_t ret; + + <loop name="read-argument"> + ret = sbus_iterator_read_${arg-signature}(${talloc-context}iter, &args->arg${index}); + if (ret != EOK) { + return ret; + } + + </loop> + return EOK; + } + + errno_t _sbus_invoker_write_${signature} + (DBusMessageIter *iter, + struct _sbus_invoker_args_${signature} *args) + { + errno_t ret; + + <loop name="write-argument"> + ret = sbus_iterator_write_${arg-signature}(iter, args->arg${index}); + if (ret != EOK) { + return ret; + } + + </loop> + return EOK; + } + +</template> diff --git a/src/sbus/codegen/templates/arguments.h.tpl b/src/sbus/codegen/templates/arguments.h.tpl new file mode 100644 index 0000000..f0d2290 --- /dev/null +++ b/src/sbus/codegen/templates/arguments.h.tpl @@ -0,0 +1,58 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #ifndef ${file-guard} + #define ${file-guard} + + #include <errno.h> + #include <stdint.h> + #include <talloc.h> + #include <stdbool.h> + #include <dbus/dbus.h> + + <loop name="custom-type-header"> + #include "${custom-type-header}" + </loop> + +</template> + +<template name="arguments"> + struct _sbus_invoker_args_${signature} { + <loop name="args"> + ${type} arg${index}; + </loop> + }; + + errno_t + _sbus_invoker_read_${signature} + (TALLOC_CTX *mem_ctx, + DBusMessageIter *iter, + struct _sbus_invoker_args_${signature} *args); + + errno_t + _sbus_invoker_write_${signature} + (DBusMessageIter *iter, + struct _sbus_invoker_args_${signature} *args); + +</template> + +<template name="file-footer"> + #endif /* ${file-guard} */ +</template> diff --git a/src/sbus/codegen/templates/client_async.c.tpl b/src/sbus/codegen/templates/client_async.c.tpl new file mode 100644 index 0000000..e16ce42 --- /dev/null +++ b/src/sbus/codegen/templates/client_async.c.tpl @@ -0,0 +1,683 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #include <errno.h> + #include <talloc.h> + #include <tevent.h> + #include <dbus/dbus.h> + + #include "${sbus-path}/sbus_private.h" + #include "${sbus-path}/interface/sbus_iterator_readers.h" + #include "${sbus-path}/interface_dbus/sbus_dbus_client_async.h" + #include "${header:arguments}" + #include "${header:keygens}" + #include "${header:client_properties}" + +</template> + +<template name="method-invoker"> + struct sbus_method_in_${input-signature}_out_${output-signature}_state { + <toggle name="if-input-arguments"> + struct _sbus_invoker_args_${input-signature} in; + </toggle> + <toggle name="if-output-arguments"> + struct _sbus_invoker_args_${output-signature} *out; + </toggle> + <toggle name="if-raw-output"> + DBusMessage *reply; + </toggle> + <toggle name="if-show-dummy"> + int dummy; + </toggle> + }; + + static void sbus_method_in_${input-signature}_out_${output-signature}_done(struct tevent_req *subreq); + + static struct tevent_req * + sbus_method_in_${input-signature}_out_${output-signature}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + <toggle name="if-raw-input"> + DBusMessage *raw_message) + <or> + sbus_invoker_keygen keygen, + const char *bus, + const char *path, + const char *iface, + const char *method<loop name="in">, + ${type} arg${index}</loop>) + </toggle> + { + struct sbus_method_in_${input-signature}_out_${output-signature}_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sbus_method_in_${input-signature}_out_${output-signature}_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + <toggle name="if-output-arguments"> + state->out = talloc_zero(state, struct _sbus_invoker_args_${output-signature}); + if (state->out == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unable to allocate space for output parameters!\n"); + ret = ENOMEM; + goto done; + } + + </toggle> + <loop name="in"> + state->in.arg${index} = arg${index}; + </loop> + + <toggle name="if-raw-input"> + subreq = sbus_call_method_send(state, conn, raw_message, NULL, NULL, NULL, + dbus_message_get_path(raw_message), + dbus_message_get_interface(raw_message), + dbus_message_get_member(raw_message), NULL); + </toggle> + <toggle name="if-input-arguments"> + subreq = sbus_call_method_send(state, conn, NULL, keygen, + (sbus_invoker_writer_fn)_sbus_invoker_write_${input-signature}, + bus, path, iface, method, &state->in); + </toggle> + <toggle name="if-empty-input"> + subreq = sbus_call_method_send(state, conn, NULL, keygen, NULL, + bus, path, iface, method, NULL); + </toggle> + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sbus_method_in_${input-signature}_out_${output-signature}_done, req); + + ret = EAGAIN; + + done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, conn->ev); + } + + return req; + } + + static void sbus_method_in_${input-signature}_out_${output-signature}_done(struct tevent_req *subreq) + { + struct sbus_method_in_${input-signature}_out_${output-signature}_state *state; + struct tevent_req *req; + DBusMessage *reply; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sbus_method_in_${input-signature}_out_${output-signature}_state); + + ret = sbus_call_method_recv(state, subreq, &reply); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + <toggle name="if-raw-output"> + state->reply = reply; + + </toggle> + <toggle name="if-output-arguments"> + ret = sbus_read_output(state->out, reply, (sbus_invoker_reader_fn)_sbus_invoker_read_${output-signature}, state->out); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + </toggle> + tevent_req_done(req); + return; + } + + static errno_t + sbus_method_in_${input-signature}_out_${output-signature}_recv + (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx, + struct tevent_req *req<or>struct tevent_req *req</toggle><toggle name="if-raw-output">, + DBusMessage **_reply) + <or><loop name="out">, + ${type} _arg${index}</loop>) + </toggle> + { + <toggle name="if-raw-output"> + errno_t ret; + </toggle> + <toggle name="if-has-output"> + struct sbus_method_in_${input-signature}_out_${output-signature}_state *state; + state = tevent_req_data(req, struct sbus_method_in_${input-signature}_out_${output-signature}_state); + + </toggle> + TEVENT_REQ_RETURN_ON_ERROR(req); + + <toggle name="if-output-arguments"> + <loop name="out-static"> + *_arg${index} = state->out->arg${index}; + </loop> + <loop name="out-talloc"> + *_arg${index} = talloc_steal(mem_ctx, state->out->arg${index}); + </loop> + + </toggle> + <toggle name="if-raw-output"> + /* Bounded reference cannot be unreferenced with dbus_message_unref. + * For that reason we do not allow NULL memory context as it would + * result in leaking the message memory. */ + if (mem_ctx == NULL) { + return EINVAL; + } + + ret = sbus_message_bound_steal(mem_ctx, state->reply); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to steal message [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + *_reply = state->reply; + + </toggle> + return EOK; + } + +</template> + +<template name="method-caller"> + struct tevent_req * + sbus_call_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + <toggle name="if-raw-input"> + DBusMessage *raw_message) + <or> + const char *busname, + const char *object_path<loop name="in">, + ${type} arg_${name}</loop>) + </toggle> + { + <toggle name="if-raw-input"> + return sbus_method_in_${input-signature}_out_${output-signature}_send(mem_ctx, conn, raw_message); + <or> + return sbus_method_in_${input-signature}_out_${output-signature}_send(mem_ctx, conn, ${keygen}, + busname, object_path, "${iface}", "${method}"<loop name="in">, arg_${name}</loop>); + </toggle> + } + + errno_t + sbus_call_${token}_recv + (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx, + struct tevent_req *req<or>struct tevent_req *req</toggle><toggle name="if-raw-output">, + DBusMessage **_reply) + <or><loop name="out">, + ${type} _${name}</loop>) + </toggle> + { + return sbus_method_in_${input-signature}_out_${output-signature}_recv(<toggle name="if-use-talloc">mem_ctx, </toggle>req<toggle line name="if-raw-output">, _reply<or><loop name="out">, _${name}</loop></toggle>); + } + +</template> + +<template name="signal-invoker"> + static void + sbus_emit_signal_${input-signature} + (struct sbus_connection *conn, + <toggle name="if-raw-input"> + DBusMessage *raw_message) + <or> + const char *path, + const char *iface, + const char *signal_name<loop name="in">, + ${type} arg${index}</loop>) + </toggle> + { + <toggle name="if-input-arguments"> + struct _sbus_invoker_args_${input-signature} args; + + <loop name="in"> + args.arg${index} = arg${index}; + </loop> + + </toggle> + <toggle name="if-raw-input"> + sbus_call_signal_send(conn, raw_message, NULL, + dbus_message_get_path(raw_message), + dbus_message_get_interface(raw_message), + dbus_message_get_member(raw_message), NULL); + </toggle> + <toggle name="if-input-arguments"> + sbus_call_signal_send(conn, NULL, (sbus_invoker_writer_fn)_sbus_invoker_write_${input-signature}, + path, iface, signal_name, &args); + </toggle> + <toggle name="if-empty-input"> + sbus_call_signal_send(conn, NULL, NULL, path, iface, signal_name, NULL); + </toggle> + } + +</template> + +<template name="signal-caller"> + void + sbus_emit_${token} + (struct sbus_connection *conn, + <toggle name="if-raw-input"> + DBusMessage *raw_message) + <or> + const char *object_path<loop name="in">, + ${type} arg_${name}</loop>) + </toggle> + { + <toggle name="if-raw-input"> + sbus_emit_signal_${input-signature}(conn, raw_message); + <or> + sbus_emit_signal_${input-signature}(conn, object_path, + "${iface}", "${signal}"<loop name="in">, arg_${name}</loop>); + </toggle> + } + +</template> + +<template name="get-invoker"> + struct sbus_get_${output-signature}_state { + struct _sbus_invoker_args_${output-signature} *out; + }; + + static void sbus_get_${output-signature}_done(struct tevent_req *subreq); + + static struct tevent_req * + sbus_get_${output-signature}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path, + const char *interface, + const char *property) + { + struct sbus_get_${output-signature}_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sbus_get_${output-signature}_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->out = talloc_zero(state, struct _sbus_invoker_args_${output-signature}); + if (state->out == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unable to allocate space for output parameters!\n"); + ret = ENOMEM; + goto done; + } + + subreq = sbus_call_DBusProperties_Get_send(mem_ctx, conn, + busname, object_path, interface, property); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sbus_get_${output-signature}_done, req); + + ret = EAGAIN; + + done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, conn->ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, conn->ev); + } + + return req; + } + + static void sbus_get_${output-signature}_done(struct tevent_req *subreq) + { + struct sbus_get_${output-signature}_state *state; + sbus_value_reader_fn reader; + sbus_value_reader_talloc_fn reader_talloc; + struct tevent_req *req; + DBusMessage *reply; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sbus_get_${output-signature}_state); + + ret = sbus_call_DBusProperties_Get_recv(state, subreq, &reply); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + reader = <toggle line name="if-use-talloc">NULL<or>(sbus_value_reader_fn)sbus_iterator_read_${output-signature}</toggle>; + reader_talloc = <toggle line name="if-use-talloc">(sbus_value_reader_talloc_fn)sbus_iterator_read_${output-signature}<or>NULL</toggle>; + ret = sbus_parse_get_message(state->out, reader, reader_talloc, reply, &state->out->arg0); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; + } + + static errno_t + sbus_get_${output-signature}_recv + (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx, + struct tevent_req *req, + <or>struct tevent_req *req, + </toggle> + ${output-type} _value) + { + struct sbus_get_${output-signature}_state *state; + state = tevent_req_data(req, struct sbus_get_${output-signature}_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_value = <toggle line name="if-use-talloc">talloc_steal(mem_ctx, state->out->arg0);<or>state->out->arg0</toggle>; + + return EOK; + } + +</template> + +<template name="set-invoker"> + struct sbus_set_${input-signature}_state { + struct _sbus_invoker_args_${input-signature} in; + }; + + static void sbus_set_${input-signature}_done(struct tevent_req *subreq); + + static struct tevent_req * + sbus_set_${input-signature}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path, + const char *interface, + const char *property, + ${input-type} value) + { + struct sbus_set_${input-signature}_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + DBusMessage *raw_message; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sbus_set_${input-signature}_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->in.arg0 = value; + + raw_message = sbus_create_set_call(state, + (sbus_invoker_writer_fn)_sbus_invoker_write_${input-signature}, + busname, object_path, interface, property, + "${dbus-type}", &state->in); + if (raw_message == NULL) { + ret = ENOMEM; + goto done; + } + + subreq = sbus_call_DBusProperties_Set_send(mem_ctx, conn, raw_message); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sbus_set_${input-signature}_done, req); + + ret = EAGAIN; + + done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, conn->ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, conn->ev); + } + + return req; + } + + static void sbus_set_${input-signature}_done(struct tevent_req *subreq) + { + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + + ret = sbus_call_DBusProperties_Set_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; + } + + static errno_t + sbus_set_${input-signature}_recv + (struct tevent_req *req) + { + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; + } + +</template> + +<template name="property-caller"> + <toggle name="get-static"> + struct tevent_req * + sbus_get_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path) + { + return sbus_get_${output-signature}_send(mem_ctx, + conn, busname, object_path, + "${iface}", "${property}"); + } + + errno_t sbus_get_${token}_recv + (struct tevent_req *req, + ${output-type} _value) + { + return sbus_get_${output-signature}_recv(req, _value); + } + + </toggle> + <toggle name="get-talloc"> + struct tevent_req * + sbus_get_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path) + { + return sbus_get_${output-signature}_send(mem_ctx, + conn, busname, object_path, + "${iface}", "${property}"); + } + + errno_t sbus_get_${token}_recv + (TALLOC_CTX *mem_ctx, + struct tevent_req *req, + ${output-type} _value) + { + return sbus_get_${output-signature}_recv(mem_ctx, req, _value); + } + + </toggle> + <toggle name="set"> + struct tevent_req * + sbus_set_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path, + ${input-type} value) + { + return sbus_set_${input-signature}_send(mem_ctx, + conn, busname, object_path, + "${iface}", "${property}", value); + } + + errno_t sbus_set_${token}_recv + (struct tevent_req *req) + { + return sbus_set_${input-signature}_recv(req); + } + + </toggle> +</template> + +<template name="getall-caller"> + struct sbus_getall_${token}_state { + struct sbus_all_${token} *properties; + }; + + static void sbus_getall_${token}_done(struct tevent_req *subreq); + + struct tevent_req * + sbus_getall_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path) + { + struct sbus_getall_${token}_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sbus_getall_${token}_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->properties = talloc_zero(state, struct sbus_all_${token}); + if (state->properties == NULL) { + ret = ENOMEM; + goto done; + } + + subreq = sbus_call_DBusProperties_GetAll_send(mem_ctx, conn, + busname, object_path, "${iface}"); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sbus_getall_${token}_done, req); + + ret = EAGAIN; + + done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, conn->ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, conn->ev); + } + + return req; + } + + static void sbus_getall_${token}_done(struct tevent_req *subreq) + { + struct sbus_getall_${token}_state *state; + struct tevent_req *req; + DBusMessage *reply; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sbus_getall_${token}_state); + + ret = sbus_call_DBusProperties_GetAll_recv(state, subreq, &reply); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + struct sbus_parse_getall_table table[] = { + <loop name="property-static"> + {"${name}", (sbus_value_reader_fn)sbus_iterator_read_${output-signature}, NULL, + &state->properties->${name}.value, &state->properties->${name}.is_set}, + </loop> + <loop name="property-talloc"> + {"${name}", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_${output-signature}, + &state->properties->${name}.value, &state->properties->${name}.is_set}, + </loop> + {NULL, NULL, NULL, NULL, NULL} + }; + + ret = sbus_parse_getall_message(state, table, reply); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; + } + + errno_t sbus_getall_${token}_recv + (TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct sbus_all_${token} **_properties) + { + struct sbus_getall_${token}_state *state; + state = tevent_req_data(req, struct sbus_getall_${token}_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_properties = talloc_steal(mem_ctx, state->properties); + + return EOK; + } + +</template> diff --git a/src/sbus/codegen/templates/client_async.h.tpl b/src/sbus/codegen/templates/client_async.h.tpl new file mode 100644 index 0000000..97b9f7c --- /dev/null +++ b/src/sbus/codegen/templates/client_async.h.tpl @@ -0,0 +1,137 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #ifndef ${file-guard} + #define ${file-guard} + + #include <errno.h> + #include <talloc.h> + #include <tevent.h> + + #include "${sbus-path}/sbus.h" + #include "${header:client_properties}" + <loop name="custom-type-header"> + #include "${custom-type-header}" + </loop> + +</template> + +<template name="method-caller"> + struct tevent_req * + sbus_call_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + <toggle name="if-raw-input"> + DBusMessage *raw_message); + <or> + const char *busname, + const char *object_path<loop name="in">, + ${type} arg_${name}</loop>); + </toggle> + + errno_t + sbus_call_${token}_recv + (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx, + struct tevent_req *req<or>struct tevent_req *req</toggle><toggle name="if-raw-output">, + DBusMessage **_reply); + <or><loop name="out">, + ${type} _${name}</loop>); + </toggle> + +</template> + +<template name="signal-caller"> + void + sbus_emit_${token} + (struct sbus_connection *conn, + <toggle name="if-raw-input"> + DBusMessage *raw_message); + <or> + const char *object_path<loop name="in">, + ${type} arg_${name}</loop>); + </toggle> + +</template> + +<template name="property-caller"> + <toggle name="get-static"> + struct tevent_req * + sbus_get_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path); + + errno_t + sbus_get_${token}_recv + (struct tevent_req *req, + ${output-type} _value); + + </toggle> + <toggle name="get-talloc"> + struct tevent_req * + sbus_get_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path); + + errno_t + sbus_get_${token}_recv + (TALLOC_CTX *mem_ctx, + struct tevent_req *req, + ${output-type} _value); + + </toggle> + <toggle name="set"> + struct tevent_req * + sbus_set_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path, + ${input-type} value); + + errno_t + sbus_set_${token}_recv + (struct tevent_req *req); + + </toggle> +</template> + +<template name="getall-caller"> + struct tevent_req * + sbus_getall_${token}_send + (TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + const char *busname, + const char *object_path); + + errno_t + sbus_getall_${token}_recv + (TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct sbus_all_${token} **_properties); + +</template> + +<template name="file-footer"> + #endif /* ${file-guard} */ +</template> diff --git a/src/sbus/codegen/templates/client_properties.h.tpl b/src/sbus/codegen/templates/client_properties.h.tpl new file mode 100644 index 0000000..1a3330d --- /dev/null +++ b/src/sbus/codegen/templates/client_properties.h.tpl @@ -0,0 +1,47 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #ifndef ${file-guard} + #define ${file-guard} + + #include <stdint.h> + #include <stdbool.h> + + <loop name="custom-type-header"> + #include "${custom-type-header}" + </loop> + +</template> + +<template name="properties"> + struct sbus_all_${token} { + <loop name="property"> + struct { + bool is_set; + ${input-type} value; + } ${name}; + </loop> + }; + +</template> + +<template name="file-footer"> + #endif /* ${file-guard} */ +</template> diff --git a/src/sbus/codegen/templates/client_sync.c.tpl b/src/sbus/codegen/templates/client_sync.c.tpl new file mode 100644 index 0000000..fe9a3a4 --- /dev/null +++ b/src/sbus/codegen/templates/client_sync.c.tpl @@ -0,0 +1,444 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #include <errno.h> + #include <talloc.h> + #include <dbus/dbus.h> + + #include "${sbus-path}/sbus_sync.h" + #include "${sbus-path}/sbus_sync_private.h" + #include "${sbus-path}/sbus_message.h" + #include "${sbus-path}/interface/sbus_iterator_readers.h" + #include "${sbus-path}/interface_dbus/sbus_dbus_client_sync.h" + #include "${header:arguments}" + #include "${header:client_properties}" + +</template> + +<template name="method-invoker"> + static errno_t + sbus_method_in_${input-signature}_out_${output-signature} + (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + <or>struct sbus_sync_connection *conn, + </toggle><toggle name="if-raw-input"> + DBusMessage *raw_message<or> + const char *bus, + const char *path, + const char *iface, + const char *method<loop name="in">, + ${type} arg${index}</loop></toggle> + <toggle name="if-raw-output">, + DBusMessage **_reply) + <or><loop name="out">, + ${type} _arg${index}</loop>) + </toggle> + { + TALLOC_CTX *tmp_ctx; + <toggle name="if-input-arguments"> + struct _sbus_invoker_args_${input-signature} in; + </toggle> + <toggle name="if-output-arguments"> + struct _sbus_invoker_args_${output-signature} *out; + </toggle> + DBusMessage *reply; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + <toggle name="if-output-arguments"> + out = talloc_zero(tmp_ctx, struct _sbus_invoker_args_${output-signature}); + if (out == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unable to allocate space for output parameters!\n"); + ret = ENOMEM; + goto done; + } + + </toggle> + <loop name="in"> + in.arg${index} = arg${index}; + </loop> + + <toggle name="if-raw-input"> + ret = sbus_sync_call_method(tmp_ctx, conn, raw_message, NULL, NULL, + dbus_message_get_path(raw_message), + dbus_message_get_interface(raw_message), + dbus_message_get_member(raw_message), + NULL, &reply); + </toggle> + <toggle name="if-input-arguments"> + ret = sbus_sync_call_method(tmp_ctx, conn, NULL, + (sbus_invoker_writer_fn)_sbus_invoker_write_${input-signature}, + bus, path, iface, method, &in, &reply); + </toggle> + <toggle name="if-empty-input"> + ret = sbus_sync_call_method(tmp_ctx, conn, NULL, NULL, + bus, path, iface, method, NULL, &reply); + </toggle> + if (ret != EOK) { + goto done; + } + + <toggle name="if-raw-output"> + /* Bounded reference cannot be unreferenced with dbus_message_unref. + * For that reason we do not allow NULL memory context as it would + * result in leaking the message memory. */ + if (mem_ctx == NULL) { + ret = EINVAL; + goto done; + } + + ret = sbus_message_bound_steal(mem_ctx, reply); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to steal message [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + *_reply = reply; + + </toggle> + <toggle name="if-output-arguments"> + ret = sbus_read_output(out, reply, (sbus_invoker_reader_fn)_sbus_invoker_read_${output-signature}, out); + if (ret != EOK) { + goto done; + } + + <loop name="out-static"> + *_arg${index} = out->arg${index}; + </loop> + <loop name="out-talloc"> + *_arg${index} = talloc_steal(mem_ctx, out->arg${index}); + </loop> + + </toggle> + ret = EOK; + + done: + talloc_free(tmp_ctx); + + return ret; + } + +</template> + +<template name="method-caller"> + errno_t + sbus_call_${token} + (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + <or>struct sbus_sync_connection *conn, + </toggle><toggle name="if-raw-input"> + DBusMessage *raw_message<or> + const char *busname, + const char *object_path<loop name="in">, + ${type} arg_${name}</loop></toggle> + <toggle name="if-raw-output">, + DBusMessage **_reply) + <or><loop name="out">, + ${type} _arg_${name}</loop>) + </toggle> + { + return sbus_method_in_${input-signature}_out_${output-signature}(<toggle name="if-use-talloc">mem_ctx, </toggle>conn, + <toggle name="if-raw-input"> + raw_message<or> + busname, object_path, "${iface}", "${method}"<loop name="in">, arg_${name}</loop></toggle> + <toggle name="if-raw-output">, + _reply); + <or><loop name="out">, + _arg_${name}</loop>); + </toggle> + } + +</template> + +<template name="signal-invoker"> + static void + sbus_emit_signal_${input-signature} + (struct sbus_sync_connection *conn, + <toggle name="if-raw-input"> + DBusMessage *raw_message) + <or> + const char *path, + const char *iface, + const char *signal_name<loop name="in">, + ${type} arg${index}</loop>) + </toggle> + { + <toggle name="if-input-arguments"> + struct _sbus_invoker_args_${input-signature} args; + + <loop name="in"> + args.arg${index} = arg${index}; + </loop> + + </toggle> + <toggle name="if-raw-input"> + sbus_sync_call_signal(conn, raw_message, NULL, + dbus_message_get_path(raw_message), + dbus_message_get_interface(raw_message), + dbus_message_get_member(raw_message), NULL); + </toggle> + <toggle name="if-input-arguments"> + sbus_sync_call_signal(conn, NULL, (sbus_invoker_writer_fn)_sbus_invoker_write_${input-signature}, + path, iface, signal_name, &args); + </toggle> + <toggle name="if-empty-input"> + sbus_sync_call_signal(conn, NULL, NULL, path, iface, signal_name, NULL); + </toggle> + } + +</template> + +<template name="signal-caller"> + void + sbus_sync_emit_${token} + (struct sbus_sync_connection *conn, + <toggle name="if-raw-input"> + DBusMessage *raw_message) + <or> + const char *object_path<loop name="in">, + ${type} arg_${name}</loop>) + </toggle> + { + <toggle name="if-raw-input"> + sbus_emit_signal_${input-signature}(conn, raw_message); + <or> + sbus_emit_signal_${input-signature}(conn, object_path, + "${iface}", "${signal}"<loop name="in">, arg_${name}</loop>); + </toggle> + } + +</template> + +<template name="get-invoker"> + static errno_t + sbus_get_${output-signature} + (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + <or>struct sbus_sync_connection *conn, + </toggle> + const char *bus, + const char *path, + const char *iface, + const char *property, + ${output-type} _value) + { + TALLOC_CTX *tmp_ctx; + struct _sbus_invoker_args_${output-signature} *out; + sbus_value_reader_fn reader; + sbus_value_reader_talloc_fn reader_talloc; + DBusMessage *reply; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + out = talloc_zero(tmp_ctx, struct _sbus_invoker_args_${output-signature}); + if (out == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unable to allocate space for output parameters!\n"); + ret = ENOMEM; + goto done; + } + + ret = sbus_call_DBusProperties_Get(tmp_ctx, conn, + bus, path, iface, property, &reply); + if (ret != EOK) { + goto done; + } + + reader = <toggle line name="if-use-talloc">NULL<or>(sbus_value_reader_fn)sbus_iterator_read_${output-signature}</toggle>; + reader_talloc = <toggle line name="if-use-talloc">(sbus_value_reader_talloc_fn)sbus_iterator_read_${output-signature}<or>NULL</toggle>; + ret = sbus_parse_get_message(out, reader, reader_talloc, reply, &out->arg0); + if (ret != EOK) { + goto done; + } + + *_value = <toggle line name="if-use-talloc">talloc_steal(mem_ctx, out->arg0)<or>out->arg0</toggle>; + + ret = EOK; + + done: + talloc_free(tmp_ctx); + + return ret; + } + +</template> + +<template name="set-invoker"> + static errno_t + sbus_set_${input-signature} + (struct sbus_sync_connection *conn, + const char *bus, + const char *path, + const char *iface, + const char *property, + ${input-type} value) + { + TALLOC_CTX *tmp_ctx; + struct _sbus_invoker_args_${input-signature} in; + DBusMessage *raw_message; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + in.arg0 = value; + + raw_message = sbus_create_set_call(tmp_ctx, + (sbus_invoker_writer_fn)_sbus_invoker_write_${input-signature}, + bus, path, iface, property, + "${dbus-type}", &in); + if (raw_message == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sbus_call_DBusProperties_Set(conn, raw_message); + if (ret != EOK) { + goto done; + } + + ret = EOK; + + done: + talloc_free(tmp_ctx); + + return ret; + } + +</template> + +<template name="property-caller"> + <toggle name="get-static"> + errno_t + sbus_get_${token} + (struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + ${output-type} _value) + { + return sbus_get_${output-signature}(conn, busname, object_path, + "${iface}", "${property}", _value); + } + + </toggle> + <toggle name="get-talloc"> + errno_t + sbus_get_${token} + (TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + ${output-type} _value) + { + return sbus_get_${output-signature}(mem_ctx, conn, busname, object_path, + "${iface}", "${property}", _value); + } + + </toggle> + <toggle name="set"> + errno_t + sbus_set_${token} + (struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + ${input-type} value) + { + return sbus_set_${input-signature}(conn, busname, object_path, + "${iface}", "${property}", value); + } + + </toggle> +</template> + +<template name="getall-caller"> + errno_t + sbus_getall_${token} + (TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + struct sbus_all_${token} **_properties) + { + TALLOC_CTX *tmp_ctx; + struct sbus_all_${token} *properties; + DBusMessage *reply; + errno_t ret; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + properties = talloc_zero(tmp_ctx, struct sbus_all_${token}); + if (properties == NULL) { + ret = ENOMEM; + goto done; + } + + struct sbus_parse_getall_table table[] = { + <loop name="property-static"> + {"${name}", (sbus_value_reader_fn)sbus_iterator_read_${output-signature}, NULL, + &properties->${name}.value, &properties->${name}.is_set}, + </loop> + <loop name="property-talloc"> + {"${name}", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_${output-signature}, + &properties->${name}.value, &properties->${name}.is_set}, + </loop> + {NULL, NULL, NULL, NULL, NULL} + }; + + ret = sbus_call_DBusProperties_GetAll(tmp_ctx, conn, + busname, object_path, "${iface}", &reply); + if (ret != EOK) { + goto done; + } + + ret = sbus_parse_getall_message(properties, table, reply); + if (ret != EOK) { + goto done; + } + + *_properties = talloc_steal(mem_ctx, properties); + + ret = EOK; + + done: + talloc_free(tmp_ctx); + + return ret; + } + +</template> diff --git a/src/sbus/codegen/templates/client_sync.h.tpl b/src/sbus/codegen/templates/client_sync.h.tpl new file mode 100644 index 0000000..acf4d4d --- /dev/null +++ b/src/sbus/codegen/templates/client_sync.h.tpl @@ -0,0 +1,112 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #ifndef ${file-guard} + #define ${file-guard} + + #include <errno.h> + #include <talloc.h> + #include <tevent.h> + + #include "${sbus-path}/sbus_sync.h" + #include "${header:client_properties}" + <loop name="custom-type-header"> + #include "${custom-type-header}" + </loop> + +</template> + +<template name="method-caller"> + errno_t + sbus_call_${token} + (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + <or>struct sbus_sync_connection *conn, + </toggle><toggle name="if-raw-input"> + DBusMessage *raw_message<or> + const char *busname, + const char *object_path<loop name="in">, + ${type} arg_${name}</loop></toggle> + <toggle name="if-raw-output">, + DBusMessage **_reply); + <or><loop name="out">, + ${type} _arg_${name}</loop>); + </toggle> + +</template> + +<template name="signal-caller"> + void + sbus_sync_emit_${token} + (struct sbus_sync_connection *conn, + <toggle name="if-raw-input"> + DBusMessage *raw_message); + <or> + const char *object_path<loop name="in">, + ${type} arg_${name}</loop>); + </toggle> + +</template> + +<template name="property-caller"> + <toggle name="get-static"> + errno_t + sbus_get_${token} + (struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + ${output-type} _value); + + </toggle> + <toggle name="get-talloc"> + errno_t + sbus_get_${token} + (TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + ${output-type} _value); + + </toggle> + <toggle name="set"> + errno_t + sbus_set_${token} + (struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + ${input-type} value); + + </toggle> +</template> + +<template name="getall-caller"> + errno_t + sbus_getall_${token} + (TALLOC_CTX *mem_ctx, + struct sbus_sync_connection *conn, + const char *busname, + const char *object_path, + struct sbus_all_${token} **_properties); + +</template> + +<template name="file-footer"> + #endif /* ${file-guard} */ +</template> diff --git a/src/sbus/codegen/templates/interface.h.tpl b/src/sbus/codegen/templates/interface.h.tpl new file mode 100644 index 0000000..89da556 --- /dev/null +++ b/src/sbus/codegen/templates/interface.h.tpl @@ -0,0 +1,132 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #ifndef ${file-guard} + #define ${file-guard} + + #include "${sbus-path}/sbus_interface_declarations.h" + #include "${header:invokers}" + #include "${header:symbols}" + #include "${header:keygens}" + +</template> + +<template name="interface"> + /* Interface: ${name} */ + #define SBUS_IFACE_${token}(methods, signals, properties) ({ \ + sbus_interface("${name}", <toggle line name="annotations">_sbus_annotations_${token}<or>NULL</toggle>, \ + (methods), (signals), (properties)); \ + }) + +</template> + +<template name="method"> + /* Method: ${interface}.${name} */ + #define SBUS_METHOD_SYNC_${token}(handler, data) ({ \ + SBUS_CHECK_SYNC((handler), (data)<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop><loop name="out">, ${type}</loop><loop name="out-raw">, ${type}</loop>); \ + sbus_method_sync("${name}", \ + &_sbus_args_${token}, \ + <toggle line name="annotations">_sbus_annotations_${token}<or>NULL</toggle>, \ + _sbus_invoke_in_${input-signature}_out_${output-signature}_send, \ + <toggle line name="keygen">_sbus_key_${key-signature}<loop name="key-argument">_${key-index}</loop><or>NULL</toggle>, \ + (handler), (data)); \ + }) + + #define SBUS_METHOD_ASYNC_${token}(handler_send, handler_recv, data) ({ \ + SBUS_CHECK_SEND((handler_send), (data)<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop><loop name="out-raw">, ${type}</loop>); \ + SBUS_CHECK_RECV((handler_recv)<loop name="out">, ${type}</loop>); \ + sbus_method_async("${name}", \ + &_sbus_args_${token}, \ + <toggle line name="annotations">_sbus_annotations_${token}<or>NULL</toggle>, \ + _sbus_invoke_in_${input-signature}_out_${output-signature}_send, \ + <toggle line name="keygen">_sbus_key_${key-signature}<loop name="key-argument">_${key-index}</loop><or>NULL</toggle>, \ + (handler_send), (handler_recv), (data)); \ + }) + +</template> + +<template name="signal"> + /* Signal: ${interface}.${name} */ + #define SBUS_SIGNAL_EMITS_${token}() ({ \ + sbus_signal("${name}", \ + _sbus_args_${token}, \ + <toggle line name="annotations">_sbus_annotations_${token}<or>NULL</toggle>); \ + }) + + #define SBUS_SIGNAL_SYNC_${token}(path, handler, data) ({ \ + SBUS_CHECK_SYNC((handler), (data)<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop>); \ + sbus_listener_sync("${interface}", "${name}", (path), \ + _sbus_invoke_in_${input-signature}_out_${output-signature}_send, \ + <toggle line name="keygen">_sbus_key_${key-signature}<loop name="key-argument">_${key-index}</loop><or>NULL</toggle>, \ + (handler), (data)); \ + }) + + #define SBUS_SIGNAL_ASYNC_${token}(path, handler_send, handler_recv, data) ({ \ + SBUS_CHECK_SEND((handler_send), (data)<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop>); \ + SBUS_CHECK_RECV((handler_recv)); \ + sbus_listener_async("${interface}", "${name}", (path), \ + _sbus_invoke_in_${input-signature}_out_${output-signature}_send, \ + <toggle line name="keygen">_sbus_key_${key-signature}<loop name="key-argument">_${key-index}</loop><or>NULL</toggle>, \ + (handler_send), (handler_recv), (data)); \ + }) + +</template> + +<template name="property"> + /* Property: ${interface}.${name} */ + #define SBUS_GETTER_SYNC_${token}(handler, data) ({ \ + SBUS_CHECK_SYNC((handler), (data)<loop name="out">, ${type}</loop><loop name="out-raw">, ${type}</loop>); \ + sbus_property_sync("${name}", "${type}", SBUS_PROPERTY_READABLE, \ + <toggle line name="annotations">_sbus_annotations_${token}<or>NULL</toggle>, \ + _sbus_invoke_in__out_${output-signature}_send, \ + (handler), (data)); \ + }) + + #define SBUS_GETTER_ASYNC_${token}(handler_send, handler_recv, data) ({ \ + SBUS_CHECK_SEND((handler_send), (data)<loop name="out-raw">, ${type}</loop>); \ + SBUS_CHECK_RECV((handler_recv)<loop name="out">, ${type}</loop>); \ + sbus_property_async("${name}", "${type}", SBUS_PROPERTY_READABLE, \ + <toggle line name="annotations">_sbus_annotations_${token}<or>NULL</toggle>, \ + _sbus_invoke_in__out_${output-signature}_send, \ + (handler_send), (handler_recv), (data)); \ + }) + + #define SBUS_SETTER_SYNC_${token}(handler, data) ({\ + SBUS_CHECK_SYNC((handler), (data)<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop>); \ + sbus_property_sync("${name}", "${type}", SBUS_PROPERTY_WRITABLE, \ + <toggle line name="annotations">_sbus_annotations_${token}<or>NULL</toggle>, \ + _sbus_invoke_in_${input-signature}_out__send, \ + (handler), (data)); \ + }) + + #define SBUS_SETTER_ASYNC_${token}(handler_send, handler_recv, data) ({ \ + SBUS_CHECK_SEND((handler_send), (data)<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop>); \ + SBUS_CHECK_RECV((handler_recv)); \ + sbus_property_async("${name}", "${type}", SBUS_PROPERTY_WRITABLE, \ + <toggle line name="annotations">_sbus_annotations_${token}<or>NULL</toggle>, \ + _sbus_invoke_in_${input-signature}_out__send, \ + (handler_send), (handler_recv), (data)); \ + }) + +</template> + +<template name="file-footer"> + #endif /* ${file-guard} */ +</template> diff --git a/src/sbus/codegen/templates/invokers.c.tpl b/src/sbus/codegen/templates/invokers.c.tpl new file mode 100644 index 0000000..a1c2b31 --- /dev/null +++ b/src/sbus/codegen/templates/invokers.c.tpl @@ -0,0 +1,252 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #include <errno.h> + #include <talloc.h> + #include <tevent.h> + #include <dbus/dbus.h> + + #include "${sbus-path}/sbus_private.h" + #include "${sbus-path}/sbus_interface_declarations.h" + #include "${header:arguments}" + #include "${header:invokers}" + + static errno_t + sbus_invoker_schedule(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + void *handler, + void *private_data) + { + /* Schedule invoker as a timed event so it is processed after other + * event types. This will give dispatcher a chance to catch more + * messages before this invoker is triggered and therefore it will + * allow to potentially chain more request into one, especially for + * synchronous handlers. */ + + struct tevent_timer *te; + struct timeval tv; + + tv = tevent_timeval_current_ofs(0, 5); + te = tevent_add_timer(ev, mem_ctx, tv, handler, private_data); + if (te == NULL) { + /* There is not enough memory to create a timer. We can't do + * anything about it. */ + DEBUG(SSSDBG_OP_FAILURE, "Could not add invoker event!\n"); + return ENOMEM; + } + + return EOK; + } + +</template> + +<template name="invoker"> + struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state { + <toggle name="if-input-arguments"> + struct _sbus_invoker_args_${input-signature} *in; + </toggle> + <toggle name="if-output-arguments"> + struct _sbus_invoker_args_${output-signature} out; + </toggle> + struct { + enum sbus_handler_type type; + void *data; + errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop><loop name="out">, ${type}</loop><loop name="out-raw">, ${type}</loop>); + struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop><loop name="out-raw">, ${type}</loop>); + errno_t (*recv)(TALLOC_CTX *, struct tevent_req *<loop name="out">, ${type}</loop>); + } handler; + + struct sbus_request *sbus_req; + DBusMessageIter *read_iterator; + DBusMessageIter *write_iterator; + }; + + static void + _sbus_invoke_in_${input-signature}_out_${output-signature}_step + (struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, + void *private_data); + + static void + _sbus_invoke_in_${input-signature}_out_${output-signature}_done + (struct tevent_req *subreq); + + struct tevent_req * + _sbus_invoke_in_${input-signature}_out_${output-signature}_send + (TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + sbus_invoker_keygen keygen, + const struct sbus_handler *handler, + DBusMessageIter *read_iterator, + DBusMessageIter *write_iterator, + const char **_key) + { + struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state *state; + struct tevent_req *req; + const char *key; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->handler.type = handler->type; + state->handler.data = handler->data; + state->handler.sync = handler->sync; + state->handler.send = handler->async_send; + state->handler.recv = handler->async_recv; + + state->sbus_req = sbus_req; + state->read_iterator = read_iterator; + state->write_iterator = write_iterator; + + <toggle name="if-input-arguments"> + state->in = talloc_zero(state, struct _sbus_invoker_args_${input-signature}); + if (state->in == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Unable to allocate space for input parameters!\n"); + ret = ENOMEM; + goto done; + } + + ret = _sbus_invoker_read_${input-signature}(state, read_iterator, state->in); + if (ret != EOK) { + goto done; + } + + </toggle> + ret = sbus_invoker_schedule(state, ev, _sbus_invoke_in_${input-signature}_out_${output-signature}_step, req); + if (ret != EOK) { + goto done; + } + + ret = sbus_request_key(state, keygen, sbus_req,<toggle name="if-input-arguments"> state->in<or> NULL</toggle>, &key); + if (ret != EOK) { + goto done; + } + + if (_key != NULL) { + *_key = talloc_steal(mem_ctx, key); + } + + ret = EAGAIN; + + done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; + } + + static void _sbus_invoke_in_${input-signature}_out_${output-signature}_step + (struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, + void *private_data) + { + struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = talloc_get_type(private_data, struct tevent_req); + state = tevent_req_data(req, struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state); + + switch (state->handler.type) { + case SBUS_HANDLER_SYNC: + if (state->handler.sync == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n"); + ret = ERR_INTERNAL; + goto done; + } + + ret = state->handler.sync(state, state->sbus_req, state->handler.data<loop name="in">, state->in->arg${index}</loop><loop name="in-raw">, state->read_iterator</loop><loop name="out">, &state->out.arg${index}</loop><loop name="out-raw">, state->write_iterator</loop>); + if (ret != EOK) { + goto done; + } + + <toggle name="if-output-arguments"> + ret = _sbus_invoker_write_${output-signature}(state->write_iterator, &state->out); + </toggle> + goto done; + case SBUS_HANDLER_ASYNC: + if (state->handler.send == NULL || state->handler.recv == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n"); + ret = ERR_INTERNAL; + goto done; + } + + subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data<loop name="in">, state->in->arg${index}</loop><loop name="in-raw">, state->read_iterator</loop><loop name="out-raw">, state->write_iterator</loop>); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, _sbus_invoke_in_${input-signature}_out_${output-signature}_done, req); + ret = EAGAIN; + goto done; + } + + ret = ERR_INTERNAL; + + done: + if (ret == EOK) { + tevent_req_done(req); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + } + } + + static void _sbus_invoke_in_${input-signature}_out_${output-signature}_done(struct tevent_req *subreq) + { + struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state); + + ret = state->handler.recv(state, subreq<loop name="out">, &state->out.arg${index}</loop>); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + <toggle name="if-output-arguments"> + ret = _sbus_invoker_write_${output-signature}(state->write_iterator, &state->out); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + </toggle> + tevent_req_done(req); + return; + } + +</template> diff --git a/src/sbus/codegen/templates/invokers.h.tpl b/src/sbus/codegen/templates/invokers.h.tpl new file mode 100644 index 0000000..710b7cd --- /dev/null +++ b/src/sbus/codegen/templates/invokers.h.tpl @@ -0,0 +1,52 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #ifndef ${file-guard} + #define ${file-guard} + + #include <talloc.h> + #include <tevent.h> + #include <dbus/dbus.h> + + #include "${sbus-path}/sbus_interface_declarations.h" + #include "${sbus-path}/sbus_request.h" + + #define _sbus_declare_invoker(input, output) \ + struct tevent_req * \ + _sbus_invoke_in_ ## input ## _out_ ## output ## _send \ + (TALLOC_CTX *mem_ctx, \ + struct tevent_context *ev, \ + struct sbus_request *sbus_req, \ + sbus_invoker_keygen keygen, \ + const struct sbus_handler *handler, \ + DBusMessageIter *read_iterator, \ + DBusMessageIter *write_iterator, \ + const char **_key) + +</template> + +<template name="invoker"> + _sbus_declare_invoker(${input-signature}, ${output-signature}); +</template> + +<template name="file-footer"> + + #endif /* ${file-guard} */ +</template> diff --git a/src/sbus/codegen/templates/keygens.c.tpl b/src/sbus/codegen/templates/keygens.c.tpl new file mode 100644 index 0000000..da7f10b --- /dev/null +++ b/src/sbus/codegen/templates/keygens.c.tpl @@ -0,0 +1,65 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #include <inttypes.h> + #include <talloc.h> + + #include "${sbus-path}/sbus_request.h" + #include "${header:arguments}" + #include "${header:keygens}" + +</template> + +<template name="key"> + const char * + _sbus_key_${key-signature}<loop line name="key-argument">_${key-index}</loop> + (TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct _sbus_invoker_args_${key-signature} *args) + { + if (sbus_req->sender == NULL) { + return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s<loop name="key-argument">:%${key-format}</loop>", + sbus_req->type, sbus_req->interface, sbus_req->member, + sbus_req->path<loop name="key-argument">, args->arg${key-index}</loop>); + } + + return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s<loop name="key-argument">:%${key-format}</loop>", + sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member, + sbus_req->path<loop name="key-argument">, args->arg${key-index}</loop>); + } + +</template> + +<template name="key-no-arguments"> + const char * + _sbus_key_${key-signature} + (TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req) + { + if (sbus_req->sender == NULL) { + return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s", + sbus_req->type, sbus_req->interface, sbus_req->member, sbus_req->path); + } + + return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s", + sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member, sbus_req->path); + } + +</template> diff --git a/src/sbus/codegen/templates/keygens.h.tpl b/src/sbus/codegen/templates/keygens.h.tpl new file mode 100644 index 0000000..6425c9a --- /dev/null +++ b/src/sbus/codegen/templates/keygens.h.tpl @@ -0,0 +1,50 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #ifndef ${file-guard} + #define ${file-guard} + + #include <talloc.h> + + #include "${sbus-path}/sbus_request.h" + #include "${header:arguments}" + +</template> + +<template name="key"> + const char * + _sbus_key_${key-signature}<loop line name="key-argument">_${key-index}</loop> + (TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct _sbus_invoker_args_${key-signature} *args); + +</template> + +<template name="key-no-arguments"> + const char * + _sbus_key_${key-signature} + (TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req); + +</template> + +<template name="file-footer"> + #endif /* ${file-guard} */ +</template> diff --git a/src/sbus/codegen/templates/server.h.tpl b/src/sbus/codegen/templates/server.h.tpl new file mode 100644 index 0000000..6268090 --- /dev/null +++ b/src/sbus/codegen/templates/server.h.tpl @@ -0,0 +1,32 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #ifndef ${file-guard} + #define ${file-guard} + + #include "${sbus-path}/sbus.h" + #include "${sbus-path}/sbus_interface.h" + #include "${header:interface}" + +</template> + +<template name="file-footer"> + #endif /* ${file-guard} */ +</template> diff --git a/src/sbus/codegen/templates/symbols.c.tpl b/src/sbus/codegen/templates/symbols.c.tpl new file mode 100644 index 0000000..476474d --- /dev/null +++ b/src/sbus/codegen/templates/symbols.c.tpl @@ -0,0 +1,65 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #include "${sbus-path}/sbus_interface_declarations.h" + #include "${header:symbols}" + +</template> + +<template name="annotations"> + const struct sbus_annotation + _sbus_annotations_${token}[] = { + <loop name="annotation"> + {.name = "${annotation-name}", .value = ${annotation-value}}, + </loop> + {NULL} + }; + +</template> + +<template name="method"> + const struct sbus_method_arguments + _sbus_args_${token} = { + .input = (const struct sbus_argument[]){ + <loop name="input"> + {.type = "${arg-type}", .name = "${arg-name}"}, + </loop> + {NULL} + }, + .output = (const struct sbus_argument[]){ + <loop name="output"> + {.type = "${arg-type}", .name = "${arg-name}"}, + </loop> + {NULL} + } + }; + +</template> + +<template name="signal"> + const struct sbus_argument + _sbus_args_${token}[] = { + <loop name="input"> + {.type = "${arg-type}", .name = "${arg-name}"}, + </loop> + {NULL} + }; + +</template> diff --git a/src/sbus/codegen/templates/symbols.h.tpl b/src/sbus/codegen/templates/symbols.h.tpl new file mode 100644 index 0000000..2795ace --- /dev/null +++ b/src/sbus/codegen/templates/symbols.h.tpl @@ -0,0 +1,48 @@ +<template name="file-header"> + /* + Generated by sbus code generator + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + #ifndef ${file-guard} + #define ${file-guard} + + #include "${sbus-path}/sbus_interface_declarations.h" + +</template> + +<template name="method"> + extern const struct sbus_method_arguments + _sbus_args_${token}; + +</template> + +<template name="annotations"> + extern const struct sbus_annotation + _sbus_annotations_${token}[]; + +</template> + +<template name="signal"> + extern const struct sbus_argument + _sbus_args_${token}[]; + +</template> + +<template name="file-footer"> + #endif /* ${file-guard} */ +</template> |