summaryrefslogtreecommitdiffstats
path: root/src/sbus/codegen
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/sbus/codegen
parentInitial commit. (diff)
downloadsssd-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')
-rw-r--r--src/sbus/codegen/dbus.xml100
-rw-r--r--src/sbus/codegen/sbus_CodeGen.py323
-rw-r--r--src/sbus/codegen/sbus_DataType.py87
-rw-r--r--src/sbus/codegen/sbus_Generator.py868
-rw-r--r--src/sbus/codegen/sbus_Introspection.py287
-rw-r--r--src/sbus/codegen/sbus_Invoker.py399
-rw-r--r--src/sbus/codegen/sbus_Template.py322
-rw-r--r--src/sbus/codegen/templates/arguments.c.tpl67
-rw-r--r--src/sbus/codegen/templates/arguments.h.tpl58
-rw-r--r--src/sbus/codegen/templates/client_async.c.tpl683
-rw-r--r--src/sbus/codegen/templates/client_async.h.tpl137
-rw-r--r--src/sbus/codegen/templates/client_properties.h.tpl47
-rw-r--r--src/sbus/codegen/templates/client_sync.c.tpl444
-rw-r--r--src/sbus/codegen/templates/client_sync.h.tpl112
-rw-r--r--src/sbus/codegen/templates/interface.h.tpl132
-rw-r--r--src/sbus/codegen/templates/invokers.c.tpl252
-rw-r--r--src/sbus/codegen/templates/invokers.h.tpl52
-rw-r--r--src/sbus/codegen/templates/keygens.c.tpl65
-rw-r--r--src/sbus/codegen/templates/keygens.h.tpl50
-rw-r--r--src/sbus/codegen/templates/server.h.tpl32
-rw-r--r--src/sbus/codegen/templates/symbols.c.tpl65
-rw-r--r--src/sbus/codegen/templates/symbols.h.tpl48
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>