summaryrefslogtreecommitdiffstats
path: root/src/sbus
diff options
context:
space:
mode:
Diffstat (limited to 'src/sbus')
-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
-rw-r--r--src/sbus/connection/sbus_connection.c460
-rw-r--r--src/sbus/connection/sbus_connection_connect.c430
-rw-r--r--src/sbus/connection/sbus_dbus.c188
-rw-r--r--src/sbus/connection/sbus_dbus_private.h34
-rw-r--r--src/sbus/connection/sbus_dispatcher.c156
-rw-r--r--src/sbus/connection/sbus_reconnect.c249
-rw-r--r--src/sbus/connection/sbus_send.c245
-rw-r--r--src/sbus/connection/sbus_watch.c629
-rw-r--r--src/sbus/interface/sbus_interface.c464
-rw-r--r--src/sbus/interface/sbus_introspection.c679
-rw-r--r--src/sbus/interface/sbus_iterator_readers.c414
-rw-r--r--src/sbus/interface/sbus_iterator_readers.h126
-rw-r--r--src/sbus/interface/sbus_iterator_writers.c365
-rw-r--r--src/sbus/interface/sbus_iterator_writers.h126
-rw-r--r--src/sbus/interface/sbus_properties.c894
-rw-r--r--src/sbus/interface/sbus_properties_parser.c198
-rw-r--r--src/sbus/interface/sbus_std_signals.c65
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_arguments.c271
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_arguments.h139
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_client_async.c751
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_client_async.h109
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_client_properties.h27
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_client_sync.c215
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_client_sync.h54
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_interface.h514
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_invokers.c2145
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_invokers.h55
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_keygens.c90
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_keygens.h51
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_server.h27
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_symbols.c254
-rw-r--r--src/sbus/interface_dbus/sbus_dbus_symbols.h88
-rw-r--r--src/sbus/request/sbus_message.c583
-rw-r--r--src/sbus/request/sbus_request.c818
-rw-r--r--src/sbus/request/sbus_request_call.c160
-rw-r--r--src/sbus/request/sbus_request_hash.c325
-rw-r--r--src/sbus/request/sbus_request_sender.c340
-rw-r--r--src/sbus/request/sbus_request_util.c67
-rw-r--r--src/sbus/router/sbus_router.c387
-rw-r--r--src/sbus/router/sbus_router_handler.c324
-rw-r--r--src/sbus/router/sbus_router_hash.c547
-rw-r--r--src/sbus/sbus.h460
-rw-r--r--src/sbus/sbus_annotations.h27
-rw-r--r--src/sbus/sbus_declarations.h112
-rw-r--r--src/sbus/sbus_errors.c127
-rw-r--r--src/sbus/sbus_errors.h45
-rw-r--r--src/sbus/sbus_interface.h522
-rw-r--r--src/sbus/sbus_interface_declarations.h497
-rw-r--r--src/sbus/sbus_message.h175
-rw-r--r--src/sbus/sbus_opath.c417
-rw-r--r--src/sbus/sbus_opath.h181
-rw-r--r--src/sbus/sbus_private.h671
-rw-r--r--src/sbus/sbus_request.h169
-rw-r--r--src/sbus/sbus_sync.h94
-rw-r--r--src/sbus/sbus_sync_private.h129
-rw-r--r--src/sbus/sbus_typeof.h53
-rw-r--r--src/sbus/server/sbus_server.c778
-rw-r--r--src/sbus/server/sbus_server_handler.c190
-rw-r--r--src/sbus/server/sbus_server_interface.c429
-rw-r--r--src/sbus/server/sbus_server_match.c450
-rw-r--r--src/sbus/sync/sbus_sync.c183
-rw-r--r--src/sbus/sync/sbus_sync_call.c101
84 files changed, 24503 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>
diff --git a/src/sbus/connection/sbus_connection.c b/src/sbus/connection/sbus_connection.c
new file mode 100644
index 0000000..fb53f7d
--- /dev/null
+++ b/src/sbus/connection/sbus_connection.c
@@ -0,0 +1,460 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <time.h>
+#include <errno.h>
+#include <dhash.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/connection/sbus_dbus_private.h"
+#include "sbus/sbus_private.h"
+
+struct sbus_connection_access {
+ const char *name;
+ sbus_connection_access_check_fn check_fn;
+ sbus_connection_access_check_data data;
+};
+
+struct sbus_connection_destructor {
+ const char *name;
+ sbus_connection_destructor_fn destructor;
+ sbus_connection_destructor_data data;
+};
+
+static void
+sbus_connection_release(struct sbus_connection *conn)
+{
+ /* Now release the connection. */
+ if (conn->type == SBUS_CONNECTION_CLIENT) {
+ /* Client connections must be closed explicitly. */
+ dbus_connection_close(conn->connection);
+ }
+
+ /* All connection must be unreferenced. */
+ dbus_connection_unref(conn->connection);
+}
+
+static int
+sbus_connection_destructor(struct sbus_connection *conn)
+{
+ conn->disconnecting = true;
+
+ /* First, disable reconnection and tevent integration
+ * so we don't handle any more messages. */
+ sbus_reconnect_disable(conn);
+ sbus_connection_tevent_disable(conn);
+
+ /* Remove router data. */
+ talloc_zfree(conn->router);
+
+ /* Release D-Bus connection. */
+ sbus_connection_release(conn);
+
+ if (conn->destructor->destructor != NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Calling custom connection destructor %s\n",
+ conn->destructor->name);
+ conn->destructor->destructor(conn->destructor->data);
+ }
+
+ return 0;
+}
+
+static errno_t
+sbus_connection_data_init(struct sbus_connection *conn)
+{
+ conn->senders = sbus_senders_init(conn);
+ if (conn->senders == NULL) {
+ goto fail;
+ }
+
+ conn->requests = sbus_active_requests_init(conn);
+ if (conn->requests == NULL) {
+ goto fail;
+ }
+
+ conn->reconnect = sbus_reconnect_init(conn);
+ if (conn->reconnect == NULL) {
+ goto fail;
+ }
+
+ conn->router = sbus_router_init(conn, conn);
+ if (conn->router == NULL) {
+ goto fail;
+ }
+
+ conn->access = talloc_zero(conn, struct sbus_connection_access);
+ if (conn->access == NULL) {
+ goto fail;
+ }
+
+ conn->destructor = talloc_zero(conn, struct sbus_connection_destructor);
+ if (conn->destructor == NULL) {
+ goto fail;
+ }
+
+ return EOK;
+
+fail:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+}
+
+struct sbus_connection *
+sbus_connection_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ DBusConnection *dbus_conn,
+ const char *address,
+ const char *dbus_name,
+ enum sbus_connection_type type,
+ time_t *last_activity_time)
+{
+ struct sbus_connection *sbus_conn;
+ errno_t ret;
+
+ /* We do not want to exit if the connection is dropped. */
+ dbus_connection_set_exit_on_disconnect(dbus_conn, FALSE);
+
+ /* Create a new sbus connection */
+ sbus_conn = talloc_zero(mem_ctx, struct sbus_connection);
+ if (sbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return NULL;
+ }
+
+ sbus_conn->ev = ev;
+ sbus_conn->connection = dbus_connection_ref(dbus_conn);
+ sbus_conn->type = type;
+ sbus_conn->last_activity = last_activity_time;
+
+ if (address != NULL) {
+ sbus_conn->address = talloc_strdup(sbus_conn, address);
+ if (sbus_conn->address == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (dbus_name != NULL) {
+ sbus_conn->wellknown_name = talloc_strdup(sbus_conn, dbus_name);
+ if (sbus_conn->wellknown_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sbus_connection_data_init(sbus_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize internal connection "
+ "data [%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sbus_connection_tevent_enable(sbus_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to integrate with tevent [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ talloc_set_destructor(sbus_conn, sbus_connection_destructor);
+
+ /* Mark this connection as active. */
+ sbus_connection_mark_active(sbus_conn);
+
+ /* Dispatch events that happend on the bus before we setup the dispatcher,
+ * if the queue is empty, this will do nothing. */
+ sbus_dispatch_now(sbus_conn);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(sbus_conn);
+ return NULL;
+ }
+
+ return sbus_conn;
+}
+
+errno_t
+sbus_connection_replace(struct sbus_connection *sbus_conn,
+ DBusConnection *dbus_conn)
+{
+ errno_t ret;
+
+ if (dbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: dbus_conn must not be NULL!\n");
+ return ERR_INTERNAL;
+ }
+
+ sbus_connection_release(sbus_conn);
+ sbus_conn->connection = dbus_connection_ref(dbus_conn);
+
+ /* Do not unreference the dbus connection so it is available in
+ * sbus connection for further use, even thought it is not
+ * initialized properly. */
+
+ ret = sbus_router_reset(sbus_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to reset the router [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = sbus_connection_tevent_enable(sbus_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to integrate with tevent [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t sbus_connection_tevent_enable(struct sbus_connection *conn)
+{
+ errno_t ret;
+
+ ret = sbus_watch_connection(conn, conn->ev, conn->connection,
+ &conn->watch);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup D-Bus watch [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ sbus_dispatcher_setup(conn);
+
+ return EOK;
+}
+
+void sbus_connection_tevent_disable(struct sbus_connection *conn)
+{
+ sbus_dispatcher_disable(conn);
+ talloc_zfree(conn->watch);
+}
+
+void sbus_connection_mark_active(struct sbus_connection *conn)
+{
+ if (conn->last_activity != NULL) {
+ *conn->last_activity = time(NULL);
+ }
+}
+
+errno_t sbus_connection_add_path(struct sbus_connection *conn,
+ const char *path,
+ struct sbus_interface *iface)
+{
+ return sbus_router_add_path(conn->router, path, iface);
+}
+
+errno_t sbus_connection_add_path_map(struct sbus_connection *conn,
+ struct sbus_path *map)
+{
+ return sbus_router_add_path_map(conn->router, map);
+}
+
+const char * sbus_connection_get_name(struct sbus_connection *conn)
+{
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ if (conn->wellknown_name != NULL) {
+ return conn->wellknown_name;
+ }
+
+ if (conn->unique_name != NULL) {
+ return conn->unique_name;
+ }
+
+ return ":not.active.yet";
+}
+
+errno_t sbus_connection_set_name(struct sbus_connection *conn,
+ const char *name)
+{
+ const char *dup;
+
+ dup = talloc_strdup(conn, name);
+ if (dup == NULL && name == NULL) {
+ return ENOMEM;
+ }
+
+ talloc_zfree(conn->wellknown_name);
+ conn->wellknown_name = dup;
+
+ return EOK;
+}
+
+void _sbus_connection_set_destructor(struct sbus_connection *conn,
+ const char *name,
+ sbus_connection_destructor_fn destructor,
+ sbus_connection_destructor_data data)
+{
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: connection is NULL\n");
+ return;
+ }
+
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name is NULL\n");
+ return;
+ }
+
+ if (destructor == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Unsetting connection destructor\n");
+ conn->destructor->destructor = NULL;
+ conn->destructor->data = NULL;
+ conn->destructor->name = NULL;
+ return;
+ }
+
+ if (conn->destructor->destructor != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: destructor is already set to %s\n",
+ conn->destructor->name);
+ return;
+ }
+
+ conn->destructor->destructor = destructor;
+ conn->destructor->data = data;
+ conn->destructor->name = name;
+}
+
+void _sbus_connection_set_access_check(struct sbus_connection *conn,
+ const char *name,
+ sbus_connection_access_check_fn check_fn,
+ sbus_connection_access_check_data data)
+{
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: connection is NULL\n");
+ return;
+ }
+
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name is NULL\n");
+ return;
+ }
+
+ if (check_fn == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Unsetting access check function\n");
+ conn->access->check_fn = NULL;
+ conn->access->data = NULL;
+ conn->access->name = NULL;
+ return;
+ }
+
+ if (conn->access->check_fn != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: access check function is "
+ "already set to %s\n", conn->access->name);
+ return;
+ }
+
+ conn->access->check_fn = check_fn;
+ conn->access->data = data;
+ conn->access->name = name;
+}
+
+void sbus_connection_set_data(struct sbus_connection *conn,
+ void *data)
+{
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: connection is NULL\n");
+ return;
+ }
+
+ conn->data = data;
+}
+
+void *_sbus_connection_get_data(struct sbus_connection *conn)
+{
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ return conn->data;
+}
+
+errno_t
+sbus_check_access(struct sbus_connection *conn,
+ struct sbus_request *sbus_req)
+{
+ errno_t ret;
+
+ if (conn->access->check_fn == NULL) {
+ return EOK;
+ }
+
+ ret = conn->access->check_fn(sbus_req, conn->access->data);
+ if (ret == EPERM || ret == EACCES) {
+ if (sbus_req->sender == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "%s.%s: permission denied for unknown sender\n",
+ sbus_req->interface, sbus_req->member);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "%s.%s: permission denied for sender %s with uid %"PRIi64"\n",
+ sbus_req->interface, sbus_req->member,
+ sbus_req->sender->name, sbus_req->sender->uid);
+ }
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform access check [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+static void
+sbus_connection_free_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *data)
+{
+ talloc_free(data);
+}
+
+void sbus_connection_free(struct sbus_connection *conn)
+{
+ struct tevent_timer *te;
+ struct timeval tv;
+
+ conn->disconnecting = true;
+ sbus_reconnect_disable(conn);
+ sbus_connection_tevent_disable(conn);
+
+ tv = tevent_timeval_current();
+ te = tevent_add_timer(conn->ev, conn, tv, sbus_connection_free_handler,
+ conn);
+ if (te == NULL) {
+ /* We can't do anything about it. */
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to set up free event!\n");
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Connection %p will be freed during next loop!\n",
+ conn);
+}
diff --git a/src/sbus/connection/sbus_connection_connect.c b/src/sbus/connection/sbus_connection_connect.c
new file mode 100644
index 0000000..edc090e
--- /dev/null
+++ b/src/sbus/connection/sbus_connection_connect.c
@@ -0,0 +1,430 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/connection/sbus_dbus_private.h"
+#include "sbus/sbus_private.h"
+#include "sbus/interface_dbus/sbus_dbus_client_async.h"
+
+struct sbus_connect_init_state {
+ struct sbus_connection *conn;
+ const char *name;
+};
+
+static void sbus_connect_init_hello_done(struct tevent_req *subreq);
+static void sbus_connect_init_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_connect_init_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *name)
+{
+ struct sbus_connect_init_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_connect_init_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->conn = conn;
+ state->name = name;
+
+ subreq = sbus_call_DBus_Hello_send(state, conn, DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ tevent_req_error(req, ENOMEM);
+ tevent_req_post(req, conn->ev);
+ return req;
+ }
+
+ tevent_req_set_callback(subreq, sbus_connect_init_hello_done, req);
+
+ arm_watchdog();
+
+ return req;
+}
+
+static void sbus_connect_init_hello_done(struct tevent_req *subreq)
+{
+ struct sbus_connect_init_state *state;
+ const char *unique_name;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_connect_init_state);
+
+ ret = sbus_call_DBus_Hello_recv(state, subreq, &unique_name);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->name == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = sbus_call_DBus_RequestName_send(state, state->conn,
+ DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, state->name,
+ DBUS_NAME_FLAG_DO_NOT_QUEUE);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_set_callback(subreq, sbus_connect_init_done, req);
+ return;
+}
+
+static void sbus_connect_init_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ uint32_t res;
+ errno_t ret;
+
+ disarm_watchdog();
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ ret = sbus_call_DBus_RequestName_recv(subreq, &res);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ switch (res) {
+ case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+ case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+ tevent_req_done(req);
+ return;
+ case DBUS_REQUEST_NAME_REPLY_EXISTS:
+ tevent_req_error(req, EEXIST);
+ return;
+ default:
+ tevent_req_error(req, EIO);
+ return;
+ }
+}
+
+errno_t sbus_connect_init_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+struct sbus_connection *
+sbus_connect_system(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *dbus_name,
+ time_t *last_activity_time)
+{
+ struct sbus_connection *sbus_conn;
+ DBusConnection *dbus_conn;
+ errno_t ret;
+
+ dbus_conn = sbus_dbus_connect_bus(DBUS_BUS_SYSTEM, dbus_name);
+ if (dbus_conn == NULL) {
+ return NULL;
+ }
+
+ sbus_conn = sbus_connection_init(mem_ctx, ev, dbus_conn, NULL, dbus_name,
+ SBUS_CONNECTION_SYSBUS,
+ last_activity_time);
+ dbus_connection_unref(dbus_conn);
+ if (sbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create connection context!\n");
+ return NULL;
+ }
+
+ ret = sbus_register_standard_signals(sbus_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register signal listeners "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ talloc_free(sbus_conn);
+ return NULL;
+ }
+
+ return sbus_conn;
+}
+
+struct sbus_connection *
+sbus_connect_private(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *address,
+ const char *dbus_name,
+ time_t *last_activity_time)
+{
+ struct sbus_connection *sbus_conn;
+ DBusConnection *dbus_conn;
+ errno_t ret;
+
+ dbus_conn = sbus_dbus_connect_address(address, dbus_name, true);
+ if (dbus_conn == NULL) {
+ return NULL;
+ }
+
+ sbus_conn = sbus_connection_init(mem_ctx, ev, dbus_conn, address, dbus_name,
+ SBUS_CONNECTION_ADDRESS,
+ last_activity_time);
+ dbus_connection_unref(dbus_conn);
+ if (sbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create connection context!\n");
+ return NULL;
+ }
+
+ ret = sbus_register_standard_signals(sbus_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register signal listeners "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ talloc_free(sbus_conn);
+ return NULL;
+ }
+
+ return sbus_conn;
+}
+
+struct sbus_connect_private_state {
+ struct sbus_connection *conn;
+};
+
+static void sbus_connect_private_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_connect_private_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *address,
+ const char *dbus_name,
+ time_t *last_activity_time)
+{
+ struct sbus_connect_private_state *state;
+ DBusConnection *dbus_conn;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct sbus_connect_private_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ dbus_conn = sbus_dbus_connect_address(address, dbus_name, false);
+ if (dbus_conn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->conn = sbus_connection_init(state, ev, dbus_conn, address,
+ dbus_name, SBUS_CONNECTION_ADDRESS,
+ last_activity_time);
+ dbus_connection_unref(dbus_conn);
+ if (state->conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create connection context!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = sbus_connect_init_send(state, state->conn, dbus_name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_connect_private_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_connect_private_done(struct tevent_req *subreq)
+{
+ struct sbus_connect_private_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_connect_private_state);
+
+ ret = sbus_connect_init_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to initialize connection "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ talloc_free(state->conn);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sbus_register_standard_signals(state->conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register signal listeners "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ talloc_zfree(state->conn);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (state->conn->wellknown_name == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Connected to %s bus as anonymous\n",
+ state->conn->address);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "Connected to %s bus as %s\n",
+ state->conn->address, state->conn->wellknown_name);
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t sbus_connect_private_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sbus_connection **_conn)
+{
+ struct sbus_connect_private_state *state;
+ state = tevent_req_data(req, struct sbus_connect_private_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_conn = talloc_steal(mem_ctx, state->conn);
+
+ return EOK;
+}
+
+struct sbus_server_create_and_connect_state {
+ struct sbus_server *server;
+ struct sbus_connection *conn;
+};
+
+static void sbus_server_create_and_connect_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_server_create_and_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *dbus_name,
+ time_t *last_activity_time,
+ const char *address,
+ bool use_symlink,
+ uint32_t max_connections,
+ uid_t uid,
+ gid_t gid,
+ sbus_server_on_connection_cb on_conn_cb,
+ sbus_server_on_connection_data on_conn_data)
+{
+ struct sbus_server_create_and_connect_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_server_create_and_connect_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->server = sbus_server_create(state, ev, address, use_symlink,
+ max_connections, uid, gid,
+ on_conn_cb, on_conn_data);
+ if (state->server == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = sbus_connect_private_send(state, ev, address, dbus_name,
+ last_activity_time);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_server_create_and_connect_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_server_create_and_connect_done(struct tevent_req *subreq)
+{
+ struct sbus_server_create_and_connect_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_server_create_and_connect_state);
+
+ ret = sbus_connect_private_recv(state, subreq, &state->conn);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+sbus_server_create_and_connect_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sbus_server **_server,
+ struct sbus_connection **_conn)
+{
+ struct sbus_server_create_and_connect_state *state;
+ state = tevent_req_data(req, struct sbus_server_create_and_connect_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_server = talloc_steal(mem_ctx, state->server);
+ *_conn = talloc_steal(mem_ctx, state->conn);
+
+ return EOK;
+}
diff --git a/src/sbus/connection/sbus_dbus.c b/src/sbus/connection/sbus_dbus.c
new file mode 100644
index 0000000..69b18bc
--- /dev/null
+++ b/src/sbus/connection/sbus_dbus.c
@@ -0,0 +1,188 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/connection/sbus_dbus_private.h"
+
+static errno_t
+sbus_dbus_request_name(DBusConnection *dbus_conn, const char *name)
+{
+ DBusError dbus_error;
+ errno_t ret;
+ int flags;
+ int dbret;
+
+ dbus_error_init(&dbus_error);
+
+ /* We are interested only in being the primary owner of this name. */
+ flags = DBUS_NAME_FLAG_DO_NOT_QUEUE;
+
+ dbret = dbus_bus_request_name(dbus_conn, name, flags, &dbus_error);
+ if (dbret == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to request name '%s' on the system"
+ " bus [%s]: %s\n", name, dbus_error.name, dbus_error.message);
+ if (dbus_error_has_name(&dbus_error, DBUS_ERROR_ACCESS_DENIED)) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Access denied - check dbus service configuration.\n");
+ sss_log(SSS_LOG_CRIT, "SSSD dbus service can't acquire bus name"
+ " - check dbus service configuration.");
+ }
+ ret = EIO;
+ goto done;
+ } else if (dbret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to request name on the "
+ "system bus [%d]\n", dbret);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ dbus_error_free(&dbus_error);
+
+ return ret;
+}
+
+DBusConnection *
+sbus_dbus_connect_bus(DBusBusType bus, const char *name)
+{
+ DBusConnection *dbus_conn;
+ DBusError dbus_error;
+ const char *busname = "not-set";
+ errno_t ret;
+
+ switch (bus) {
+ case DBUS_BUS_SESSION:
+ busname = "session";
+ break;
+ case DBUS_BUS_SYSTEM:
+ busname = "system";
+ break;
+ case DBUS_BUS_STARTER:
+ busname = "starter";
+ break;
+ }
+
+ dbus_error_init(&dbus_error);
+
+ /* Connect to the system bus. */
+ dbus_conn = dbus_bus_get(bus, &dbus_error);
+ if (dbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to connect to %s bus [%s]: %s\n",
+ busname, dbus_error.name, dbus_error.message);
+ ret = EIO;
+ goto done;
+ }
+
+ if (name != NULL) {
+ /* Request a well-known name. */
+ ret = sbus_dbus_request_name(dbus_conn, name);
+ if (ret != EOK) {
+ dbus_connection_unref(dbus_conn);
+ goto done;
+ }
+ }
+
+ if (name == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Connected to %s bus as anonymous\n", busname);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "Connected to %s bus as %s\n", busname, name);
+ }
+
+ ret = EOK;
+
+done:
+ dbus_error_free(&dbus_error);
+
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return dbus_conn;
+}
+
+DBusConnection *
+sbus_dbus_connect_address(const char *address, const char *name, bool init)
+{
+ DBusConnection *dbus_conn;
+ DBusError dbus_error;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ if (address == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Can not connect to an empty address!\n");
+ return NULL;
+ }
+
+ dbus_error_init(&dbus_error);
+
+ dbus_conn = dbus_connection_open(address, &dbus_error);
+ if (dbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to connect to %s [%s]: %s\n",
+ address, dbus_error.name, dbus_error.message);
+ ret = EIO;
+ goto done;
+ }
+
+ if (!init) {
+ ret = EOK;
+ goto done;
+ }
+
+ dbret = dbus_bus_register(dbus_conn, &dbus_error);
+ if (!dbret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register to %s [%s]: %s\n",
+ address, dbus_error.name, dbus_error.message);
+ ret = EIO;
+ goto done;
+ }
+
+ /* Request a well-known name. */
+ if (name != NULL) {
+ ret = sbus_dbus_request_name(dbus_conn, name);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (name == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Connected to %s bus as anonymous\n", address);
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "Connected to %s bus as %s\n", address, name);
+ }
+
+ ret = EOK;
+
+done:
+ dbus_error_free(&dbus_error);
+ if (ret != EOK && dbus_conn != NULL) {
+ dbus_connection_unref(dbus_conn);
+ dbus_conn = NULL;
+ }
+
+ return dbus_conn;
+}
diff --git a/src/sbus/connection/sbus_dbus_private.h b/src/sbus/connection/sbus_dbus_private.h
new file mode 100644
index 0000000..2e45494
--- /dev/null
+++ b/src/sbus/connection/sbus_dbus_private.h
@@ -0,0 +1,34 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2018 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 _SBUS_DBUS_PRIVATE_H_
+#define _SBUS_DBUS_PRIVATE_H_
+
+#include <dbus/dbus.h>
+
+/* Get D-Bus connection to a D-Bus system or session bus. */
+DBusConnection *sbus_dbus_connect_bus(DBusBusType bus, const char *name);
+
+/* Get D-Bus connection to a D-Bus address. */
+DBusConnection *sbus_dbus_connect_address(const char *address,
+ const char *name,
+ bool init);
+
+#endif /* _SBUS_DBUS_PRIVATE_H_ */
diff --git a/src/sbus/connection/sbus_dispatcher.c b/src/sbus/connection/sbus_dispatcher.c
new file mode 100644
index 0000000..3631c17
--- /dev/null
+++ b/src/sbus/connection/sbus_dispatcher.c
@@ -0,0 +1,156 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "sbus/sbus_private.h"
+
+static void
+sbus_dispatch_schedule(struct sbus_connection *conn, uint32_t usecs);
+
+static void
+sbus_dispatch_reconnect(struct sbus_connection *conn)
+{
+ /* Terminate all outgoing requests associated with this connection. */
+ DEBUG(SSSDBG_TRACE_FUNC, "Connection lost. Terminating active requests.\n");
+ sbus_requests_terminate_all(conn->requests->outgoing, ERR_TERMINATED);
+
+ switch (conn->type) {
+ case SBUS_CONNECTION_CLIENT:
+ /* Remote client closed the connection. We can't reestablish
+ * connection with a client, it must reconnect to us if it
+ * needs to. Therefore we are done here. */
+ DEBUG(SSSDBG_TRACE_ALL, "Remote client terminated the connection. "
+ "Releasing data...\n");
+ sbus_connection_free(conn);
+ break;
+ case SBUS_CONNECTION_ADDRESS:
+ case SBUS_CONNECTION_SYSBUS:
+ /* Try to reconnect if it was enabled. */
+ if (sbus_reconnect_enabled(conn)) {
+ sbus_reconnect(conn);
+ return;
+ }
+
+ /* We were unable to reconnect. There is nothing we can do. */
+ DEBUG(SSSDBG_MINOR_FAILURE, "Connection is not open for "
+ "dispatching. Releasing data...\n");
+ sbus_connection_free(conn);
+ break;
+ }
+}
+
+static void
+sbus_dispatch(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *data)
+{
+ DBusDispatchStatus status;
+ struct sbus_connection *conn;
+ bool connected;
+
+ conn = talloc_get_type(data, struct sbus_connection);
+
+ /* Just return if the connection is being terminated. */
+ if (conn->disconnecting) {
+ return;
+ }
+
+ /* Defer dispatch if we reconnecting. */
+ if (sbus_reconnect_in_progress(conn)) {
+ DEBUG(SSSDBG_TRACE_FUNC, "SBUS is reconnecting. Deferring.\n");
+ sbus_dispatch_schedule(conn, 30);
+ return;
+ }
+
+ /* Try to reconnect if we are not connected. */
+ connected = dbus_connection_get_is_connected(conn->connection);
+ if (!connected) {
+ sbus_dispatch_reconnect(conn);
+ return;
+ }
+
+ /* Dispatch only once to avoid starving other tevent requests. */
+ status = dbus_connection_get_dispatch_status(conn->connection);
+ if (status != DBUS_DISPATCH_COMPLETE) {
+ DEBUG(SSSDBG_TRACE_ALL, "Dispatching.\n");
+ dbus_connection_dispatch(conn->connection);
+ }
+
+ /* If other dispatches are waiting, schedule next dispatch. */
+ status = dbus_connection_get_dispatch_status(conn->connection);
+ if (status != DBUS_DISPATCH_COMPLETE) {
+ sbus_dispatch_schedule(conn, 0);
+ }
+}
+
+static void
+sbus_dispatch_schedule(struct sbus_connection *conn, uint32_t usecs)
+{
+ struct tevent_timer *te;
+ struct timeval tv;
+
+ tv = tevent_timeval_current_ofs(0, usecs);
+ te = tevent_add_timer(conn->ev, conn, tv, sbus_dispatch, conn);
+ 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 dispatch event!\n");
+ }
+}
+
+/**
+ * This is called each time when D-Bus has data to dispatch available.
+ * We create a timed event to avoid tevent request starving.
+ */
+static void
+sbus_dispatch_wakeup(void *data)
+{
+ struct sbus_connection *conn;
+
+ conn = talloc_get_type(data, struct sbus_connection);
+ sbus_dispatch_schedule(conn, 0);
+}
+
+void sbus_dispatcher_setup(struct sbus_connection *conn)
+{
+ dbus_connection_set_wakeup_main_function(conn->connection,
+ sbus_dispatch_wakeup,
+ conn, NULL);
+}
+
+void sbus_dispatcher_disable(struct sbus_connection *conn)
+{
+ dbus_connection_set_wakeup_main_function(conn->connection,
+ NULL, NULL, NULL);
+}
+
+void sbus_dispatch_now(struct sbus_connection *conn)
+{
+ sbus_dispatch_wakeup(conn);
+}
diff --git a/src/sbus/connection/sbus_reconnect.c b/src/sbus/connection/sbus_reconnect.c
new file mode 100644
index 0000000..a0055e5
--- /dev/null
+++ b/src/sbus/connection/sbus_reconnect.c
@@ -0,0 +1,249 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_private.h"
+#include "sbus/connection/sbus_dbus_private.h"
+
+struct sbus_reconnect {
+ bool enabled;
+
+ struct {
+ unsigned int current;
+ unsigned int max;
+ } retry;
+
+ sbus_reconnect_cb callback;
+ sbus_reconnect_data data;
+};
+
+static void
+sbus_reconnect_notify(struct sbus_connection *conn,
+ enum sbus_reconnect_status status)
+{
+ if (conn->reconnect->callback == NULL) {
+ return;
+ }
+
+ conn->reconnect->callback(conn, status, conn->reconnect->data);
+}
+
+static void
+sbus_reconnect_success(struct sbus_connection *conn)
+{
+ conn->reconnect->retry.current = 0;
+ DEBUG(SSSDBG_MINOR_FAILURE, "Reconnection successful.\n");
+ sbus_reconnect_notify(conn, SBUS_RECONNECT_SUCCESS);
+}
+
+static void
+sbus_reconnect_attempt(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *data)
+{
+ struct sbus_connection *sbus_conn;
+ DBusConnection *dbus_conn = NULL;
+ errno_t ret;
+
+ sbus_conn = talloc_get_type(data, struct sbus_connection);
+
+ /* Do not try to reconnect if the connection is being disconnected. */
+ if (sbus_conn->disconnecting) {
+ return;
+ }
+
+ /* Obtain new connection. */
+ switch (sbus_conn->type) {
+ case SBUS_CONNECTION_CLIENT:
+ /* We can't really reconnect to a client. There is nothing to do. */
+ DEBUG(SSSDBG_OP_FAILURE, "We can't reconnect to the client!\n");
+ return;
+ case SBUS_CONNECTION_ADDRESS:
+ DEBUG(SSSDBG_MINOR_FAILURE, "Making reconnection attempt %d to [%s]\n",
+ sbus_conn->reconnect->retry.current, sbus_conn->address);
+ /* It is necessary to use blocking Hello and RequestName method
+ * so those two are the only methods that are sent to the new
+ * dbus connection before it is properly initialized.
+ */
+ dbus_conn = sbus_dbus_connect_address(sbus_conn->address,
+ sbus_conn->wellknown_name,
+ true);
+ break;
+ case SBUS_CONNECTION_SYSBUS:
+ DEBUG(SSSDBG_MINOR_FAILURE, "Making reconnection attempt %d "
+ "to system bus\n", sbus_conn->reconnect->retry.current);
+ dbus_conn = sbus_dbus_connect_bus(DBUS_BUS_SYSTEM,
+ sbus_conn->wellknown_name);
+ break;
+ }
+
+ if (dbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to connect to D-Bus\n");
+ ret = EIO;
+ goto done;
+ }
+
+ /* Replace old connection with newly created. */
+ ret = sbus_connection_replace(sbus_conn, dbus_conn);
+ dbus_connection_unref(dbus_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to replace D-Bus connection\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ /* Issue next attempt or finish. */
+ if (ret != EOK) {
+ sbus_reconnect(sbus_conn);
+ return;
+ }
+
+ sbus_reconnect_success(sbus_conn);
+}
+
+static struct timeval
+sbus_reconnect_delay(struct sbus_reconnect *reconnect)
+{
+ unsigned int delay;
+
+ /* Calculate how many seconds should we wait
+ * before new reconnection attempt. */
+ switch (reconnect->retry.current) {
+ case 1:
+ delay = 1;
+ break;
+ case 2:
+ delay = 3;
+ break;
+ case 3:
+ delay = 10;
+ break;
+ default:
+ delay = 30;
+ break;
+ }
+
+ return tevent_timeval_current_ofs(delay, 0);
+}
+
+void sbus_reconnect(struct sbus_connection *conn)
+{
+ struct sbus_reconnect *reconnect = conn->reconnect;
+ struct tevent_timer *te;
+ struct timeval tv;
+
+ /* Do not try to reconnect if the connection is being disconnected. */
+ if (conn->disconnecting) {
+ return;
+ }
+
+ if (dbus_connection_get_is_connected(conn->connection)) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Already connected!\n");
+ return;
+ }
+
+ if (!sbus_reconnect_enabled(conn)) {
+ DEBUG(SSSDBG_TRACE_FUNC, "We are not allowed to reconnect!\n");
+ return;
+ }
+
+ /* Remove tevent integration since the connection is dropped, we have
+ * nothing to listen to. */
+ sbus_connection_tevent_disable(conn);
+
+ /* Increase retry counter and check if we are still allowed to reconnect. */
+ reconnect->retry.current++;
+
+ if (reconnect->retry.current > reconnect->retry.max) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to reconnect: maximum retries exceeded.\n");
+ sbus_reconnect_notify(conn, SBUS_RECONNECT_EXCEEDED_RETRIES);
+ return;
+ }
+
+ tv = sbus_reconnect_delay(reconnect);
+ te = tevent_add_timer(conn->ev, conn, tv, sbus_reconnect_attempt, conn);
+ if (te == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to reconnect: cannot create timed event.\n");
+ sbus_reconnect_notify(conn, SBUS_RECONNECT_ERROR);
+ return;
+ }
+
+ return;
+}
+
+struct sbus_reconnect *
+sbus_reconnect_init(TALLOC_CTX *mem_ctx)
+{
+ return talloc_zero(mem_ctx, struct sbus_reconnect);
+}
+
+void _sbus_reconnect_enable(struct sbus_connection *conn,
+ unsigned int max_retries,
+ sbus_reconnect_cb callback,
+ sbus_reconnect_data callback_data)
+{
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: connection is NULL\n");
+ return;
+ }
+
+ if (sbus_reconnect_enabled(conn)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: reconnection is already enabled\n");
+ return;
+ }
+
+ conn->reconnect->enabled = true;
+ conn->reconnect->callback = callback;
+ conn->reconnect->data = callback_data;
+ conn->reconnect->retry.max = max_retries;
+ conn->reconnect->retry.current = 0;
+}
+
+void sbus_reconnect_disable(struct sbus_connection *conn)
+{
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: connection is NULL\n");
+ return;
+ }
+
+ conn->reconnect->enabled = false;
+}
+
+bool sbus_reconnect_in_progress(struct sbus_connection *conn)
+{
+ return conn->reconnect->retry.current != 0;
+}
+
+bool sbus_reconnect_enabled(struct sbus_connection *conn)
+{
+ return conn->reconnect->enabled;
+}
diff --git a/src/sbus/connection/sbus_send.c b/src/sbus/connection/sbus_send.c
new file mode 100644
index 0000000..407ea0f
--- /dev/null
+++ b/src/sbus/connection/sbus_send.c
@@ -0,0 +1,245 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_private.h"
+
+static errno_t
+sbus_pending_call_create(DBusConnection *conn,
+ DBusMessage *msg,
+ DBusPendingCallNotifyFunction notify_fn,
+ void *notify_data,
+ int timeout_ms,
+ DBusPendingCall **_pending_call)
+{
+ DBusPendingCall *pending;
+ dbus_bool_t dbret;
+
+ dbret = dbus_connection_send_with_reply(conn, msg, &pending, timeout_ms);
+ if (!dbret) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to create pending call!\n");
+ return ENOMEM;
+ }
+
+ if (pending == NULL) {
+ /* Connection may have been disconnected. */
+ return ERR_OFFLINE;
+ }
+
+ dbret = dbus_pending_call_set_notify(pending, notify_fn, notify_data, NULL);
+ if (!dbret) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unable to setup callback for pending reply!\n");
+ dbus_pending_call_cancel(pending);
+ dbus_pending_call_unref(pending);
+ return ENOMEM;
+ }
+
+ *_pending_call = pending;
+
+ return EOK;
+}
+
+static errno_t
+sbus_pending_call_process(DBusPendingCall *pending,
+ DBusMessage **_reply)
+{
+ DBusMessage *reply;
+ DBusError error;
+ errno_t ret;
+
+ dbus_error_init(&error);
+
+ reply = dbus_pending_call_steal_reply(pending);
+ if (reply == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Severe error. A reply callback was "
+ "called but no reply was received and no timeout occurred\n");
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = sbus_reply_check(reply);
+ if (ret != EOK) {
+ dbus_message_unref(reply);
+ goto done;
+ }
+
+ *_reply = reply;
+
+done:
+ dbus_pending_call_unref(pending);
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+struct sbus_message_state {
+ DBusPendingCall *pending;
+ DBusMessage *reply;
+};
+
+static int sbus_message_destructor(struct sbus_message_state *state)
+{
+ if (state->pending == NULL) {
+ return 0;
+ }
+
+ /* This request was interrupted before the pending call has finished. */
+ dbus_pending_call_cancel(state->pending);
+ dbus_pending_call_unref(state->pending);
+ state->pending = NULL;
+
+ return 0;
+}
+
+static void sbus_message_done(DBusPendingCall *pending,
+ void *pvt);
+
+struct tevent_req *
+sbus_message_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ DBusMessage *msg,
+ int timeout_ms)
+{
+ struct sbus_message_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_message_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ if (conn->disconnecting) {
+ ret = ERR_TERMINATED;
+ goto done;
+ }
+
+ ret = sbus_pending_call_create(conn->connection, msg, sbus_message_done,
+ req, timeout_ms, &state->pending);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ talloc_set_destructor(state, sbus_message_destructor);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, conn->ev);
+ }
+
+ return req;
+}
+
+static void
+sbus_message_done(DBusPendingCall *pending,
+ void *pvt)
+{
+ struct sbus_message_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(pvt, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_message_state);
+
+ /* The pending call has finished. Remove the destructor. */
+ state->pending = NULL;
+ talloc_set_destructor(state, NULL);
+
+ ret = sbus_pending_call_process(pending, &state->reply);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t
+sbus_message_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ struct sbus_message_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sbus_message_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ ret = sbus_message_bound(mem_ctx, state->reply);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_reply = state->reply;
+
+ return EOK;
+}
+
+void sbus_reply(struct sbus_connection *conn,
+ DBusMessage *reply)
+{
+ if (conn->disconnecting) {
+ dbus_message_unref(reply);
+ return;
+ }
+
+ dbus_connection_send(conn->connection, reply, NULL);
+ dbus_message_unref(reply);
+}
+
+void sbus_reply_error(struct sbus_connection *conn,
+ DBusMessage *reply_to,
+ const char *error_name,
+ const char *error_message)
+{
+ DBusMessage *reply;
+
+ /* We can|t really send reply to a signal. */
+ if (dbus_message_get_type(reply_to) == DBUS_MESSAGE_TYPE_SIGNAL) {
+ return;
+ }
+
+ reply = dbus_message_new_error(reply_to, error_name, error_message);
+ if (reply == NULL) {
+ /* There is really nothing we can do. */
+ return;
+ }
+
+ sbus_reply(conn, reply);
+}
+
+void sbus_emit_signal(struct sbus_connection *conn,
+ DBusMessage *msg)
+{
+ sbus_reply(conn, msg);
+}
diff --git a/src/sbus/connection/sbus_watch.c b/src/sbus/connection/sbus_watch.c
new file mode 100644
index 0000000..d1b55e9
--- /dev/null
+++ b/src/sbus/connection/sbus_watch.c
@@ -0,0 +1,629 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "sbus/sbus_private.h"
+
+#ifdef HAVE_DBUS_WATCH_GET_UNIX_FD
+#define sbus_watch_get_fd(dbus_watch) dbus_watch_get_unix_fd(dbus_watch)
+#else
+#define sbus_watch_get_fd(dbus_watch) dbus_watch_get_fd(dbus_watch)
+#endif
+
+/**
+ * These types definition are here so we can manipulate both D-Bus
+ * connection and server with one API.
+ */
+
+typedef dbus_bool_t
+(*sbus_dbus_set_watch_fn)(void *conn_or_server,
+ DBusAddWatchFunction,
+ DBusRemoveWatchFunction,
+ DBusWatchToggledFunction,
+ void *data,
+ DBusFreeFunction);
+
+typedef dbus_bool_t
+(*sbus_dbus_set_timeout_fn)(void *conn_or_server,
+ DBusAddTimeoutFunction,
+ DBusRemoveTimeoutFunction,
+ DBusTimeoutToggledFunction,
+ void *data,
+ DBusFreeFunction);
+
+typedef void *
+(*sbus_dbus_ref_fn)(void *conn_or_server);
+
+typedef void
+(*sbus_dbus_unref_fn)(void *conn_or_server);
+
+/**
+ * D-Bus watch is a mechanism to notify D-Bus every time a read or write event
+ * occurs on D-Bus connection file descriptor.
+ *
+ * D-Bus provides add, remove and toggle function to create/remove a file
+ * descriptor event listener and to switch between enabled and disabled
+ * states when a file descriptor is kept opened for a longer period of time
+ * to safe allocations.
+ *
+ * We incorporate this watch mechanism into a tevent file handler.
+ */
+
+enum sbus_watch_type {
+ SBUS_WATCH_CONNECTION,
+ SBUS_WATCH_SERVER
+};
+
+struct sbus_watch_control {
+ void *dbus_ctx;
+ sbus_dbus_set_watch_fn set_watch;
+ sbus_dbus_set_timeout_fn set_timeout;
+ sbus_dbus_ref_fn ref;
+ sbus_dbus_unref_fn unref;
+};
+
+struct sbus_watch {
+ struct tevent_context *ev;
+ enum sbus_watch_type type;
+ struct sbus_watch_control control;
+ struct sbus_watch_fd *watch_list;
+};
+
+struct sbus_watch_fd {
+ struct sbus_watch *sbus_watch;
+
+ struct {
+ DBusWatch *read;
+ DBusWatch *write;
+ } dbus_watch;
+
+ int fd;
+ struct tevent_fd *fdevent;
+ struct tevent_immediate *im_event;
+
+ struct sbus_watch_fd *prev;
+ struct sbus_watch_fd *next;
+};
+
+static void
+sbus_watch_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *data)
+{
+ struct sbus_watch_control control;
+ struct sbus_watch_fd *watch_fd;
+
+ watch_fd = talloc_get_type(data, struct sbus_watch_fd);
+
+ /**
+ * Watch context may get freed if it's associated memory context
+ * (connection or server) is freed inside a handle. We need to remember
+ * it and increase reference to the connection or server so we can safely
+ * issue both read and write handlers.
+ */
+ control = watch_fd->sbus_watch->control;
+
+ control.ref(control.dbus_ctx);
+
+ /* Fire if readable */
+ if (flags & TEVENT_FD_READ && watch_fd->dbus_watch.read != NULL) {
+ dbus_watch_handle(watch_fd->dbus_watch.read, DBUS_WATCH_READABLE);
+ }
+
+ /* Fire if writable */
+ if (flags & TEVENT_FD_WRITE && watch_fd->dbus_watch.write != NULL) {
+ dbus_watch_handle(watch_fd->dbus_watch.write, DBUS_WATCH_WRITABLE);
+ }
+
+ control.unref(control.dbus_ctx);
+}
+
+static int
+sbus_watch_fd_destructor(struct sbus_watch_fd *watch_fd)
+{
+ if (watch_fd->sbus_watch == NULL) {
+ return 0;
+ }
+
+ DLIST_REMOVE(watch_fd->sbus_watch->watch_list, watch_fd);
+
+ return 0;
+}
+
+static struct sbus_watch_fd *
+sbus_watch_get_by_fd(TALLOC_CTX *mem_ctx,
+ struct sbus_watch *watch,
+ int fd)
+{
+ struct sbus_watch_fd *watch_fd;
+
+ /**
+ * D-Bus may ask us to add a watch to a file descriptor that already had
+ * a watch associated. If this is the case we return the existing context.
+ */
+ DLIST_FOR_EACH(watch_fd, watch->watch_list) {
+ if (watch_fd->fd == fd) {
+ return watch_fd;
+ }
+ }
+
+ /* Create new one otherwise. */
+ watch_fd = talloc_zero(mem_ctx, struct sbus_watch_fd);
+ if (watch_fd == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return NULL;
+ }
+
+ watch_fd->im_event = tevent_create_immediate(watch_fd);
+ if (watch_fd->im_event == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of Memory!\n");
+ talloc_free(watch_fd);
+ return NULL;
+ }
+
+ talloc_set_destructor(watch_fd, sbus_watch_fd_destructor);
+
+ watch_fd->sbus_watch = watch;
+ watch_fd->fd = fd;
+
+ return watch_fd;
+}
+
+static void
+sbus_watch_toggle(DBusWatch *dbus_watch, void *data);
+
+static dbus_bool_t
+sbus_watch_add(DBusWatch *dbus_watch, void *data)
+{
+ struct sbus_watch *sbus_watch;
+ struct sbus_watch_fd *watch_fd;
+ unsigned int watch_flags;
+ dbus_bool_t is_enabled;
+ uint16_t ev_flags;
+ int fd;
+
+ sbus_watch = talloc_get_type(data, struct sbus_watch);
+ fd = sbus_watch_get_fd(dbus_watch);
+
+ watch_fd = sbus_watch_get_by_fd(sbus_watch, sbus_watch, fd);
+ if (watch_fd == NULL) {
+ return FALSE;
+ }
+
+ is_enabled = dbus_watch_get_enabled(dbus_watch);
+ watch_flags = dbus_watch_get_flags(dbus_watch);
+ ev_flags = 0;
+
+ /* Remember the D-Bus watch and its context. */
+ if (watch_flags & DBUS_WATCH_READABLE) {
+ watch_fd->dbus_watch.read = dbus_watch;
+ if (is_enabled) {
+ ev_flags |= TEVENT_FD_READ;
+ }
+ }
+
+ if (watch_flags & DBUS_WATCH_WRITABLE) {
+ watch_fd->dbus_watch.write = dbus_watch;
+ if (is_enabled) {
+ ev_flags |= TEVENT_FD_WRITE;
+ }
+ }
+
+ dbus_watch_set_data(dbus_watch, watch_fd, NULL);
+
+ /* Just update flags if an event handler already exists. */
+ if (watch_fd->fdevent) {
+ sbus_watch_toggle(dbus_watch, data);
+ return TRUE;
+ }
+
+ /* Create new one otherwise. */
+ watch_fd->fdevent = tevent_add_fd(sbus_watch->ev, watch_fd, fd, ev_flags,
+ sbus_watch_handler, watch_fd);
+ if (watch_fd->fdevent == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set up fd event!\n");
+ talloc_zfree(watch_fd);
+ return FALSE;
+ }
+
+ DLIST_ADD(sbus_watch->watch_list, watch_fd);
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Created a %s %s/%s watch on %d\n",
+ is_enabled ? "enabled" : "disabled",
+ (watch_flags & DBUS_WATCH_READABLE) ? "R" : "-",
+ (watch_flags & DBUS_WATCH_WRITABLE) ? "W" : "-",
+ fd);
+
+ return TRUE;
+}
+
+static void
+free_sbus_watch(struct tevent_context *ev, struct tevent_immediate *im,
+ void *data)
+{
+ struct sbus_watch_fd *w = talloc_get_type(data, struct sbus_watch_fd);
+ talloc_free(w); /* this will free attached 'im' as well */
+}
+
+static void
+sbus_watch_remove(DBusWatch *dbus_watch, void *data)
+{
+ struct sbus_watch_fd *watch_fd;
+
+ watch_fd = talloc_get_type(dbus_watch_get_data(dbus_watch),
+ struct sbus_watch_fd);
+ if (watch_fd == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "D-Bus is trying to remove an unknown watch!\n");
+ return;
+ }
+
+ /* Remove D-Bus watch data. */
+ dbus_watch_set_data(dbus_watch, NULL, NULL);
+
+ /* Check which watch to remove, or free if none left. */
+ if (watch_fd->dbus_watch.read == dbus_watch) {
+ watch_fd->dbus_watch.read = NULL;
+ }
+
+ if (watch_fd->dbus_watch.write == dbus_watch) {
+ watch_fd->dbus_watch.write = NULL;
+ }
+
+ if (watch_fd->dbus_watch.read == NULL
+ && watch_fd->dbus_watch.write == NULL) {
+ /* libdbus doesn't need this watch{fd} anymore, so associated
+ * tevent_fd should be removed from monitoring at the spot.
+ */
+ talloc_zfree(watch_fd->fdevent);
+ /* watch_fd itself can't be freed yet as it still may be referenced
+ * in the current context (for example in sbus_watch_handler())
+ * so instead schedule immediate event to delete it.
+ */
+ tevent_schedule_immediate(watch_fd->im_event, watch_fd->sbus_watch->ev,
+ free_sbus_watch, watch_fd);
+ }
+}
+
+static void
+sbus_watch_toggle(DBusWatch *dbus_watch, void *data)
+{
+ struct sbus_watch_fd *watch_fd;
+ dbus_bool_t is_enabled;
+ unsigned int flags;
+ int fd;
+
+ is_enabled = dbus_watch_get_enabled(dbus_watch);
+ flags = dbus_watch_get_flags(dbus_watch);
+
+ watch_fd = talloc_get_type(dbus_watch_get_data(dbus_watch),
+ struct sbus_watch_fd);
+ if (watch_fd == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "D-Bus watch [%p] does not carry "
+ "a watch context?\n", dbus_watch);
+ return;
+ }
+
+ /* Toggle state. */
+ if (is_enabled) {
+ if (flags & DBUS_WATCH_READABLE) {
+ TEVENT_FD_READABLE(watch_fd->fdevent);
+ }
+ if (flags & DBUS_WATCH_WRITABLE) {
+ TEVENT_FD_WRITEABLE(watch_fd->fdevent);
+ }
+ } else {
+ if (flags & DBUS_WATCH_READABLE) {
+ TEVENT_FD_NOT_READABLE(watch_fd->fdevent);
+ }
+ if (flags & DBUS_WATCH_WRITABLE) {
+ TEVENT_FD_NOT_WRITEABLE(watch_fd->fdevent);
+ }
+ }
+
+ fd = sbus_watch_get_fd(dbus_watch);
+
+ DEBUG(SSSDBG_TRACE_ALL, "Toggle to %s %s/%s watch on %d\n",
+ is_enabled ? "enabled" : "disabled",
+ (flags & DBUS_WATCH_READABLE) ? "R" : "-",
+ (flags & DBUS_WATCH_WRITABLE) ? "W" : "-",
+ fd);
+}
+
+/**
+ * D-Bus timeout is a mechanism to notify D-Bus every time a requested timeout
+ * is reached for a D-Bus connection.
+ *
+ * D-Bus provides add, remove and toggle function to create/remove a timer
+ * event and to switch between enabled and disabled states when a specific
+ * timer is being reused.
+ *
+ * We incorporate this watch mechanism into a tevent timer.
+ */
+
+struct sbus_timeout_ctx {
+ DBusTimeout *dbus_timeout;
+ struct tevent_timer *timed_event;
+};
+
+static void sbus_timeout_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *data)
+{
+ struct sbus_timeout_ctx *timeout;
+
+ timeout = talloc_get_type(data, struct sbus_timeout_ctx);
+
+ dbus_timeout_handle(timeout->dbus_timeout);
+}
+
+static errno_t
+sbus_timer_schedule(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_timeout_ctx *timeout_ctx)
+{
+ struct timeval tv;
+ int interval;
+
+ /* Get interval in milliseconds and use it to compute timeval. */
+ interval = dbus_timeout_get_interval(timeout_ctx->dbus_timeout);
+ tv = tevent_timeval_current_ofs(interval / 1000, interval % 1000);
+
+ timeout_ctx->timed_event = tevent_add_timer(ev, mem_ctx, tv,
+ sbus_timeout_handler,
+ timeout_ctx);
+ if (timeout_ctx->timed_event == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to set up timeout event!\n");
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static dbus_bool_t
+sbus_timer_add(DBusTimeout *dbus_timeout, void *data)
+{
+ struct sbus_watch *watch;
+ struct sbus_timeout_ctx *timeout_ctx;
+ errno_t ret;
+
+ if (!dbus_timeout_get_enabled(dbus_timeout)) {
+ return TRUE;
+ }
+
+ watch = talloc_get_type(data, struct sbus_watch);
+
+ /* Create a timeout context. */
+ timeout_ctx = talloc_zero(watch, struct sbus_timeout_ctx);
+ if (timeout_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return FALSE;
+ }
+
+ timeout_ctx->dbus_timeout = dbus_timeout;
+
+ ret = sbus_timer_schedule(timeout_ctx, watch->ev, timeout_ctx);
+ if (ret != EOK) {
+ return FALSE;
+ }
+
+ /* Save the event to the watch object so it can be removed later. */
+ dbus_timeout_set_data(timeout_ctx->dbus_timeout, timeout_ctx, NULL);
+
+ return TRUE;
+}
+
+static void
+sbus_timer_remove(DBusTimeout *dbus_timeout, void *data)
+{
+ void *timeout = dbus_timeout_get_data(dbus_timeout);
+
+ /* Remove D-Bus timeout data. */
+ dbus_timeout_set_data(dbus_timeout, NULL, NULL);
+
+ /* Free the event object. */
+ talloc_free(timeout);
+}
+
+static void
+sbus_timer_toggle(DBusTimeout *dbus_timeout, void *data)
+{
+ if (dbus_timeout_get_enabled(dbus_timeout)) {
+ sbus_timer_add(dbus_timeout, data);
+ } else {
+ sbus_timer_remove(dbus_timeout, data);
+ }
+}
+
+/**
+ * Setup tevent integration on sbus connection and server.
+ */
+
+static struct sbus_watch_control
+sbus_watch_control_setup(enum sbus_watch_type type,
+ DBusConnection *conn,
+ DBusServer *server)
+{
+ struct sbus_watch_control control;
+
+ switch (type) {
+ case SBUS_WATCH_CONNECTION:
+ control.dbus_ctx = conn;
+ control.set_watch = (sbus_dbus_set_watch_fn)dbus_connection_set_watch_functions;
+ control.set_timeout = (sbus_dbus_set_timeout_fn)dbus_connection_set_timeout_functions;
+ control.ref = (sbus_dbus_ref_fn)dbus_connection_ref;
+ control.unref = (sbus_dbus_unref_fn)dbus_connection_unref;
+ break;
+ case SBUS_WATCH_SERVER:
+ control.dbus_ctx = server;
+ control.set_watch = (sbus_dbus_set_watch_fn)dbus_server_set_watch_functions;
+ control.set_timeout = (sbus_dbus_set_timeout_fn)dbus_server_set_timeout_functions;
+ control.ref = (sbus_dbus_ref_fn)dbus_server_ref;
+ control.unref = (sbus_dbus_unref_fn)dbus_server_unref;
+ break;
+ }
+
+ return control;
+}
+
+static int
+sbus_watch_destructor(struct sbus_watch *watch)
+{
+ struct sbus_watch_control control = watch->control;
+
+ /* Disable watch. */
+ control.set_timeout(control.dbus_ctx, NULL, NULL, NULL, NULL, NULL);
+ control.set_watch(control.dbus_ctx, NULL, NULL, NULL, NULL, NULL);
+
+ return 0;
+}
+
+
+static struct sbus_watch *
+sbus_watch_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ enum sbus_watch_type type,
+ DBusConnection *conn,
+ DBusServer *server)
+{
+ struct sbus_watch *watch;
+
+ switch (type) {
+ case SBUS_WATCH_CONNECTION:
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: conn pointer is NULL!\n");
+ return NULL;
+ }
+ break;
+ case SBUS_WATCH_SERVER:
+ if (server == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: server pointer is NULL!\n");
+ return NULL;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown watch type!\n");
+ return NULL;
+ }
+
+ watch = talloc_zero(mem_ctx, struct sbus_watch);
+ if (watch == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return NULL;
+ }
+
+ watch->ev = ev;
+ watch->type = type;
+ watch->control = sbus_watch_control_setup(type, conn, server);
+
+ talloc_set_destructor(watch, sbus_watch_destructor);
+
+ return watch;
+}
+
+static errno_t
+sbus_watch_setup(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ enum sbus_watch_type type,
+ DBusConnection *conn,
+ DBusServer *server,
+ struct sbus_watch **_watch)
+{
+ struct sbus_watch *watch;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ if (_watch == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No context pointer to set!\n");
+ return ERR_INTERNAL;
+ }
+
+ if (*_watch != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Watch context is already set!\n");
+ return ERR_INTERNAL;
+ }
+
+ watch = sbus_watch_create(mem_ctx, ev, type, conn, server);
+ if (watch == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create watch context!\n");
+ return ENOMEM;
+ }
+
+ dbret = watch->control.set_watch(watch->control.dbus_ctx,
+ sbus_watch_add,
+ sbus_watch_remove,
+ sbus_watch_toggle,
+ watch, NULL);
+ if (!dbret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup D-Bus watch functions!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ dbret = watch->control.set_timeout(watch->control.dbus_ctx,
+ sbus_timer_add,
+ sbus_timer_remove,
+ sbus_timer_toggle,
+ watch, NULL);
+ if (!dbret) {
+ DEBUG(SSSDBG_CRIT_FAILURE,"Unable to setup D-Bus timeout functions\n");
+ ret = EIO;
+ goto done;
+ }
+
+ *_watch = watch;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(watch);
+ }
+
+ return ret;
+}
+
+errno_t
+sbus_watch_connection(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ DBusConnection *conn,
+ struct sbus_watch **_watch)
+{
+ return sbus_watch_setup(mem_ctx, ev, SBUS_WATCH_CONNECTION,
+ conn, NULL, _watch);
+}
+
+errno_t
+sbus_watch_server(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ DBusServer *server,
+ struct sbus_watch **_watch)
+{
+ return sbus_watch_setup(mem_ctx, ev, SBUS_WATCH_SERVER,
+ NULL, server, _watch);
+}
diff --git a/src/sbus/interface/sbus_interface.c b/src/sbus/interface/sbus_interface.c
new file mode 100644
index 0000000..e7fc05d
--- /dev/null
+++ b/src/sbus/interface/sbus_interface.c
@@ -0,0 +1,464 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <talloc.h>
+#include <string.h>
+
+#include "sbus/sbus_annotations.h"
+#include "sbus/sbus_interface_declarations.h"
+
+static struct sbus_handler
+sbus_sync_handler(sbus_handler_sync_fn handler,
+ sbus_handler_data data)
+{
+ struct sbus_handler object = {
+ .type = SBUS_HANDLER_SYNC,
+ .sync = handler,
+ .data = data
+ };
+
+ return object;
+}
+
+static struct sbus_handler
+sbus_async_handler(sbus_handler_send_fn handler_send,
+ sbus_handler_recv_fn handler_recv,
+ sbus_handler_data data)
+{
+ struct sbus_handler object = {
+ .type = SBUS_HANDLER_ASYNC,
+ .async_send = handler_send,
+ .async_recv = handler_recv,
+ .data = data
+ };
+
+ return object;
+}
+
+struct sbus_method
+sbus_method_sync(const char *name,
+ const struct sbus_method_arguments *arguments,
+ const struct sbus_annotation *annotations,
+ sbus_invoker_issue invoker_issue,
+ sbus_invoker_keygen invoker_keygen,
+ sbus_handler_sync_fn handler,
+ sbus_handler_data data)
+{
+ struct sbus_method object = {
+ .name = name,
+ .annotations = annotations,
+ .invoker = {.issue = invoker_issue, .keygen = invoker_keygen},
+ .handler = sbus_sync_handler(handler, data),
+ .arguments = arguments
+ };
+
+ return object;
+}
+
+struct sbus_method
+sbus_method_async(const char *name,
+ const struct sbus_method_arguments *arguments,
+ const struct sbus_annotation *annotations,
+ sbus_invoker_issue invoker_issue,
+ sbus_invoker_keygen invoker_keygen,
+ sbus_handler_send_fn handler_send,
+ sbus_handler_recv_fn handler_recv,
+ sbus_handler_data data)
+{
+ struct sbus_method object = {
+ .name = name,
+ .annotations = annotations,
+ .invoker = {.issue = invoker_issue, .keygen = invoker_keygen},
+ .handler = sbus_async_handler(handler_send, handler_recv, data),
+ .arguments = arguments
+ };
+
+ return object;
+}
+
+static struct sbus_method *
+sbus_method_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_method *input)
+{
+ struct sbus_method *copy;
+ size_t count;
+
+ for (count = 0; input[count].name != NULL; count++);
+
+ copy = talloc_zero_array(mem_ctx, struct sbus_method, count + 1);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ /* All data is either pointer to a static data or it is not a pointer.
+ * We can just copy it. */
+ memcpy(copy, input, sizeof(struct sbus_method) * (count + 1));
+
+ return copy;
+}
+
+struct sbus_signal
+sbus_signal(const char *name,
+ const struct sbus_argument *arguments,
+ const struct sbus_annotation *annotations)
+{
+ struct sbus_signal object = {
+ .name = name,
+ .arguments = arguments,
+ .annotations = annotations
+ };
+
+ return object;
+}
+
+static struct sbus_signal *
+sbus_signal_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_signal *input)
+{
+ struct sbus_signal *copy;
+ size_t count;
+
+ for (count = 0; input[count].name != NULL; count++);
+
+ copy = talloc_zero_array(mem_ctx, struct sbus_signal, count + 1);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ /* All data is either pointer to a static data or it is not a pointer.
+ * We can just copy it. */
+ memcpy(copy, input, sizeof(struct sbus_signal) * (count + 1));
+
+ return copy;
+}
+
+struct sbus_property
+sbus_property_sync(const char *name,
+ const char *type,
+ enum sbus_property_access access,
+ const struct sbus_annotation *annotations,
+ sbus_invoker_issue invoker_issue,
+ sbus_handler_sync_fn handler,
+ sbus_handler_data data)
+{
+ struct sbus_property object = {
+ .name = name,
+ .type = type,
+ .access = access,
+ .annotations = annotations,
+ .invoker = {.issue = invoker_issue, .keygen = NULL},
+ .handler = sbus_sync_handler(handler, data)
+ };
+
+ return object;
+}
+
+struct sbus_property
+sbus_property_async(const char *name,
+ const char *type,
+ enum sbus_property_access access,
+ const struct sbus_annotation *annotations,
+ sbus_invoker_issue invoker_issue,
+ sbus_handler_send_fn handler_send,
+ sbus_handler_recv_fn handler_recv,
+ sbus_handler_data data)
+{
+ struct sbus_property object = {
+ .name = name,
+ .type = type,
+ .access = access,
+ .annotations = annotations,
+ .invoker = {.issue = invoker_issue, .keygen = NULL},
+ .handler = sbus_async_handler(handler_send, handler_recv, data)
+ };
+
+ return object;
+}
+
+static struct sbus_property *
+sbus_property_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_property *input)
+{
+ struct sbus_property *copy;
+ size_t count;
+
+ for (count = 0; input[count].name != NULL; count++);
+
+ copy = talloc_zero_array(mem_ctx, struct sbus_property, count + 1);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ /* All data is either pointer to a static data or it is not a pointer.
+ * We can just copy it. */
+ memcpy(copy, input, sizeof(struct sbus_property) * (count + 1));
+
+ return copy;
+}
+
+struct sbus_interface
+sbus_interface(const char *name,
+ const struct sbus_annotation *annotations,
+ const struct sbus_method *methods,
+ const struct sbus_signal *signals,
+ const struct sbus_property *properties)
+{
+ struct sbus_interface object = {
+ .name = name,
+ .annotations = annotations,
+ .methods = methods,
+ .signals = signals,
+ .properties = properties
+ };
+
+ return object;
+}
+
+struct sbus_interface *
+sbus_interface_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_interface *input)
+{
+ struct sbus_interface *copy;
+
+ copy = talloc_zero(mem_ctx, struct sbus_interface);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ /* Name and annotations are pointer to static data, no need to copy them. */
+ copy->name = input->name;
+ copy->annotations = input->annotations;
+
+ copy->methods = sbus_method_copy(copy, input->methods);
+ copy->signals = sbus_signal_copy(copy, input->signals);
+ copy->properties = sbus_property_copy(copy, input->properties);
+
+ if (copy->methods == NULL || copy->signals == NULL
+ || copy->properties == NULL) {
+ talloc_free(copy);
+ return NULL;
+ }
+
+ return copy;
+}
+
+const struct sbus_method *
+sbus_interface_find_method(struct sbus_interface *iface,
+ const char *method_name)
+{
+ unsigned int i;
+
+ for (i = 0; iface->methods[i].name != NULL; i++) {
+ if (strcmp(iface->methods[i].name, method_name) == 0) {
+ return &iface->methods[i];
+ }
+ }
+
+ return NULL;
+}
+
+const struct sbus_property *
+sbus_interface_find_property(struct sbus_interface *iface,
+ enum sbus_property_access access,
+ const char *property_name)
+{
+ unsigned int i;
+
+ for (i = 0; iface->properties[i].name != NULL; i++) {
+ if (iface->properties[i].access != access) {
+ continue;
+ }
+
+ if (strcmp(iface->properties[i].name, property_name) == 0) {
+ return &iface->properties[i];
+ }
+ }
+
+ return NULL;
+}
+
+struct sbus_listener
+sbus_listener_sync(const char *interface,
+ const char *signal_name,
+ const char *object_path,
+ sbus_invoker_issue invoker_issue,
+ sbus_invoker_keygen invoker_keygen,
+ sbus_handler_sync_fn handler,
+ sbus_handler_data data)
+{
+ struct sbus_listener object = {
+ .interface = interface,
+ .signal_name = signal_name,
+ .object_path = object_path,
+ .invoker = {.issue = invoker_issue, .keygen = invoker_keygen},
+ .handler = sbus_sync_handler(handler, data)
+ };
+
+ return object;
+}
+
+struct sbus_listener
+sbus_listener_async(const char *interface,
+ const char *signal_name,
+ const char *object_path,
+ sbus_invoker_issue invoker_issue,
+ sbus_invoker_keygen invoker_keygen,
+ sbus_handler_send_fn handler_send,
+ sbus_handler_recv_fn handler_recv,
+ sbus_handler_data data)
+{
+ struct sbus_listener object = {
+ .interface = interface,
+ .signal_name = signal_name,
+ .object_path = object_path,
+ .invoker = {.issue = invoker_issue, .keygen = invoker_keygen},
+ .handler = sbus_async_handler(handler_send, handler_recv, data)
+ };
+
+ return object;
+}
+
+struct sbus_listener *
+sbus_listener_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_listener *input)
+{
+ /* All data is either pointer to a static data or it is not a pointer.
+ * We can just copy it. */
+ return talloc_memdup(mem_ctx, input, sizeof(struct sbus_listener));
+}
+
+struct sbus_node
+sbus_node_sync(const char *path,
+ sbus_handler_sync_fn factory,
+ sbus_handler_data data)
+{
+ struct sbus_node object = {
+ .path = path,
+ .factory = sbus_sync_handler(factory, data)
+ };
+
+ return object;
+}
+
+struct sbus_node
+sbus_node_async(const char *path,
+ sbus_handler_send_fn factory_send,
+ sbus_handler_recv_fn factory_recv,
+ sbus_handler_data data)
+{
+ struct sbus_node object = {
+ .path = path,
+ .factory = sbus_async_handler(factory_send, factory_recv, data)
+ };
+
+ return object;
+}
+
+struct sbus_node *
+sbus_node_copy(TALLOC_CTX *mem_ctx,
+ struct sbus_node *input)
+{
+ struct sbus_node *copy;
+
+ copy = talloc_zero(mem_ctx, struct sbus_node);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ copy->path = talloc_strdup(copy, input->path);
+ if (copy->path == NULL) {
+ talloc_free(copy);
+ return NULL;
+ }
+
+ copy->factory = input->factory;
+
+ return copy;
+}
+
+const char *
+sbus_annotation_find(const struct sbus_annotation *annotations,
+ const char *name)
+{
+ int i;
+
+ if (annotations == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; annotations[i].name != NULL; i++) {
+ if (strcmp(annotations[i].name, name) == 0) {
+ return annotations[i].value;
+ }
+ }
+
+ return NULL;
+}
+
+bool
+sbus_annotation_find_as_bool(const struct sbus_annotation *annotations,
+ const char *name)
+{
+ const char *value;
+
+ value = sbus_annotation_find(annotations, name);
+
+ if (value != NULL && strcasecmp(value, "true") == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+static void
+sbus_warn_deprecated(const struct sbus_annotation *annotations,
+ const char *iface_name,
+ const char *method_name)
+{
+ const char *by;
+ const char *member;
+ const char *dot;
+
+ if (annotations == NULL) {
+ return;
+ }
+
+ if (sbus_annotation_find_as_bool(annotations, SBUS_ANNOTATION_DEPRECATED)) {
+ member = method_name == NULL ? "" : method_name;
+ dot = method_name == NULL ? "" : ".";
+
+ by = sbus_annotation_find(annotations, SBUS_ANNOTATION_DEPRECATED_BY);
+ if (by != NULL) {
+ DEBUG(SSSDBG_IMPORTANT_INFO, "%s%s%s is deprecated by %s\n",
+ iface_name, dot, member, by);
+ } else {
+ DEBUG(SSSDBG_IMPORTANT_INFO, "%s%s%s is deprecated\n",
+ iface_name, dot, member);
+ }
+ }
+}
+
+void
+sbus_annotation_warn(const struct sbus_interface *iface,
+ const struct sbus_method *method)
+{
+ sbus_warn_deprecated(iface->annotations, iface->name, NULL);
+ sbus_warn_deprecated(method->annotations, iface->name, method->name);
+}
diff --git a/src/sbus/interface/sbus_introspection.c b/src/sbus/interface/sbus_introspection.c
new file mode 100644
index 0000000..8633837
--- /dev/null
+++ b/src/sbus/interface/sbus_introspection.c
@@ -0,0 +1,679 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <string.h>
+#include <stdio.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_private.h"
+#include "sbus/sbus_interface.h"
+#include "sbus/interface_dbus/sbus_dbus_server.h"
+
+#define FMT_DOCTYPE \
+ "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
+ " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+
+#define FMT_NODE "<node name=\"%s\">\n"
+#define FMT_IFACE " <interface name=\"%s\">\n"
+#define FMT_ANNOTATION " %s<annotation name=\"%s\" value=\"%s\" />\n"
+#define FMT_METHOD_EMPTY " <method name=\"%s\" />\n"
+#define FMT_METHOD_OPEN " <method name=\"%s\">\n"
+#define FMT_METHOD_ARG " <arg type=\"%s\" name=\"%s\" direction=\"%s\" />\n"
+#define FMT_METHOD_CLOSE " </method>\n"
+#define FMT_SIGNAL_EMPTY " <signal name=\"%s\" />\n"
+#define FMT_SIGNAL_OPEN " <signal name=\"%s\">\n"
+#define FMT_SIGNAL_ARG " <arg type=\"%s\" name=\"%s\" />\n"
+#define FMT_SIGNAL_CLOSE " </signal>\n"
+#define FMT_PROPERTY_EMPTY " <property name=\"%s\" type=\"%s\" access=\"%s\" />\n"
+#define FMT_PROPERTY_OPEN " <property name=\"%s\" type=\"%s\" access=\"%s\">\n"
+#define FMT_PROPERTY_CLOSE " </property>\n"
+#define FMT_IFACE_CLOSE " </interface>\n"
+#define FMT_CHILD_NODE " <node name=\"%s\" />\n"
+#define FMT_NODE_CLOSE "</node>\n"
+
+#define WRITE_OR_FAIL(file, ret, label, fmt, ...) do { \
+ ret = fprintf(file, fmt, ##__VA_ARGS__); \
+ if (ret < 0) { \
+ ret = EIO; \
+ goto label; \
+ } \
+} while (0)
+
+#define EMPTY(field) ((field) == NULL || (field)[0].name == NULL)
+
+enum sbus_arg_type {
+ SBUS_ARG_IN,
+ SBUS_ARG_OUT,
+ SBUS_ARG_SIGNAL
+};
+
+static errno_t
+sbus_introspect_annotations(FILE *file,
+ bool inside,
+ const struct sbus_annotation *annotations)
+{
+ errno_t ret;
+ const char *indent = inside ? " " : "";
+ int i;
+
+ if (annotations == NULL) {
+ return EOK;
+ }
+
+ for (i = 0; annotations[i].name != NULL; i++) {
+ WRITE_OR_FAIL(file, ret, done, FMT_ANNOTATION, indent,
+ annotations[i].name, annotations[i].value);
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static errno_t
+sbus_introspect_args(FILE *file,
+ enum sbus_arg_type type,
+ const struct sbus_argument *args)
+{
+ errno_t ret;
+ int i;
+
+ if (args == NULL) {
+ return EOK;
+ }
+
+ for (i = 0; args[i].name != NULL; i++) {
+ switch (type) {
+ case SBUS_ARG_SIGNAL:
+ WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_ARG,
+ args[i].type, args[i].name);
+ break;
+ case SBUS_ARG_IN:
+ WRITE_OR_FAIL(file, ret, done, FMT_METHOD_ARG,
+ args[i].type, args[i].name, "in");
+ break;
+ case SBUS_ARG_OUT:
+ WRITE_OR_FAIL(file, ret, done, FMT_METHOD_ARG,
+ args[i].type, args[i].name, "out");
+ break;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static errno_t
+sbus_introspect_methods(FILE *file,
+ const struct sbus_method *methods)
+{
+ errno_t ret;
+ int i;
+
+ if (methods == NULL) {
+ return EOK;
+ }
+
+ for (i = 0; methods[i].name != NULL; i++) {
+ if (EMPTY(methods[i].annotations)
+ && EMPTY(methods[i].arguments->input)
+ && EMPTY(methods[i].arguments->output)) {
+ WRITE_OR_FAIL(file, ret, done, FMT_METHOD_EMPTY, methods[i].name);
+ continue;
+ }
+
+ WRITE_OR_FAIL(file, ret, done, FMT_METHOD_OPEN, methods[i].name);
+
+ ret = sbus_introspect_annotations(file, true, methods[i].annotations);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_introspect_args(file, SBUS_ARG_IN,
+ methods[i].arguments->input);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_introspect_args(file, SBUS_ARG_OUT,
+ methods[i].arguments->output);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ WRITE_OR_FAIL(file, ret, done, FMT_METHOD_CLOSE);
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static errno_t
+sbus_introspect_signals(FILE *file,
+ const struct sbus_signal *signals)
+{
+ errno_t ret;
+ int i;
+
+ if (signals == NULL) {
+ return EOK;
+ }
+
+ for (i = 0; signals[i].name != NULL; i++) {
+ if (EMPTY(signals[i].annotations) && EMPTY(signals[i].arguments)) {
+ WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_EMPTY, signals[i].name);
+ continue;
+ }
+
+ WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_OPEN, signals[i].name);
+
+ ret = sbus_introspect_annotations(file, true, signals[i].annotations);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_introspect_args(file, SBUS_ARG_SIGNAL, signals[i].arguments);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_CLOSE);
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+struct sbus_introspect_property {
+ const char *name;
+ const char *type;
+ const struct sbus_annotation *annotations;
+ enum sbus_property_access access;
+};
+
+static void
+sbus_introspect_property_set(struct sbus_introspect_property *properties,
+ const struct sbus_property *property)
+{
+ int i;
+
+ for (i = 0; properties[i].name != NULL; i++) {
+ if (strcmp(properties[i].name, property->name) == 0) {
+ break;
+ }
+ }
+
+ /* Name, type and annotation is the same for both getter and setter.
+ * We just need to update access mode. */
+ properties[i].name = property->name;
+ properties[i].type = property->type;
+ properties[i].annotations = property->annotations;
+ properties[i].access |= property->access;
+}
+
+static const char *
+sbus_introspect_property_mode(struct sbus_introspect_property *property)
+{
+ switch (property->access) {
+ case SBUS_PROPERTY_READABLE:
+ return "read";
+ case SBUS_PROPERTY_WRITABLE:
+ return "write";
+ default:
+ return "readwrite";
+ }
+}
+
+static errno_t
+sbus_introspect_properties(FILE *file,
+ const struct sbus_property *properties)
+{
+ struct sbus_introspect_property *props;
+ const char *mode;
+ errno_t ret;
+ int len;
+ int i;
+
+ if (properties == NULL) {
+ return EOK;
+ }
+
+ for (len = 0; properties[len].name != NULL ; len++);
+
+ props = talloc_zero_array(NULL, struct sbus_introspect_property, len + 1);
+ if (props == NULL) {
+ return ENOMEM;
+ }
+
+ for (i = 0; properties[i].name != NULL; i++) {
+ sbus_introspect_property_set(props, &properties[i]);
+ }
+
+ for (i = 0; props[i].name != NULL; i++) {
+ mode = sbus_introspect_property_mode(&props[i]);
+
+ if (EMPTY(props[i].annotations)) {
+ WRITE_OR_FAIL(file, ret, done, FMT_PROPERTY_EMPTY,
+ props[i].name, props[i].type, mode);
+ continue;
+ }
+
+ WRITE_OR_FAIL(file, ret, done, FMT_PROPERTY_OPEN,
+ props[i].name, props[i].type, mode);
+
+ ret = sbus_introspect_annotations(file, true, props[i].annotations);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ WRITE_OR_FAIL(file, ret, done, FMT_PROPERTY_CLOSE);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(props);
+ return ret;
+}
+
+static int
+sbus_introspect_iface(FILE *file, struct sbus_interface *iface)
+{
+ errno_t ret;
+
+ WRITE_OR_FAIL(file, ret, done, FMT_IFACE, iface->name);
+
+ ret = sbus_introspect_annotations(file, false, iface->annotations);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_introspect_methods(file, iface->methods);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_introspect_signals(file, iface->signals);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_introspect_properties(file, iface->properties);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ WRITE_OR_FAIL(file, ret, done, FMT_IFACE_CLOSE);
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static int
+sbus_introspect_nodes(FILE *file, const char **nodes)
+{
+ errno_t ret;
+ int i;
+
+ if (nodes == NULL) {
+ return EOK;
+ }
+
+ for (i = 0; nodes[i] != NULL; i++) {
+ WRITE_OR_FAIL(file, ret, done, FMT_CHILD_NODE, nodes[i]);
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static char *
+sbus_introspect(TALLOC_CTX *mem_ctx,
+ const char *node,
+ const char **nodes,
+ struct sbus_interface_list *list)
+{
+ struct sbus_interface_list *item;
+ char *introspection = NULL;
+ FILE *memstream;
+ char *buffer;
+ size_t size;
+ errno_t ret;
+
+ memstream = open_memstream(&buffer, &size);
+ if (memstream == NULL) {
+ goto done;
+ }
+
+ WRITE_OR_FAIL(memstream, ret, done, FMT_DOCTYPE);
+ WRITE_OR_FAIL(memstream, ret, done, FMT_NODE, node);
+
+ DLIST_FOR_EACH(item, list) {
+ ret = sbus_introspect_iface(memstream, item->interface);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = sbus_introspect_nodes(memstream, nodes);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ WRITE_OR_FAIL(memstream, ret, done, FMT_NODE_CLOSE);
+
+ fflush(memstream);
+ introspection = talloc_memdup(mem_ctx, buffer, size + 1);
+
+done:
+ if (memstream != NULL) {
+ fclose(memstream);
+ free(buffer);
+ }
+
+ return introspection;
+}
+
+typedef errno_t
+(*sbus_node_factory_sync)(TALLOC_CTX *, const char *, void *, const char ***);
+
+typedef struct tevent_req *
+(*sbus_node_factory_send)(TALLOC_CTX *, struct tevent_context *,
+ const char *, void *);
+
+typedef errno_t
+(*sbus_node_factory_recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
+
+struct sbus_acquire_nodes_state {
+ const char **nodes;
+ struct sbus_handler *handler;
+};
+
+static void sbus_acquire_nodes_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_acquire_nodes_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_router *router,
+ const char *path)
+{
+ struct sbus_acquire_nodes_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ struct sbus_node *node;
+ sbus_node_factory_sync handler_sync;
+ sbus_node_factory_send handler_send;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_acquire_nodes_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ node = sbus_router_nodes_lookup(router->nodes, path);
+ if (node == NULL) {
+ /* If there is no node factory registered and it is a root path,
+ * we return all known paths to the router. */
+ if (strcmp(path, "/") == 0) {
+ state->nodes = sbus_router_paths_nodes(state, router->paths);
+ } else {
+ state->nodes = NULL;
+ }
+ ret = EOK;
+ goto done;
+ }
+
+ state->handler = &node->factory;
+
+ switch (node->factory.type) {
+ case SBUS_HANDLER_SYNC:
+ handler_sync = node->factory.sync;
+ if (handler_sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = handler_sync(state, path, node->factory.data, &state->nodes);
+ goto done;
+ case SBUS_HANDLER_ASYNC:
+ handler_send = node->factory.async_send;
+ if (handler_send == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ subreq = handler_send(state, ev, path, node->factory.data);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_acquire_nodes_done, req);
+ break;
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_acquire_nodes_done(struct tevent_req *subreq)
+{
+ struct sbus_acquire_nodes_state *state;
+ sbus_node_factory_recv handler_recv;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_acquire_nodes_state);
+
+ handler_recv = state->handler->async_recv;
+ if (handler_recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
+ tevent_req_error(req, ERR_INTERNAL);
+ return;
+ }
+
+ ret = handler_recv(state, subreq, &state->nodes);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+sbus_acquire_nodes_recv(struct tevent_req *req,
+ const char ***_nodes)
+{
+ struct sbus_acquire_nodes_state *state;
+ state = tevent_req_data(req, struct sbus_acquire_nodes_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ /* We keep the nodes allocated on this request state, so we do not have
+ * to expect that state->nodes is a talloc context. This way, it may
+ * be static array. */
+
+ *_nodes = state->nodes;
+
+ return EOK;
+}
+
+struct sbus_introspection_state {
+ struct sbus_interface_list *list;
+ const char *introspection;
+ const char *path;
+};
+
+static void sbus_introspection_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_introspection_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct sbus_router *router)
+{
+ struct sbus_introspection_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_introspection_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->path = sbus_req->path;
+ state->introspection = NULL;
+
+ ret = sbus_router_paths_supported(state, router->paths,
+ sbus_req->path, &state->list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to acquire interface list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ subreq = sbus_acquire_nodes_send(mem_ctx, ev, router, sbus_req->path);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_introspection_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_introspection_done(struct tevent_req *subreq)
+{
+ struct sbus_introspection_state *state;
+ struct tevent_req *req;
+ const char **nodes;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_introspection_state);
+
+ /* We keep the nodes allocated on subrequest state, so we do not have
+ * to expect that it is a talloc context and allow it also as a static
+ * array. Therefore we must free subreq later. */
+
+ ret = sbus_acquire_nodes_recv(subreq, &nodes);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ state->introspection = sbus_introspect(state, state->path,
+ nodes, state->list);
+ if (state->introspection == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+done:
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+sbus_introspection_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char **_introspection)
+{
+ struct sbus_introspection_state *state;
+ state = tevent_req_data(req, struct sbus_introspection_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_introspection = talloc_steal(mem_ctx, state->introspection);
+
+ return EOK;
+}
+
+errno_t
+sbus_register_introspection(struct sbus_router *router)
+{
+
+ SBUS_INTERFACE(iface,
+ org_freedesktop_DBus_Introspectable,
+ SBUS_METHODS(
+ SBUS_ASYNC(METHOD, org_freedesktop_DBus_Introspectable, Introspect,
+ sbus_introspection_send, sbus_introspection_recv,
+ router)
+ ),
+ SBUS_WITHOUT_SIGNALS,
+ SBUS_WITHOUT_PROPERTIES
+ );
+
+ struct sbus_path paths[] = {
+ {"/", &iface},
+ {"/*", &iface},
+ {NULL, NULL}
+ };
+
+ return sbus_router_add_path_map(router, paths);
+}
diff --git a/src/sbus/interface/sbus_iterator_readers.c b/src/sbus/interface/sbus_iterator_readers.c
new file mode 100644
index 0000000..6113f6d
--- /dev/null
+++ b/src/sbus/interface/sbus_iterator_readers.c
@@ -0,0 +1,414 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <errno.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/interface/sbus_iterator_readers.h"
+
+static errno_t
+sbus_iterator_read_basic(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ int dbus_type,
+ void *_value_ptr)
+{
+ int arg_type;
+ char **strptr;
+ char *str;
+
+ arg_type = dbus_message_iter_get_arg_type(iterator);
+ if (arg_type != dbus_type) {
+ return ERR_SBUS_INVALID_TYPE;
+ }
+
+ dbus_message_iter_get_basic(iterator, _value_ptr);
+ dbus_message_iter_next(iterator);
+
+ switch (dbus_type) {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ strptr = (char**)_value_ptr;
+ str = talloc_strdup(mem_ctx, *strptr);
+ if (str == NULL) {
+ return ENOMEM;
+ }
+ *strptr = str;
+ break;
+ default:
+ break;
+ }
+
+ return EOK;
+}
+
+static errno_t
+_sbus_iterator_read_basic_array(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ int dbus_type,
+ int element_size,
+ void **_value_ptr)
+{
+ DBusMessageIter subiter;
+ uint8_t *arrayptr;
+ void *array = NULL;
+ int arg_type;
+ int count;
+ errno_t ret;
+ int i;
+
+ arg_type = dbus_message_iter_get_arg_type(iterator);
+ if (arg_type != DBUS_TYPE_ARRAY) {
+ ret = ERR_SBUS_INVALID_TYPE;
+ goto done;
+ }
+
+ count = dbus_message_iter_get_element_count(iterator);
+ dbus_message_iter_recurse(iterator, &subiter);
+
+ /* NULL-terminated array for pointer types */
+ switch (dbus_type) {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ array = talloc_zero_size(mem_ctx, (size_t)(count + 1) * element_size);
+ if (array == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (count == 0) {
+ array = NULL;
+ ret = EOK;
+ goto done;
+ }
+ break;
+ default:
+ if (count == 0) {
+ array = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ array = talloc_zero_size(mem_ctx, (size_t)count * element_size);
+ if (array == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ break;
+ }
+
+ arrayptr = array;
+ for (i = 0; i < count; i++) {
+ ret = sbus_iterator_read_basic(array, &subiter, dbus_type, arrayptr);
+ if (ret != EOK) {
+ talloc_free(array);
+ goto done;
+ }
+
+ arrayptr += element_size;
+ }
+
+ ret = EOK;
+
+done:
+ /* Always step past the array. */
+ dbus_message_iter_next(iterator);
+
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_value_ptr = array;
+
+ return ret;
+}
+
+#define sbus_iterator_read_basic_array(mem_ctx, iterator, dbus_type, c_type, dest) \
+ _sbus_iterator_read_basic_array((mem_ctx), (iterator), (dbus_type), \
+ sizeof(c_type), (void**)(dest))
+
+errno_t sbus_iterator_read_y(DBusMessageIter *iterator,
+ uint8_t *_value)
+{
+ return sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_BYTE, _value);
+}
+
+errno_t sbus_iterator_read_b(DBusMessageIter *iterator,
+ bool *_value)
+{
+ dbus_bool_t dbus_value;
+ errno_t ret;
+
+ ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_BOOLEAN, &dbus_value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_value = dbus_value;
+
+ return EOK;
+}
+
+errno_t sbus_iterator_read_n(DBusMessageIter *iterator,
+ int16_t *_value)
+{
+ dbus_int16_t dbus_value;
+ errno_t ret;
+
+ ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_INT16, &dbus_value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_value = dbus_value;
+
+ return EOK;
+}
+
+errno_t sbus_iterator_read_q(DBusMessageIter *iterator,
+ uint16_t *_value)
+{
+ dbus_uint16_t dbus_value;
+ errno_t ret;
+
+ ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_UINT16, &dbus_value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_value = dbus_value;
+
+ return EOK;
+}
+
+errno_t sbus_iterator_read_i(DBusMessageIter *iterator,
+ int32_t *_value)
+{
+ dbus_int32_t dbus_value;
+ errno_t ret;
+
+ ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_INT32, &dbus_value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_value = dbus_value;
+
+ return EOK;
+}
+
+errno_t sbus_iterator_read_u(DBusMessageIter *iterator,
+ uint32_t *_value)
+{
+ dbus_uint32_t dbus_value;
+ errno_t ret;
+
+ ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_UINT32, &dbus_value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_value = dbus_value;
+
+ return EOK;
+}
+
+errno_t sbus_iterator_read_x(DBusMessageIter *iterator,
+ int64_t *_value)
+{
+ dbus_int64_t dbus_value;
+ errno_t ret;
+
+ ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_INT64, &dbus_value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_value = dbus_value;
+
+ return EOK;
+}
+
+errno_t sbus_iterator_read_t(DBusMessageIter *iterator,
+ uint64_t *_value)
+{
+ dbus_uint64_t dbus_value;
+ errno_t ret;
+
+ ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_UINT64, &dbus_value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_value = dbus_value;
+
+ return EOK;
+}
+
+errno_t sbus_iterator_read_d(DBusMessageIter *iterator,
+ double *_value)
+{
+ return sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_DOUBLE, _value);
+}
+
+errno_t sbus_iterator_read_s(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ const char **_value)
+{
+ return sbus_iterator_read_basic(mem_ctx, iterator, DBUS_TYPE_STRING, _value);
+}
+
+errno_t sbus_iterator_read_S(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ char **_value)
+{
+ return sbus_iterator_read_basic(mem_ctx, iterator, DBUS_TYPE_STRING, _value);
+}
+
+errno_t sbus_iterator_read_o(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ const char **_value)
+{
+ return sbus_iterator_read_basic(mem_ctx, iterator, DBUS_TYPE_OBJECT_PATH, _value);
+}
+
+errno_t sbus_iterator_read_O(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ char **_value)
+{
+ return sbus_iterator_read_basic(mem_ctx, iterator, DBUS_TYPE_OBJECT_PATH, _value);
+}
+
+errno_t sbus_iterator_read_ay(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ uint8_t **_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_BYTE,
+ uint8_t, _value);
+}
+
+errno_t sbus_iterator_read_ab(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ bool **_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_BOOLEAN,
+ uint8_t, _value);
+}
+
+errno_t sbus_iterator_read_an(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ int16_t **_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_INT16,
+ int16_t, _value);
+}
+
+errno_t sbus_iterator_read_aq(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ uint16_t **_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_UINT16,
+ uint16_t, _value);
+}
+
+errno_t sbus_iterator_read_ai(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ int32_t **_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_INT32,
+ int32_t, _value);
+}
+
+errno_t sbus_iterator_read_au(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ uint32_t **_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_UINT32,
+ uint32_t, _value);
+}
+
+errno_t sbus_iterator_read_ax(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ int64_t **_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_INT64,
+ int64_t, _value);
+}
+
+errno_t sbus_iterator_read_at(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ uint64_t **_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_UINT64,
+ uint64_t, _value);
+}
+
+errno_t sbus_iterator_read_ad(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ double **_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_DOUBLE,
+ double, _value);
+}
+
+errno_t sbus_iterator_read_as(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ const char ***_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_STRING,
+ const char *, _value);
+}
+
+errno_t sbus_iterator_read_aS(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ char ***_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_STRING,
+ char *, _value);
+}
+
+errno_t sbus_iterator_read_ao(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ const char ***_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_OBJECT_PATH,
+ const char *, _value);
+}
+
+errno_t sbus_iterator_read_aO(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ char ***_value)
+{
+ return sbus_iterator_read_basic_array(mem_ctx, iterator,
+ DBUS_TYPE_OBJECT_PATH,
+ char *, _value);
+}
diff --git a/src/sbus/interface/sbus_iterator_readers.h b/src/sbus/interface/sbus_iterator_readers.h
new file mode 100644
index 0000000..6785243
--- /dev/null
+++ b/src/sbus/interface/sbus_iterator_readers.h
@@ -0,0 +1,126 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_ITERATOR_READERS_H_
+#define _SBUS_ITERATOR_READERS_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+
+errno_t sbus_iterator_read_y(DBusMessageIter *iterator,
+ uint8_t *_value);
+
+errno_t sbus_iterator_read_b(DBusMessageIter *iterator,
+ bool *_value);
+
+errno_t sbus_iterator_read_n(DBusMessageIter *iterator,
+ int16_t *_value);
+
+errno_t sbus_iterator_read_q(DBusMessageIter *iterator,
+ uint16_t *_value);
+
+errno_t sbus_iterator_read_i(DBusMessageIter *iterator,
+ int32_t *_value);
+
+errno_t sbus_iterator_read_u(DBusMessageIter *iterator,
+ uint32_t *_value);
+
+errno_t sbus_iterator_read_x(DBusMessageIter *iterator,
+ int64_t *_value);
+
+errno_t sbus_iterator_read_t(DBusMessageIter *iterator,
+ uint64_t *_value);
+
+errno_t sbus_iterator_read_d(DBusMessageIter *iterator,
+ double *_value);
+
+errno_t sbus_iterator_read_s(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ const char **_value);
+
+errno_t sbus_iterator_read_S(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ char **_value);
+
+errno_t sbus_iterator_read_o(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ const char **_value);
+
+errno_t sbus_iterator_read_O(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ char **_value);
+
+errno_t sbus_iterator_read_ay(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ uint8_t **_value);
+
+errno_t sbus_iterator_read_ab(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ bool **_value);
+
+errno_t sbus_iterator_read_an(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ int16_t **_value);
+
+errno_t sbus_iterator_read_aq(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ uint16_t **_value);
+
+errno_t sbus_iterator_read_ai(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ int32_t **_value);
+
+errno_t sbus_iterator_read_au(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ uint32_t **_value);
+
+errno_t sbus_iterator_read_ax(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ int64_t **_value);
+
+errno_t sbus_iterator_read_at(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ uint64_t **_value);
+
+errno_t sbus_iterator_read_ad(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ double **_value);
+
+errno_t sbus_iterator_read_as(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ const char ***_value);
+
+errno_t sbus_iterator_read_aS(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ char ***_value);
+
+errno_t sbus_iterator_read_ao(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ const char ***_value);
+
+errno_t sbus_iterator_read_aO(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iterator,
+ char ***_value);
+
+#endif /* _SBUS_ITERATOR_READERS_H_ */
diff --git a/src/sbus/interface/sbus_iterator_writers.c b/src/sbus/interface/sbus_iterator_writers.c
new file mode 100644
index 0000000..3becc8f
--- /dev/null
+++ b/src/sbus/interface/sbus_iterator_writers.c
@@ -0,0 +1,365 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/sss_utf8.h"
+#include "sbus/interface/sbus_iterator_writers.h"
+
+static errno_t
+sbus_iterator_write_basic(DBusMessageIter *iterator,
+ int dbus_type,
+ void *value_ptr)
+{
+ dbus_bool_t ret;
+
+ ret = dbus_message_iter_append_basic(iterator, dbus_type, value_ptr);
+
+ return ret ? EOK : EIO;
+}
+
+static errno_t
+sbus_iterator_write_string(DBusMessageIter *iterator,
+ int dbus_type,
+ const char *value,
+ const char *default_value)
+{
+ dbus_bool_t ret;
+ bool is_valid;
+
+ /* If the value is not set, we will provide a correct default value. */
+ value = value == NULL ? default_value : value;
+
+ /* D-Bus is not capable of sending NULL string. If even the default value
+ * was not set, we return an error. */
+ if (value == NULL) {
+ return ERR_SBUS_EMPTY_STRING;
+ }
+
+ /* D-Bus can send only correct UTF-8 strings. */
+ is_valid = sss_utf8_check((const uint8_t *)value, strlen(value));
+ if (!is_valid) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "String with non-utf8 characters was "
+ "given [%s]\n", value);
+ return ERR_SBUS_INVALID_STRING;
+ }
+
+ ret = dbus_message_iter_append_basic(iterator, dbus_type, &value);
+
+ return ret ? EOK : EIO;
+}
+
+static errno_t
+sbus_iterator_write_string_elements(DBusMessageIter *iterator,
+ int dbus_type,
+ const char **values)
+{
+ errno_t ret;
+ int i;
+
+ if (values == NULL) {
+ return EOK;
+ }
+
+ /* String arrays are NULL-terminated. */
+ for (i = 0; values[i] != NULL; i++) {
+ ret = sbus_iterator_write_string(iterator, dbus_type, values[i], NULL);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t
+sbus_iterator_write_fixed_elements(DBusMessageIter *iterator,
+ int dbus_type,
+ int element_size,
+ int array_length,
+ void *value_ptr)
+{
+ errno_t ret;
+ uint8_t *element_ptr;
+ int count;
+ int i;
+
+ element_ptr = value_ptr;
+ if (array_length < 0) {
+ count = talloc_get_size(value_ptr) / element_size;
+ } else {
+ count = array_length;
+ }
+
+
+ for (i = 0; i < count; i++) {
+ ret = sbus_iterator_write_basic(iterator, dbus_type, element_ptr);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ element_ptr += element_size;
+ }
+
+ return EOK;
+}
+
+errno_t
+_sbus_iterator_write_basic_array(DBusMessageIter *iterator,
+ int dbus_type,
+ int element_size,
+ int array_length,
+ void *value_ptr)
+{
+ const char array_type[2] = {dbus_type, '\0'};
+ DBusMessageIter arrayiter;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ dbret = dbus_message_iter_open_container(iterator, DBUS_TYPE_ARRAY,
+ array_type, &arrayiter);
+ if (!dbret) {
+ return EIO;
+ }
+
+ switch (dbus_type) {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ ret = sbus_iterator_write_string_elements(&arrayiter, dbus_type,
+ (const char **)value_ptr);
+ if (ret != EOK) {
+ goto done;
+ }
+ break;
+ default:
+ ret = sbus_iterator_write_fixed_elements(&arrayiter, dbus_type,
+ element_size, array_length,
+ value_ptr);
+ if (ret != EOK) {
+ goto done;
+ }
+ break;
+ }
+
+ dbret = dbus_message_iter_close_container(iterator, &arrayiter);
+ if (!dbret) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ dbus_message_iter_abandon_container(iterator, &arrayiter);
+ }
+
+ return ret;
+}
+
+errno_t sbus_iterator_write_y(DBusMessageIter *iterator,
+ uint8_t value)
+{
+ return sbus_iterator_write_basic(iterator, DBUS_TYPE_BYTE, &value);
+}
+
+errno_t sbus_iterator_write_b(DBusMessageIter *iterator,
+ bool value)
+{
+ dbus_bool_t dbus_value = value;
+
+ return sbus_iterator_write_basic(iterator, DBUS_TYPE_BOOLEAN, &dbus_value);
+}
+
+errno_t sbus_iterator_write_n(DBusMessageIter *iterator,
+ int16_t value)
+{
+ dbus_int16_t dbus_value = value;
+
+ return sbus_iterator_write_basic(iterator, DBUS_TYPE_INT16, &dbus_value);
+}
+
+errno_t sbus_iterator_write_q(DBusMessageIter *iterator,
+ uint16_t value)
+{
+ dbus_uint16_t dbus_value = value;
+
+ return sbus_iterator_write_basic(iterator, DBUS_TYPE_UINT16, &dbus_value);
+}
+
+errno_t sbus_iterator_write_i(DBusMessageIter *iterator,
+ int32_t value)
+{
+ dbus_int32_t dbus_value = value;
+
+ return sbus_iterator_write_basic(iterator, DBUS_TYPE_INT32, &dbus_value);
+}
+
+errno_t sbus_iterator_write_u(DBusMessageIter *iterator,
+ uint32_t value)
+{
+ dbus_uint32_t dbus_value = value;
+
+ return sbus_iterator_write_basic(iterator, DBUS_TYPE_UINT32, &dbus_value);
+}
+
+errno_t sbus_iterator_write_x(DBusMessageIter *iterator,
+ int64_t value)
+{
+ dbus_int64_t dbus_value = value;
+
+ return sbus_iterator_write_basic(iterator, DBUS_TYPE_INT64, &dbus_value);
+}
+
+errno_t sbus_iterator_write_t(DBusMessageIter *iterator,
+ uint64_t value)
+{
+ dbus_uint64_t dbus_value = value;
+
+ return sbus_iterator_write_basic(iterator, DBUS_TYPE_UINT64, &dbus_value);
+}
+
+errno_t sbus_iterator_write_d(DBusMessageIter *iterator,
+ double value)
+{
+ return sbus_iterator_write_basic(iterator, DBUS_TYPE_DOUBLE, &value);
+}
+
+errno_t sbus_iterator_write_s(DBusMessageIter *iterator,
+ const char *value)
+{
+ return sbus_iterator_write_string(iterator, DBUS_TYPE_STRING, value, "");
+}
+
+errno_t sbus_iterator_write_S(DBusMessageIter *iterator,
+ char *value)
+{
+ return sbus_iterator_write_string(iterator, DBUS_TYPE_STRING, value, "");
+}
+
+errno_t sbus_iterator_write_o(DBusMessageIter *iterator,
+ const char *value)
+{
+ return sbus_iterator_write_string(iterator, DBUS_TYPE_OBJECT_PATH,
+ value, "/");
+}
+
+errno_t sbus_iterator_write_O(DBusMessageIter *iterator,
+ char *value)
+{
+ return sbus_iterator_write_string(iterator, DBUS_TYPE_OBJECT_PATH,
+ value, "/");
+}
+
+errno_t sbus_iterator_write_ay(DBusMessageIter *iterator,
+ uint8_t *value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_BYTE,
+ uint8_t, value);
+}
+
+errno_t sbus_iterator_write_ab(DBusMessageIter *iterator,
+ bool *value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_BOOLEAN,
+ bool, value);
+}
+
+errno_t sbus_iterator_write_an(DBusMessageIter *iterator,
+ int16_t *value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_INT16,
+ int16_t, value);
+}
+
+errno_t sbus_iterator_write_aq(DBusMessageIter *iterator,
+ uint16_t *value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_UINT16,
+ uint16_t, value);
+}
+
+errno_t sbus_iterator_write_ai(DBusMessageIter *iterator,
+ int32_t *value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_INT32,
+ int32_t, value);
+}
+
+errno_t sbus_iterator_write_au(DBusMessageIter *iterator,
+ uint32_t *value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_UINT32,
+ uint32_t, value);
+}
+
+errno_t sbus_iterator_write_ax(DBusMessageIter *iterator,
+ int64_t *value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_INT64,
+ int64_t, value);
+}
+
+errno_t sbus_iterator_write_at(DBusMessageIter *iterator,
+ uint64_t *value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_UINT64,
+ uint64_t, value);
+}
+
+errno_t sbus_iterator_write_ad(DBusMessageIter *iterator,
+ double *value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_DOUBLE,
+ double, value);
+}
+
+errno_t sbus_iterator_write_as(DBusMessageIter *iterator,
+ const char **value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_STRING,
+ const char *, value);
+}
+
+errno_t sbus_iterator_write_aS(DBusMessageIter *iterator,
+ char **value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_STRING,
+ char *, value);
+}
+
+errno_t sbus_iterator_write_ao(DBusMessageIter *iterator,
+ const char **value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_OBJECT_PATH,
+ const char *, value);
+}
+
+errno_t sbus_iterator_write_aO(DBusMessageIter *iterator,
+ char **value)
+{
+ return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_OBJECT_PATH,
+ char *, value);
+}
diff --git a/src/sbus/interface/sbus_iterator_writers.h b/src/sbus/interface/sbus_iterator_writers.h
new file mode 100644
index 0000000..ec0662e
--- /dev/null
+++ b/src/sbus/interface/sbus_iterator_writers.h
@@ -0,0 +1,126 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_ITERATOR_WRITERS_H_
+#define _SBUS_ITERATOR_WRITERS_H_
+
+#include <stdint.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+
+/* Generic writers to be used in custom type handlers. */
+
+errno_t
+_sbus_iterator_write_basic_array(DBusMessageIter *iterator,
+ int dbus_type,
+ int element_size,
+ int array_length,
+ void *value_ptr);
+
+#define sbus_iterator_write_basic_array(iterator, dbus_type, c_type, source) \
+ _sbus_iterator_write_basic_array((iterator), (dbus_type), \
+ sizeof(c_type), -1, (void*)(source))
+
+#define sbus_iterator_write_basic_array_len(iterator, dbus_type, c_type, source, length) \
+ _sbus_iterator_write_basic_array((iterator), (dbus_type), \
+ sizeof(c_type), (length), (void*)(source))
+
+/* Basic types. */
+
+errno_t sbus_iterator_write_y(DBusMessageIter *iterator,
+ uint8_t value);
+
+errno_t sbus_iterator_write_b(DBusMessageIter *iterator,
+ bool value);
+
+errno_t sbus_iterator_write_n(DBusMessageIter *iterator,
+ int16_t value);
+
+errno_t sbus_iterator_write_q(DBusMessageIter *iterator,
+ uint16_t value);
+
+errno_t sbus_iterator_write_i(DBusMessageIter *iterator,
+ int32_t value);
+
+errno_t sbus_iterator_write_u(DBusMessageIter *iterator,
+ uint32_t value);
+
+errno_t sbus_iterator_write_x(DBusMessageIter *iterator,
+ int64_t value);
+
+errno_t sbus_iterator_write_t(DBusMessageIter *iterator,
+ uint64_t value);
+
+errno_t sbus_iterator_write_d(DBusMessageIter *iterator,
+ double value);
+
+errno_t sbus_iterator_write_s(DBusMessageIter *iterator,
+ const char *value);
+
+errno_t sbus_iterator_write_S(DBusMessageIter *iterator,
+ char *value);
+
+errno_t sbus_iterator_write_o(DBusMessageIter *iterator,
+ const char *value);
+
+errno_t sbus_iterator_write_O(DBusMessageIter *iterator,
+ char *value);
+
+errno_t sbus_iterator_write_ay(DBusMessageIter *iterator,
+ uint8_t *value);
+
+errno_t sbus_iterator_write_ab(DBusMessageIter *iterator,
+ bool *value);
+
+errno_t sbus_iterator_write_an(DBusMessageIter *iterator,
+ int16_t *value);
+
+errno_t sbus_iterator_write_aq(DBusMessageIter *iterator,
+ uint16_t *value);
+
+errno_t sbus_iterator_write_ai(DBusMessageIter *iterator,
+ int32_t *value);
+
+errno_t sbus_iterator_write_au(DBusMessageIter *iterator,
+ uint32_t *value);
+
+errno_t sbus_iterator_write_ax(DBusMessageIter *iterator,
+ int64_t *value);
+
+errno_t sbus_iterator_write_at(DBusMessageIter *iterator,
+ uint64_t *value);
+
+errno_t sbus_iterator_write_ad(DBusMessageIter *iterator,
+ double *value);
+
+errno_t sbus_iterator_write_as(DBusMessageIter *iterator,
+ const char **value);
+
+errno_t sbus_iterator_write_aS(DBusMessageIter *iterator,
+ char **value);
+
+errno_t sbus_iterator_write_ao(DBusMessageIter *iterator,
+ const char **value);
+
+errno_t sbus_iterator_write_aO(DBusMessageIter *iterator,
+ char **value);
+
+#endif /* _SBUS_ITERATOR_WRITERS_H_ */
diff --git a/src/sbus/interface/sbus_properties.c b/src/sbus/interface/sbus_properties.c
new file mode 100644
index 0000000..8be933c
--- /dev/null
+++ b/src/sbus/interface/sbus_properties.c
@@ -0,0 +1,894 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <errno.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_private.h"
+#include "sbus/interface/sbus_iterator_readers.h"
+#include "sbus/interface/sbus_iterator_writers.h"
+#include "sbus/interface_dbus/sbus_dbus_server.h"
+
+static errno_t
+sbus_open_variant(DBusMessageIter *parent,
+ DBusMessageIter *sub,
+ const char *type)
+{
+ dbus_bool_t dbret;
+
+ dbret = dbus_message_iter_open_container(parent, DBUS_TYPE_VARIANT,
+ type, sub);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static errno_t
+sbus_open_dict(DBusMessageIter *parent,
+ DBusMessageIter *sub)
+{
+ dbus_bool_t dbret;
+
+ dbret = dbus_message_iter_open_container(parent, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ sub);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static errno_t
+sbus_open_dict_entry(DBusMessageIter *parent,
+ DBusMessageIter *sub)
+{
+ dbus_bool_t dbret;
+
+ dbret = dbus_message_iter_open_container(parent, DBUS_TYPE_DICT_ENTRY,
+ NULL, sub);
+ if (!dbret) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static errno_t
+sbus_close_iterator(DBusMessageIter *parent,
+ DBusMessageIter *sub)
+{
+ dbus_bool_t dbret;
+
+ dbret = dbus_message_iter_close_container(parent, sub);
+ if (!dbret) {
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static errno_t
+sbus_create_dummy_message(TALLOC_CTX *mem_ctx,
+ DBusMessage **_msg,
+ DBusMessageIter *_write_iter)
+{
+ DBusMessage *msg;
+ errno_t ret;
+
+ msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+ if (msg == NULL) {
+ return ENOMEM;
+ }
+
+ /* Set fake serial number for reply. */
+ dbus_message_set_serial(msg, 1);
+
+ ret = sbus_message_bound(mem_ctx, msg);
+ if (ret != EOK) {
+ dbus_message_unref(msg);
+ return ret;
+ }
+
+ dbus_message_iter_init_append(msg, _write_iter);
+
+ *_msg = msg;
+
+ return EOK;
+}
+
+static errno_t
+sbus_copy_iterator_value(DBusMessageIter *from,
+ DBusMessageIter *to);
+
+static errno_t
+sbus_copy_iterator_fixed_array(DBusMessageIter *from,
+ DBusMessageIter *to,
+ int type)
+{
+ DBusMessageIter from_sub;
+ DBusMessageIter to_sub;
+ dbus_bool_t dbret;
+ const char *typestr;
+ void *fixed;
+ int count;
+
+ typestr = dbus_message_type_to_string(type);
+ if (typestr == NULL) {
+ return ERR_INTERNAL;
+ }
+
+ dbret = dbus_message_iter_open_container(to, DBUS_TYPE_ARRAY,
+ typestr, &to_sub);
+ if (!dbret) {
+ return EIO;
+ }
+
+ dbus_message_iter_recurse(from, &from_sub);
+ dbus_message_iter_get_fixed_array(&from_sub, &fixed, &count);
+
+ dbret = dbus_message_iter_append_fixed_array(&to_sub, type, &fixed, count);
+ if (!dbret) {
+ goto fail;
+ }
+
+ dbret = dbus_message_iter_close_container(to, &to_sub);
+ if (!dbret) {
+ goto fail;
+ }
+
+ return EOK;
+
+fail:
+ dbus_message_iter_abandon_container(to, &to_sub);
+ return EIO;
+}
+
+static errno_t
+sbus_copy_iterator_container(DBusMessageIter *from,
+ DBusMessageIter *to,
+ int type)
+{
+ DBusMessageIter from_sub;
+ DBusMessageIter to_sub;
+ const char *signature;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ dbus_message_iter_recurse(from, &from_sub);
+
+ if (type == DBUS_TYPE_DICT_ENTRY) {
+ /* This is a special case. Dictionary entries do not have any specific
+ * signature when we open their container. */
+ signature = NULL;
+ } else {
+ signature = dbus_message_iter_get_signature(&from_sub);
+ if (signature == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ dbret = dbus_message_iter_open_container(to, type, signature, &to_sub);
+ if (!dbret) {
+ return EIO;
+ }
+
+ ret = sbus_copy_iterator_value(&from_sub, &to_sub);
+ if (ret != EOK) {
+ ret = EIO;
+ goto done;
+ }
+
+ dbret = dbus_message_iter_close_container(to, &to_sub);
+ if (!dbret) {
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ dbus_message_iter_abandon_container(to, &to_sub);
+ }
+
+ return ret;
+}
+
+static errno_t
+sbus_copy_iterator_value(DBusMessageIter *from,
+ DBusMessageIter *to)
+{
+ void *basic;
+ dbus_bool_t dbret;
+ int element_type;
+ int type;
+ errno_t ret;
+
+ do {
+ type = dbus_message_iter_get_arg_type(from);
+
+ if (type == DBUS_TYPE_INVALID) {
+ /* We have reached the end of the message. */
+ return EOK;
+ }
+
+ /* If this is a basic type, we just write it to its destination. */
+ if (dbus_type_is_basic(type)) {
+ dbus_message_iter_get_basic(from, &basic);
+ dbret = dbus_message_iter_append_basic(to, type, &basic);
+ if (!dbret) {
+ return EIO;
+ }
+
+ continue;
+ }
+
+ if (type == DBUS_TYPE_ARRAY) {
+ element_type = dbus_message_iter_get_element_type(from);
+
+ /* Fixed types can be copied at once. Otherwise we treat it
+ * as any other container. */
+ if (dbus_type_is_fixed(element_type)) {
+ ret = sbus_copy_iterator_fixed_array(from, to, element_type);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ continue;
+ }
+ }
+
+ /* If this is a container, we need to descend into it and open
+ * this container in the destination iterator. */
+ if (dbus_type_is_container(type)) {
+ ret = sbus_copy_iterator_container(from, to, type);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ continue;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type [%d]\n", type);
+ return ERR_INTERNAL;
+ } while (dbus_message_iter_next(from));
+
+ return EOK;
+}
+
+static errno_t
+sbus_copy_message_to_dictionary(const char *name,
+ DBusMessage *msg,
+ DBusMessageIter *to)
+{
+ DBusMessageIter entry;
+ DBusMessageIter from;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ /* Open dictionary entry iterator. */
+ ret = sbus_open_dict_entry(to, &entry);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Append property name as key. */
+ ret = sbus_iterator_write_s(&entry, name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Open message iterator for reading. */
+ dbret = dbus_message_iter_init(msg, &from);
+ if (!dbret) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_copy_iterator_value(&from, &entry);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_close_iterator(to, &entry);
+
+done:
+ if (ret != EOK) {
+ dbus_message_iter_abandon_container(to, &entry);
+ }
+
+ return ret;
+}
+
+static errno_t
+sbus_request_property(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ struct sbus_router *router,
+ const struct sbus_sender *sender,
+ enum sbus_property_access access,
+ const char *destination,
+ const char *path,
+ const char *interface_name,
+ const char *property_name,
+ struct sbus_request **_sbus_req,
+ const struct sbus_property **_property)
+{
+ const struct sbus_property *property;
+ struct sbus_request *sbus_req;
+ struct sbus_interface *iface;
+ enum sbus_request_type type;
+
+ iface = sbus_router_paths_lookup(router->paths, path, interface_name);
+ if (iface == NULL) {
+ return ERR_SBUS_UNKNOWN_INTERFACE;
+ }
+
+ property = sbus_interface_find_property(iface, access, property_name);
+ if (property == NULL) {
+ return ERR_SBUS_UNKNOWN_PROPERTY;
+ }
+
+ switch (access) {
+ case SBUS_PROPERTY_READABLE:
+ type = SBUS_REQUEST_PROPERTY_GET;
+ break;
+ case SBUS_PROPERTY_WRITABLE:
+ type = SBUS_REQUEST_PROPERTY_SET;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ sbus_req = sbus_request_create(mem_ctx, conn, type, destination,
+ interface_name, property_name, path);
+ if (sbus_req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create request data!\n");
+ return ENOMEM;
+ }
+
+ sbus_req->sender = sbus_sender_copy(sbus_req, sender);
+ if (sbus_req->sender == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to copy sender data!\n");
+ talloc_free(sbus_req);
+ return ENOMEM;
+ }
+
+ *_sbus_req = sbus_req;
+ *_property = property;
+
+ return EOK;
+}
+
+struct sbus_properties_get_state {
+ struct {
+ DBusMessageIter *root;
+ DBusMessageIter variant;
+ } iter;
+};
+
+static void sbus_properties_get_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_properties_get_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct sbus_router *router,
+ const char *interface_name,
+ const char *property_name,
+ DBusMessageIter *write_iterator)
+{
+ struct sbus_properties_get_state *state;
+ const struct sbus_property *property;
+ struct sbus_request *property_req;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_ALL, "Requesting property: %s.%s of %s\n",
+ interface_name, property_name, sbus_req->path);
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_properties_get_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ ret = sbus_request_property(state, sbus_req->conn, router, sbus_req->sender,
+ SBUS_PROPERTY_READABLE, sbus_req->destination,
+ sbus_req->path, interface_name, property_name,
+ &property_req, &property);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot request property %s.%s [%d]: %s\n",
+ interface_name, property_name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sbus_check_access(router->conn, property_req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ state->iter.root = write_iterator;
+ ret = sbus_open_variant(state->iter.root, &state->iter.variant,
+ property->type);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ subreq = property->invoker.issue(state, ev, property_req,
+ NULL, /* no keygen */
+ &property->handler,
+ NULL, /* no read iterator*/
+ &state->iter.variant,
+ NULL /* no key */);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_properties_get_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void
+sbus_properties_get_done(struct tevent_req *subreq)
+{
+ struct sbus_properties_get_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_properties_get_state);
+
+ ret = sbus_invoker_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ dbus_message_iter_abandon_container(state->iter.root,
+ &state->iter.variant);
+ goto done;
+ }
+
+ ret = sbus_close_iterator(state->iter.root, &state->iter.variant);
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t
+sbus_properties_get_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+struct sbus_properties_getall_state {
+ struct tevent_context *ev;
+ struct sbus_router *router;
+ struct sbus_request *sbus_req;
+ const char *interface_name;
+
+ struct {
+ DBusMessageIter *root;
+ DBusMessageIter dict;
+ DBusMessageIter entry;
+ } iter;
+
+ struct {
+ DBusMessage *msg;
+ DBusMessageIter write_iter;
+ } dummy;
+
+ const struct sbus_property *properties;
+ struct {
+ const struct sbus_property *current;
+ size_t index;
+ } property;
+};
+
+static errno_t sdap_properties_getall_next(struct tevent_req *req);
+static void sbus_properties_getall_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_properties_getall_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct sbus_router *router,
+ const char *interface_name,
+ DBusMessageIter *write_iterator)
+{
+ struct sbus_properties_getall_state *state;
+ struct sbus_interface *iface;
+ struct tevent_req *req;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_ALL, "Requesting all properties: %s of %s\n",
+ interface_name, sbus_req->path);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct sbus_properties_getall_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ iface = sbus_router_paths_lookup(router->paths, sbus_req->path,
+ interface_name);
+ if (iface == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unknown interface: %s\n", interface_name);
+ ret = ERR_SBUS_UNKNOWN_INTERFACE;
+ goto done;
+ }
+
+ state->ev = ev;
+ state->router = router;
+ state->sbus_req = sbus_req;
+ state->interface_name = interface_name;
+ state->properties = iface->properties;
+ state->iter.root = write_iterator;
+
+ /* Open array of <key, value> pairs. */
+ ret = sbus_open_dict(state->iter.root, &state->iter.dict);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sdap_properties_getall_next(req);
+ if (ret == EOK) {
+ /* There were no properties to return, we must close the container
+ * so an empty result is sent back to the caller. */
+ ret = sbus_close_iterator(state->iter.root, &state->iter.dict);
+ goto done;
+ } else if (ret != EAGAIN) {
+ dbus_message_iter_abandon_container(state->iter.root,
+ &state->iter.dict);
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static errno_t
+sdap_properties_getall_next(struct tevent_req *req)
+{
+ struct sbus_properties_getall_state *state;
+ const struct sbus_property *property;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sbus_properties_getall_state);
+
+ /* There are no properties available. */
+ if (state->properties == NULL) {
+ return EOK;
+ }
+
+ do {
+ property = &state->properties[state->property.index];
+ state->property.current = property;
+ state->property.index++;
+
+ /* There are no more properties available. */
+ if (property->name == NULL) {
+ return EOK;
+ }
+
+ /* We are interested only in readable properties. */
+ } while (property->access != SBUS_PROPERTY_READABLE);
+
+ /* Create new message that we will use to fake an Get method request.
+ * We will then copy its reply to the GetAll dictionary. */
+ ret = sbus_create_dummy_message(state, &state->dummy.msg,
+ &state->dummy.write_iter);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ subreq = sbus_properties_get_send(state, state->ev, state->sbus_req,
+ state->router, state->interface_name,
+ property->name, &state->dummy.write_iter);
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, sbus_properties_getall_done, req);
+
+ return EAGAIN;
+}
+
+static void sbus_properties_getall_done(struct tevent_req *subreq)
+{
+ struct sbus_properties_getall_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_properties_getall_state);
+
+ ret = sbus_properties_get_recv(state, subreq);
+ talloc_zfree(subreq);
+ switch (ret) {
+ case EOK:
+ ret = sbus_copy_message_to_dictionary(state->property.current->name,
+ state->dummy.msg,
+ &state->iter.dict);
+ if (ret != EOK) {
+ goto done;
+ }
+ break;
+ case ENOENT:
+ case EACCES:
+ case EPERM:
+ /* These errors are not fatal. We will just skip this property. */
+ DEBUG(SSSDBG_TRACE_FUNC, "Unable to get property %s.%s [%d]: %s\n",
+ state->interface_name, state->property.current->name,
+ ret, sss_strerror(ret));
+ break;
+ default:
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to get property %s.%s [%d]: %s\n",
+ state->interface_name, state->property.current->name,
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ dbus_message_unref(state->dummy.msg);
+ ret = sdap_properties_getall_next(req);
+ if (ret == EAGAIN) {
+ /* Continue with next property. */
+ return;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_close_iterator(state->iter.root, &state->iter.dict);
+ if (ret != EOK) {
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ dbus_message_iter_abandon_container(state->iter.root,
+ &state->iter.dict);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t
+sbus_properties_getall_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static errno_t
+sbus_properties_set_parse(TALLOC_CTX *mem_ctx,
+ DBusMessageIter *read_iter,
+ const char **_interface_name,
+ const char **_property_name)
+{
+ const char *interface_name;
+ const char *property_name;
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, read_iter, &interface_name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_s(mem_ctx, read_iter, &property_name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_interface_name = interface_name;
+ *_property_name = property_name;
+
+ return EOK;
+}
+
+struct sbus_properties_set_state {
+ DBusMessageIter variant_iterator;
+};
+
+static void sbus_properties_set_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_properties_set_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_request *sbus_req,
+ struct sbus_router *router,
+ DBusMessageIter *read_iterator)
+{
+ struct sbus_properties_set_state *state;
+ const struct sbus_property *property;
+ struct sbus_request *property_req;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ const char *interface_name;
+ const char *property_name;
+ char *signature;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_properties_set_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ ret = sbus_properties_set_parse(state, read_iterator, &interface_name,
+ &property_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to parse input message [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Setting property: %s.%s of %s\n",
+ interface_name, property_name, sbus_req->path);
+
+ ret = sbus_request_property(state, sbus_req->conn, router, sbus_req->sender,
+ SBUS_PROPERTY_WRITABLE, sbus_req->destination,
+ sbus_req->path, interface_name, property_name,
+ &property_req, &property);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_check_access(router->conn, property_req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (dbus_message_iter_get_arg_type(read_iterator) != DBUS_TYPE_VARIANT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Setter argument is not inside variant!\n");
+ ret = ERR_SBUS_INVALID_TYPE;
+ goto done;
+ }
+
+ /* Recurse into variant to get iterator for new property value. */
+ dbus_message_iter_recurse(read_iterator, &state->variant_iterator);
+ signature = dbus_message_iter_get_signature(&state->variant_iterator);
+ if (strcmp(property->type, signature) != 0) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ subreq = property->invoker.issue(state, ev, property_req,
+ NULL, /* no keygen */
+ &property->handler,
+ &state->variant_iterator,
+ NULL, /* no write iterator*/
+ NULL /* no key */);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_properties_set_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void
+sbus_properties_set_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+
+ ret = sbus_invoker_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t
+sbus_properties_set_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+errno_t
+sbus_register_properties(struct sbus_router *router)
+{
+
+ SBUS_INTERFACE(iface,
+ org_freedesktop_DBus_Properties,
+ SBUS_METHODS(
+ SBUS_ASYNC(METHOD, org_freedesktop_DBus_Properties, Get,
+ sbus_properties_get_send, sbus_properties_get_recv,
+ router),
+ SBUS_ASYNC(METHOD, org_freedesktop_DBus_Properties, Set,
+ sbus_properties_set_send, sbus_properties_set_recv,
+ router),
+ SBUS_ASYNC(METHOD, org_freedesktop_DBus_Properties, GetAll,
+ sbus_properties_getall_send, sbus_properties_getall_recv,
+ router)
+ ),
+ SBUS_WITHOUT_SIGNALS,
+ SBUS_WITHOUT_PROPERTIES
+ );
+
+ struct sbus_path paths[] = {
+ {"/", &iface},
+ {"/*", &iface},
+ {NULL, NULL}
+ };
+
+ return sbus_router_add_path_map(router, paths);
+}
diff --git a/src/sbus/interface/sbus_properties_parser.c b/src/sbus/interface/sbus_properties_parser.c
new file mode 100644
index 0000000..2a94493
--- /dev/null
+++ b/src/sbus/interface/sbus_properties_parser.c
@@ -0,0 +1,198 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <string.h>
+#include <talloc.h>
+
+#include "sbus/sbus_private.h"
+#include "sbus/interface/sbus_iterator_readers.h"
+#include "sbus/interface/sbus_iterator_writers.h"
+
+static errno_t
+sbus_parse_get_value(TALLOC_CTX *mem_ctx,
+ sbus_value_reader_fn reader,
+ sbus_value_reader_talloc_fn reader_talloc,
+ DBusMessageIter *iter,
+ void *_value_ptr)
+{
+ DBusMessageIter variant;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) {
+ return ERR_SBUS_INVALID_TYPE;
+ }
+
+ dbus_message_iter_recurse(iter, &variant);
+
+ if (reader != NULL) {
+ return reader(&variant, _value_ptr);
+ }
+
+ return reader_talloc(mem_ctx, &variant, _value_ptr);
+}
+
+errno_t
+sbus_parse_get_message(TALLOC_CTX *mem_ctx,
+ sbus_value_reader_fn reader,
+ sbus_value_reader_talloc_fn reader_talloc,
+ DBusMessage *msg,
+ void *_value_ptr)
+{
+ DBusMessageIter iterator;
+
+ dbus_message_iter_init(msg, &iterator);
+
+ return sbus_parse_get_value(mem_ctx, reader, reader_talloc,
+ &iterator, _value_ptr);
+}
+
+static errno_t
+sbus_parse_getall_name(struct sbus_parse_getall_table *table,
+ DBusMessageIter *dict_iter,
+ struct sbus_parse_getall_table **_property)
+{
+ const char *name;
+ int type;
+ int i;
+
+ type = dbus_message_iter_get_arg_type(dict_iter);
+ if (type != DBUS_TYPE_STRING) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type [%d]\n", type);
+ return ERR_SBUS_INVALID_TYPE;
+ }
+
+ dbus_message_iter_get_basic(dict_iter, &name);
+
+ for (i = 0; table[i].name != NULL; i++) {
+ if (strcmp(table[i].name, name) == 0) {
+ *_property = &table[i];
+ return EOK;
+ }
+ }
+
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unknown property [%s], skipping...\n", name);
+ *_property = NULL;
+
+ return EOK;
+}
+
+static errno_t
+sbus_parse_getall_dict_entry(TALLOC_CTX *mem_ctx,
+ struct sbus_parse_getall_table *table,
+ DBusMessageIter *dict_iter)
+{
+ struct sbus_parse_getall_table *property;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ ret = sbus_parse_getall_name(table, dict_iter, &property);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ dbret = dbus_message_iter_next(dict_iter);
+ if (!dbret) {
+ return ERR_SBUS_INVALID_TYPE;
+ }
+
+ if (property == NULL) {
+ return EOK;
+ }
+
+ ret = sbus_parse_get_value(mem_ctx, property->reader,
+ property->reader_talloc, dict_iter,
+ property->destination);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *(property->is_set) = true;
+
+ return EOK;
+}
+
+static errno_t
+sbus_parse_getall_array(TALLOC_CTX *mem_ctx,
+ struct sbus_parse_getall_table *table,
+ DBusMessageIter *array_iter)
+{
+ DBusMessageIter dict_iter;
+ errno_t ret;
+ int type;
+
+ do {
+ type = dbus_message_iter_get_arg_type(array_iter);
+
+ switch (type) {
+ case DBUS_TYPE_INVALID:
+ /* We have reached the end of the array. */
+ return EOK;
+ case DBUS_TYPE_DICT_ENTRY:
+ dbus_message_iter_recurse(array_iter, &dict_iter);
+ ret = sbus_parse_getall_dict_entry(mem_ctx, table, &dict_iter);
+ if (ret != EOK) {
+ return ret;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type [%d]\n", type);
+ return ERR_SBUS_INVALID_TYPE;
+ }
+ } while (dbus_message_iter_next(array_iter));
+
+ return EOK;
+}
+
+errno_t
+sbus_parse_getall_message(TALLOC_CTX *mem_ctx,
+ struct sbus_parse_getall_table *table,
+ DBusMessage *msg)
+{
+ DBusMessageIter array_iter;
+ DBusMessageIter iter;
+ errno_t ret;
+ int type;
+
+ dbus_message_iter_init(msg, &iter);
+
+ type = dbus_message_iter_get_arg_type(&iter);
+
+ switch (type) {
+ case DBUS_TYPE_INVALID:
+ /* Empty message. */
+ return EOK;
+ case DBUS_TYPE_ARRAY:
+ dbus_message_iter_recurse(&iter, &array_iter);
+ ret = sbus_parse_getall_array(mem_ctx, table, &array_iter);
+ if (ret != EOK) {
+ return ret;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type [%d]\n", type);
+ return ERR_SBUS_INVALID_TYPE;
+ }
+
+ if (dbus_message_iter_has_next(&iter)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid GetAll reply\n");
+ return ERR_SBUS_INVALID_TYPE;
+ }
+
+ return EOK;
+}
diff --git a/src/sbus/interface/sbus_std_signals.c b/src/sbus/interface/sbus_std_signals.c
new file mode 100644
index 0000000..c9afe44
--- /dev/null
+++ b/src/sbus/interface/sbus_std_signals.c
@@ -0,0 +1,65 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include "util/util.h"
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_private.h"
+#include "sbus/interface_dbus/sbus_dbus_server.h"
+
+static errno_t
+sbus_name_owner_changed(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_connection *conn,
+ const char *name,
+ const char *new_owner,
+ const char *old_owner)
+{
+ DEBUG(SSSDBG_TRACE_ALL, "Name of owner %s has changed from "
+ "[%s] to [%s]\n", name, old_owner, new_owner);
+
+ /* Delete any existing sender information since it is now obsolete. */
+ sbus_senders_delete(conn->senders, name);
+
+ return EOK;
+}
+
+static errno_t
+sbus_name_acquired(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_connection *conn,
+ const char *name)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "D-Bus name acquired: %s\n", name);
+
+ return EOK;
+}
+
+errno_t
+sbus_register_standard_signals(struct sbus_connection *conn)
+{
+ struct sbus_listener listeners[] = SBUS_LISTENERS(
+ SBUS_LISTEN_SYNC(org_freedesktop_DBus, NameOwnerChanged,
+ DBUS_PATH_DBUS, sbus_name_owner_changed, conn),
+ SBUS_LISTEN_SYNC(org_freedesktop_DBus, NameAcquired,
+ DBUS_PATH_DBUS, sbus_name_acquired, conn)
+ );
+
+ return sbus_router_listen_map(conn, listeners);
+}
diff --git a/src/sbus/interface_dbus/sbus_dbus_arguments.c b/src/sbus/interface_dbus/sbus_dbus_arguments.c
new file mode 100644
index 0000000..ee9039c
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_arguments.c
@@ -0,0 +1,271 @@
+/*
+ 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/interface/sbus_iterator_readers.h"
+#include "sbus/interface/sbus_iterator_writers.h"
+#include "sbus/interface_dbus/sbus_dbus_arguments.h"
+
+errno_t _sbus_dbus_invoker_read_as
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_as *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_as(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_write_as
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_as *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_as(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_read_b
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_b *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_b(iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_write_b
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_b *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_b(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_read_s
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_s *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_write_s
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_s *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_s(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_read_ss
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_ss *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_write_ss
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_ss *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_s(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_write_s(iter, args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_read_sss
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_sss *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg2);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_write_sss
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_sss *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_s(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_write_s(iter, args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_write_s(iter, args->arg2);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_read_su
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_su *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_s(mem_ctx, iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_read_u(iter, &args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_write_su
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_su *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_s(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_iterator_write_u(iter, args->arg1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_read_u
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_u *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_read_u(iter, &args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t _sbus_dbus_invoker_write_u
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_u *args)
+{
+ errno_t ret;
+
+ ret = sbus_iterator_write_u(iter, args->arg0);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
diff --git a/src/sbus/interface_dbus/sbus_dbus_arguments.h b/src/sbus/interface_dbus/sbus_dbus_arguments.h
new file mode 100644
index 0000000..13512fe
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_arguments.h
@@ -0,0 +1,139 @@
+/*
+ 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 _SBUS_DBUS_ARGUMENTS_H_
+#define _SBUS_DBUS_ARGUMENTS_H_
+
+#include <errno.h>
+#include <stdint.h>
+#include <talloc.h>
+#include <stdbool.h>
+#include <dbus/dbus.h>
+
+
+struct _sbus_dbus_invoker_args_as {
+ const char ** arg0;
+};
+
+errno_t
+_sbus_dbus_invoker_read_as
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_as *args);
+
+errno_t
+_sbus_dbus_invoker_write_as
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_as *args);
+
+struct _sbus_dbus_invoker_args_b {
+ bool arg0;
+};
+
+errno_t
+_sbus_dbus_invoker_read_b
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_b *args);
+
+errno_t
+_sbus_dbus_invoker_write_b
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_b *args);
+
+struct _sbus_dbus_invoker_args_s {
+ const char * arg0;
+};
+
+errno_t
+_sbus_dbus_invoker_read_s
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_s *args);
+
+errno_t
+_sbus_dbus_invoker_write_s
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_s *args);
+
+struct _sbus_dbus_invoker_args_ss {
+ const char * arg0;
+ const char * arg1;
+};
+
+errno_t
+_sbus_dbus_invoker_read_ss
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_ss *args);
+
+errno_t
+_sbus_dbus_invoker_write_ss
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_ss *args);
+
+struct _sbus_dbus_invoker_args_sss {
+ const char * arg0;
+ const char * arg1;
+ const char * arg2;
+};
+
+errno_t
+_sbus_dbus_invoker_read_sss
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_sss *args);
+
+errno_t
+_sbus_dbus_invoker_write_sss
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_sss *args);
+
+struct _sbus_dbus_invoker_args_su {
+ const char * arg0;
+ uint32_t arg1;
+};
+
+errno_t
+_sbus_dbus_invoker_read_su
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_su *args);
+
+errno_t
+_sbus_dbus_invoker_write_su
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_su *args);
+
+struct _sbus_dbus_invoker_args_u {
+ uint32_t arg0;
+};
+
+errno_t
+_sbus_dbus_invoker_read_u
+ (TALLOC_CTX *mem_ctx,
+ DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_u *args);
+
+errno_t
+_sbus_dbus_invoker_write_u
+ (DBusMessageIter *iter,
+ struct _sbus_dbus_invoker_args_u *args);
+
+#endif /* _SBUS_DBUS_ARGUMENTS_H_ */
diff --git a/src/sbus/interface_dbus/sbus_dbus_client_async.c b/src/sbus/interface_dbus/sbus_dbus_client_async.c
new file mode 100644
index 0000000..3551cd9
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_client_async.c
@@ -0,0 +1,751 @@
+/*
+ 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/sbus_private.h"
+#include "sbus/interface/sbus_iterator_readers.h"
+#include "sbus/interface_dbus/sbus_dbus_client_async.h"
+#include "sbus/interface_dbus/sbus_dbus_arguments.h"
+#include "sbus/interface_dbus/sbus_dbus_keygens.h"
+#include "sbus/interface_dbus/sbus_dbus_client_properties.h"
+
+struct sbus_method_in__out_s_state {
+ struct _sbus_dbus_invoker_args_s *out;
+};
+
+static void sbus_method_in__out_s_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_method_in__out_s_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ sbus_invoker_keygen keygen,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method)
+{
+ struct sbus_method_in__out_s_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_method_in__out_s_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->out = talloc_zero(state, struct _sbus_dbus_invoker_args_s);
+ if (state->out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ subreq = sbus_call_method_send(state, conn, NULL, keygen, NULL,
+ bus, path, iface, method, NULL);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_method_in__out_s_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__out_s_done(struct tevent_req *subreq)
+{
+ struct sbus_method_in__out_s_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__out_s_state);
+
+ ret = sbus_call_method_recv(state, subreq, &reply);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sbus_read_output(state->out, reply, (sbus_invoker_reader_fn)_sbus_dbus_invoker_read_s, state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+sbus_method_in__out_s_recv
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ** _arg0)
+{
+ struct sbus_method_in__out_s_state *state;
+ state = tevent_req_data(req, struct sbus_method_in__out_s_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_arg0 = talloc_steal(mem_ctx, state->out->arg0);
+
+ return EOK;
+}
+
+struct sbus_method_in_raw_out__state {
+ int dummy;
+};
+
+static void sbus_method_in_raw_out__done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_method_in_raw_out__send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ DBusMessage *raw_message)
+{
+ struct sbus_method_in_raw_out__state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_method_in_raw_out__state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+
+ 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);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_method_in_raw_out__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_raw_out__done(struct tevent_req *subreq)
+{
+ struct sbus_method_in_raw_out__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_raw_out__state);
+
+ ret = sbus_call_method_recv(state, subreq, &reply);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+sbus_method_in_raw_out__recv
+ (struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+struct sbus_method_in_s_out_raw_state {
+ struct _sbus_dbus_invoker_args_s in;
+ DBusMessage *reply;
+};
+
+static void sbus_method_in_s_out_raw_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_method_in_s_out_raw_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ sbus_invoker_keygen keygen,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0)
+{
+ struct sbus_method_in_s_out_raw_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_method_in_s_out_raw_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->in.arg0 = arg0;
+
+ subreq = sbus_call_method_send(state, conn, NULL, keygen,
+ (sbus_invoker_writer_fn)_sbus_dbus_invoker_write_s,
+ bus, path, iface, method, &state->in);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_method_in_s_out_raw_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_s_out_raw_done(struct tevent_req *subreq)
+{
+ struct sbus_method_in_s_out_raw_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_s_out_raw_state);
+
+ ret = sbus_call_method_recv(state, subreq, &reply);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->reply = reply;
+
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+sbus_method_in_s_out_raw_recv
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ errno_t ret;
+ struct sbus_method_in_s_out_raw_state *state;
+ state = tevent_req_data(req, struct sbus_method_in_s_out_raw_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ /* 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;
+
+ return EOK;
+}
+
+struct sbus_method_in_s_out_u_state {
+ struct _sbus_dbus_invoker_args_s in;
+ struct _sbus_dbus_invoker_args_u *out;
+};
+
+static void sbus_method_in_s_out_u_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_method_in_s_out_u_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ sbus_invoker_keygen keygen,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0)
+{
+ struct sbus_method_in_s_out_u_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_method_in_s_out_u_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->out = talloc_zero(state, struct _sbus_dbus_invoker_args_u);
+ if (state->out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->in.arg0 = arg0;
+
+ subreq = sbus_call_method_send(state, conn, NULL, keygen,
+ (sbus_invoker_writer_fn)_sbus_dbus_invoker_write_s,
+ bus, path, iface, method, &state->in);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_method_in_s_out_u_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_s_out_u_done(struct tevent_req *subreq)
+{
+ struct sbus_method_in_s_out_u_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_s_out_u_state);
+
+ ret = sbus_call_method_recv(state, subreq, &reply);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sbus_read_output(state->out, reply, (sbus_invoker_reader_fn)_sbus_dbus_invoker_read_u, state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+sbus_method_in_s_out_u_recv
+ (struct tevent_req *req,
+ uint32_t* _arg0)
+{
+ struct sbus_method_in_s_out_u_state *state;
+ state = tevent_req_data(req, struct sbus_method_in_s_out_u_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_arg0 = state->out->arg0;
+
+ return EOK;
+}
+
+struct sbus_method_in_ss_out_raw_state {
+ struct _sbus_dbus_invoker_args_ss in;
+ DBusMessage *reply;
+};
+
+static void sbus_method_in_ss_out_raw_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_method_in_ss_out_raw_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ sbus_invoker_keygen keygen,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ const char * arg1)
+{
+ struct sbus_method_in_ss_out_raw_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_method_in_ss_out_raw_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->in.arg0 = arg0;
+ state->in.arg1 = arg1;
+
+ subreq = sbus_call_method_send(state, conn, NULL, keygen,
+ (sbus_invoker_writer_fn)_sbus_dbus_invoker_write_ss,
+ bus, path, iface, method, &state->in);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_method_in_ss_out_raw_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_ss_out_raw_done(struct tevent_req *subreq)
+{
+ struct sbus_method_in_ss_out_raw_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_ss_out_raw_state);
+
+ ret = sbus_call_method_recv(state, subreq, &reply);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->reply = reply;
+
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+sbus_method_in_ss_out_raw_recv
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ errno_t ret;
+ struct sbus_method_in_ss_out_raw_state *state;
+ state = tevent_req_data(req, struct sbus_method_in_ss_out_raw_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ /* 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;
+
+ return EOK;
+}
+
+struct sbus_method_in_su_out_u_state {
+ struct _sbus_dbus_invoker_args_su in;
+ struct _sbus_dbus_invoker_args_u *out;
+};
+
+static void sbus_method_in_su_out_u_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+sbus_method_in_su_out_u_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ sbus_invoker_keygen keygen,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ uint32_t arg1)
+{
+ struct sbus_method_in_su_out_u_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_method_in_su_out_u_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->out = talloc_zero(state, struct _sbus_dbus_invoker_args_u);
+ if (state->out == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for output parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->in.arg0 = arg0;
+ state->in.arg1 = arg1;
+
+ subreq = sbus_call_method_send(state, conn, NULL, keygen,
+ (sbus_invoker_writer_fn)_sbus_dbus_invoker_write_su,
+ bus, path, iface, method, &state->in);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_method_in_su_out_u_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_su_out_u_done(struct tevent_req *subreq)
+{
+ struct sbus_method_in_su_out_u_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_su_out_u_state);
+
+ ret = sbus_call_method_recv(state, subreq, &reply);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sbus_read_output(state->out, reply, (sbus_invoker_reader_fn)_sbus_dbus_invoker_read_u, state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static errno_t
+sbus_method_in_su_out_u_recv
+ (struct tevent_req *req,
+ uint32_t* _arg0)
+{
+ struct sbus_method_in_su_out_u_state *state;
+ state = tevent_req_data(req, struct sbus_method_in_su_out_u_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ *_arg0 = state->out->arg0;
+
+ return EOK;
+}
+
+struct tevent_req *
+sbus_call_DBus_GetConnectionUnixUser_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name)
+{
+ return sbus_method_in_s_out_u_send(mem_ctx, conn, _sbus_dbus_key_s_0,
+ busname, object_path, "org.freedesktop.DBus", "GetConnectionUnixUser", arg_name);
+}
+
+errno_t
+sbus_call_DBus_GetConnectionUnixUser_recv
+ (struct tevent_req *req,
+ uint32_t* _uid)
+{
+ return sbus_method_in_s_out_u_recv(req, _uid);
+}
+
+struct tevent_req *
+sbus_call_DBus_Hello_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path)
+{
+ return sbus_method_in__out_s_send(mem_ctx, conn, NULL,
+ busname, object_path, "org.freedesktop.DBus", "Hello");
+}
+
+errno_t
+sbus_call_DBus_Hello_recv
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ** _name)
+{
+ return sbus_method_in__out_s_recv(mem_ctx, req, _name);
+}
+
+struct tevent_req *
+sbus_call_DBus_RequestName_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ uint32_t arg_flags)
+{
+ return sbus_method_in_su_out_u_send(mem_ctx, conn, NULL,
+ busname, object_path, "org.freedesktop.DBus", "RequestName", arg_name, arg_flags);
+}
+
+errno_t
+sbus_call_DBus_RequestName_recv
+ (struct tevent_req *req,
+ uint32_t* _result)
+{
+ return sbus_method_in_su_out_u_recv(req, _result);
+}
+
+struct tevent_req *
+sbus_call_DBusProperties_Get_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_interface_name,
+ const char * arg_property_name)
+{
+ return sbus_method_in_ss_out_raw_send(mem_ctx, conn, _sbus_dbus_key_ss_0_1,
+ busname, object_path, "org.freedesktop.DBus.Properties", "Get", arg_interface_name, arg_property_name);
+}
+
+errno_t
+sbus_call_DBusProperties_Get_recv
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ return sbus_method_in_ss_out_raw_recv(mem_ctx, req, _reply);
+}
+
+struct tevent_req *
+sbus_call_DBusProperties_GetAll_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_interface_name)
+{
+ return sbus_method_in_s_out_raw_send(mem_ctx, conn, _sbus_dbus_key_s_0,
+ busname, object_path, "org.freedesktop.DBus.Properties", "GetAll", arg_interface_name);
+}
+
+errno_t
+sbus_call_DBusProperties_GetAll_recv
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ return sbus_method_in_s_out_raw_recv(mem_ctx, req, _reply);
+}
+
+struct tevent_req *
+sbus_call_DBusProperties_Set_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ DBusMessage *raw_message)
+{
+ return sbus_method_in_raw_out__send(mem_ctx, conn, raw_message);
+}
+
+errno_t
+sbus_call_DBusProperties_Set_recv
+ (struct tevent_req *req)
+{
+ return sbus_method_in_raw_out__recv(req);
+}
diff --git a/src/sbus/interface_dbus/sbus_dbus_client_async.h b/src/sbus/interface_dbus/sbus_dbus_client_async.h
new file mode 100644
index 0000000..3c02a72
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_client_async.h
@@ -0,0 +1,109 @@
+/*
+ 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 _SBUS_DBUS_CLIENT_ASYNC_H_
+#define _SBUS_DBUS_CLIENT_ASYNC_H_
+
+#include <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "sbus/sbus.h"
+#include "sbus/interface_dbus/sbus_dbus_client_properties.h"
+
+struct tevent_req *
+sbus_call_DBus_GetConnectionUnixUser_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name);
+
+errno_t
+sbus_call_DBus_GetConnectionUnixUser_recv
+ (struct tevent_req *req,
+ uint32_t* _uid);
+
+struct tevent_req *
+sbus_call_DBus_Hello_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path);
+
+errno_t
+sbus_call_DBus_Hello_recv
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ const char ** _name);
+
+struct tevent_req *
+sbus_call_DBus_RequestName_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_name,
+ uint32_t arg_flags);
+
+errno_t
+sbus_call_DBus_RequestName_recv
+ (struct tevent_req *req,
+ uint32_t* _result);
+
+struct tevent_req *
+sbus_call_DBusProperties_Get_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_interface_name,
+ const char * arg_property_name);
+
+errno_t
+sbus_call_DBusProperties_Get_recv
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply);
+
+struct tevent_req *
+sbus_call_DBusProperties_GetAll_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_interface_name);
+
+errno_t
+sbus_call_DBusProperties_GetAll_recv
+ (TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply);
+
+struct tevent_req *
+sbus_call_DBusProperties_Set_send
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ DBusMessage *raw_message);
+
+errno_t
+sbus_call_DBusProperties_Set_recv
+ (struct tevent_req *req);
+
+#endif /* _SBUS_DBUS_CLIENT_ASYNC_H_ */
diff --git a/src/sbus/interface_dbus/sbus_dbus_client_properties.h b/src/sbus/interface_dbus/sbus_dbus_client_properties.h
new file mode 100644
index 0000000..7a9dd27
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_client_properties.h
@@ -0,0 +1,27 @@
+/*
+ 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 _SBUS_DBUS_CLIENT_PROPERTIES_H_
+#define _SBUS_DBUS_CLIENT_PROPERTIES_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+
+#endif /* _SBUS_DBUS_CLIENT_PROPERTIES_H_ */
diff --git a/src/sbus/interface_dbus/sbus_dbus_client_sync.c b/src/sbus/interface_dbus/sbus_dbus_client_sync.c
new file mode 100644
index 0000000..1be49d8
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_client_sync.c
@@ -0,0 +1,215 @@
+/*
+ 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/sbus_sync.h"
+#include "sbus/sbus_sync_private.h"
+#include "sbus/sbus_message.h"
+#include "sbus/interface/sbus_iterator_readers.h"
+#include "sbus/interface_dbus/sbus_dbus_client_sync.h"
+#include "sbus/interface_dbus/sbus_dbus_arguments.h"
+#include "sbus/interface_dbus/sbus_dbus_client_properties.h"
+
+static errno_t
+sbus_method_in_raw_out_
+ (struct sbus_sync_connection *conn,
+ DBusMessage *raw_message)
+{
+ TALLOC_CTX *tmp_ctx;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+
+ 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);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_s_out_raw
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ DBusMessage **_reply)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_dbus_invoker_args_s in;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ in.arg0 = arg0;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_dbus_invoker_write_s,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* 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;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sbus_method_in_ss_out_raw
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ const char * arg0,
+ const char * arg1,
+ DBusMessage **_reply)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct _sbus_dbus_invoker_args_ss in;
+ DBusMessage *reply;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ in.arg0 = arg0;
+ in.arg1 = arg1;
+
+ ret = sbus_sync_call_method(tmp_ctx, conn, NULL,
+ (sbus_invoker_writer_fn)_sbus_dbus_invoker_write_ss,
+ bus, path, iface, method, &in, &reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* 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;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+sbus_call_DBusProperties_Get
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_interface_name,
+ const char * arg_property_name,
+ DBusMessage **_reply)
+{
+ return sbus_method_in_ss_out_raw(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.DBus.Properties", "Get", arg_interface_name, arg_property_name,
+ _reply);
+}
+
+errno_t
+sbus_call_DBusProperties_GetAll
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_interface_name,
+ DBusMessage **_reply)
+{
+ return sbus_method_in_s_out_raw(mem_ctx, conn,
+ busname, object_path, "org.freedesktop.DBus.Properties", "GetAll", arg_interface_name,
+ _reply);
+}
+
+errno_t
+sbus_call_DBusProperties_Set
+ (struct sbus_sync_connection *conn,
+ DBusMessage *raw_message)
+{
+ return sbus_method_in_raw_out_(conn,
+ raw_message);
+}
diff --git a/src/sbus/interface_dbus/sbus_dbus_client_sync.h b/src/sbus/interface_dbus/sbus_dbus_client_sync.h
new file mode 100644
index 0000000..c6c34e3
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_client_sync.h
@@ -0,0 +1,54 @@
+/*
+ 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 _SBUS_DBUS_CLIENT_SYNC_H_
+#define _SBUS_DBUS_CLIENT_SYNC_H_
+
+#include <errno.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "sbus/sbus_sync.h"
+#include "sbus/interface_dbus/sbus_dbus_client_properties.h"
+
+errno_t
+sbus_call_DBusProperties_Get
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_interface_name,
+ const char * arg_property_name,
+ DBusMessage **_reply);
+
+errno_t
+sbus_call_DBusProperties_GetAll
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *busname,
+ const char *object_path,
+ const char * arg_interface_name,
+ DBusMessage **_reply);
+
+errno_t
+sbus_call_DBusProperties_Set
+ (struct sbus_sync_connection *conn,
+ DBusMessage *raw_message);
+
+#endif /* _SBUS_DBUS_CLIENT_SYNC_H_ */
diff --git a/src/sbus/interface_dbus/sbus_dbus_interface.h b/src/sbus/interface_dbus/sbus_dbus_interface.h
new file mode 100644
index 0000000..ba49cbb
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_interface.h
@@ -0,0 +1,514 @@
+/*
+ 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 _SBUS_DBUS_INTERFACE_H_
+#define _SBUS_DBUS_INTERFACE_H_
+
+#include "sbus/sbus_interface_declarations.h"
+#include "sbus/interface_dbus/sbus_dbus_invokers.h"
+#include "sbus/interface_dbus/sbus_dbus_symbols.h"
+#include "sbus/interface_dbus/sbus_dbus_keygens.h"
+
+/* Interface: org.freedesktop.DBus */
+#define SBUS_IFACE_org_freedesktop_DBus(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.DBus", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.DBus.AddMatch */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_AddMatch(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *); \
+ sbus_method_sync("AddMatch", \
+ &_sbus_dbus_args_org_freedesktop_DBus_AddMatch, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out__send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_AddMatch(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_method_async("AddMatch", \
+ &_sbus_dbus_args_org_freedesktop_DBus_AddMatch, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out__send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.GetConnectionUnixProcessID */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_GetConnectionUnixProcessID(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, uint32_t*); \
+ sbus_method_sync("GetConnectionUnixProcessID", \
+ &_sbus_dbus_args_org_freedesktop_DBus_GetConnectionUnixProcessID, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_u_send, \
+ _sbus_dbus_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_GetConnectionUnixProcessID(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_method_async("GetConnectionUnixProcessID", \
+ &_sbus_dbus_args_org_freedesktop_DBus_GetConnectionUnixProcessID, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_u_send, \
+ _sbus_dbus_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.GetConnectionUnixUser */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_GetConnectionUnixUser(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, uint32_t*); \
+ sbus_method_sync("GetConnectionUnixUser", \
+ &_sbus_dbus_args_org_freedesktop_DBus_GetConnectionUnixUser, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_u_send, \
+ _sbus_dbus_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_GetConnectionUnixUser(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_method_async("GetConnectionUnixUser", \
+ &_sbus_dbus_args_org_freedesktop_DBus_GetConnectionUnixUser, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_u_send, \
+ _sbus_dbus_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.GetId */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_GetId(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_method_sync("GetId", \
+ &_sbus_dbus_args_org_freedesktop_DBus_GetId, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_s_send, \
+ _sbus_dbus_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_GetId(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("GetId", \
+ &_sbus_dbus_args_org_freedesktop_DBus_GetId, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_s_send, \
+ _sbus_dbus_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.GetNameOwner */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_GetNameOwner(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char **); \
+ sbus_method_sync("GetNameOwner", \
+ &_sbus_dbus_args_org_freedesktop_DBus_GetNameOwner, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_s_send, \
+ _sbus_dbus_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_GetNameOwner(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("GetNameOwner", \
+ &_sbus_dbus_args_org_freedesktop_DBus_GetNameOwner, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_s_send, \
+ _sbus_dbus_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.Hello */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_Hello(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_method_sync("Hello", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Hello, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_s_send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_Hello(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("Hello", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Hello, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_s_send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.ListActivatableNames */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_ListActivatableNames(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_method_sync("ListActivatableNames", \
+ &_sbus_dbus_args_org_freedesktop_DBus_ListActivatableNames, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_as_send, \
+ _sbus_dbus_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_ListActivatableNames(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListActivatableNames", \
+ &_sbus_dbus_args_org_freedesktop_DBus_ListActivatableNames, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_as_send, \
+ _sbus_dbus_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.ListNames */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_ListNames(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char ***); \
+ sbus_method_sync("ListNames", \
+ &_sbus_dbus_args_org_freedesktop_DBus_ListNames, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_as_send, \
+ _sbus_dbus_key_, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_ListNames(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListNames", \
+ &_sbus_dbus_args_org_freedesktop_DBus_ListNames, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_as_send, \
+ _sbus_dbus_key_, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.ListQueuedOwners */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_ListQueuedOwners(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char ***); \
+ sbus_method_sync("ListQueuedOwners", \
+ &_sbus_dbus_args_org_freedesktop_DBus_ListQueuedOwners, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_as_send, \
+ _sbus_dbus_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_ListQueuedOwners(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), const char ***); \
+ sbus_method_async("ListQueuedOwners", \
+ &_sbus_dbus_args_org_freedesktop_DBus_ListQueuedOwners, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_as_send, \
+ _sbus_dbus_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.NameHasOwner */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_NameHasOwner(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, bool*); \
+ sbus_method_sync("NameHasOwner", \
+ &_sbus_dbus_args_org_freedesktop_DBus_NameHasOwner, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_b_send, \
+ _sbus_dbus_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_NameHasOwner(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), bool*); \
+ sbus_method_async("NameHasOwner", \
+ &_sbus_dbus_args_org_freedesktop_DBus_NameHasOwner, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_b_send, \
+ _sbus_dbus_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.ReleaseName */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_ReleaseName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, uint32_t*); \
+ sbus_method_sync("ReleaseName", \
+ &_sbus_dbus_args_org_freedesktop_DBus_ReleaseName, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_u_send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_ReleaseName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_method_async("ReleaseName", \
+ &_sbus_dbus_args_org_freedesktop_DBus_ReleaseName, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_u_send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.RemoveMatch */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_RemoveMatch(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *); \
+ sbus_method_sync("RemoveMatch", \
+ &_sbus_dbus_args_org_freedesktop_DBus_RemoveMatch, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out__send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_RemoveMatch(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_method_async("RemoveMatch", \
+ &_sbus_dbus_args_org_freedesktop_DBus_RemoveMatch, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out__send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.RequestName */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_RequestName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, uint32_t, uint32_t*); \
+ sbus_method_sync("RequestName", \
+ &_sbus_dbus_args_org_freedesktop_DBus_RequestName, \
+ NULL, \
+ _sbus_dbus_invoke_in_su_out_u_send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_RequestName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_method_async("RequestName", \
+ &_sbus_dbus_args_org_freedesktop_DBus_RequestName, \
+ NULL, \
+ _sbus_dbus_invoke_in_su_out_u_send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.StartServiceByName */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_StartServiceByName(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, uint32_t, uint32_t*); \
+ sbus_method_sync("StartServiceByName", \
+ &_sbus_dbus_args_org_freedesktop_DBus_StartServiceByName, \
+ NULL, \
+ _sbus_dbus_invoke_in_su_out_u_send, \
+ _sbus_dbus_key_su_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_StartServiceByName(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, uint32_t); \
+ SBUS_CHECK_RECV((handler_recv), uint32_t*); \
+ sbus_method_async("StartServiceByName", \
+ &_sbus_dbus_args_org_freedesktop_DBus_StartServiceByName, \
+ NULL, \
+ _sbus_dbus_invoke_in_su_out_u_send, \
+ _sbus_dbus_key_su_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Signal: org.freedesktop.DBus.NameAcquired */
+#define SBUS_SIGNAL_EMITS_org_freedesktop_DBus_NameAcquired() ({ \
+ sbus_signal("NameAcquired", \
+ _sbus_dbus_args_org_freedesktop_DBus_NameAcquired, \
+ NULL); \
+})
+
+#define SBUS_SIGNAL_SYNC_org_freedesktop_DBus_NameAcquired(path, handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *); \
+ sbus_listener_sync("org.freedesktop.DBus", "NameAcquired", (path), \
+ _sbus_dbus_invoke_in_s_out__send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_SIGNAL_ASYNC_org_freedesktop_DBus_NameAcquired(path, handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_listener_async("org.freedesktop.DBus", "NameAcquired", (path), \
+ _sbus_dbus_invoke_in_s_out__send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Signal: org.freedesktop.DBus.NameLost */
+#define SBUS_SIGNAL_EMITS_org_freedesktop_DBus_NameLost() ({ \
+ sbus_signal("NameLost", \
+ _sbus_dbus_args_org_freedesktop_DBus_NameLost, \
+ NULL); \
+})
+
+#define SBUS_SIGNAL_SYNC_org_freedesktop_DBus_NameLost(path, handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *); \
+ sbus_listener_sync("org.freedesktop.DBus", "NameLost", (path), \
+ _sbus_dbus_invoke_in_s_out__send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_SIGNAL_ASYNC_org_freedesktop_DBus_NameLost(path, handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_listener_async("org.freedesktop.DBus", "NameLost", (path), \
+ _sbus_dbus_invoke_in_s_out__send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Signal: org.freedesktop.DBus.NameOwnerChanged */
+#define SBUS_SIGNAL_EMITS_org_freedesktop_DBus_NameOwnerChanged() ({ \
+ sbus_signal("NameOwnerChanged", \
+ _sbus_dbus_args_org_freedesktop_DBus_NameOwnerChanged, \
+ NULL); \
+})
+
+#define SBUS_SIGNAL_SYNC_org_freedesktop_DBus_NameOwnerChanged(path, handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char *, const char *); \
+ sbus_listener_sync("org.freedesktop.DBus", "NameOwnerChanged", (path), \
+ _sbus_dbus_invoke_in_sss_out__send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_SIGNAL_ASYNC_org_freedesktop_DBus_NameOwnerChanged(path, handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, const char *, const char *); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_listener_async("org.freedesktop.DBus", "NameOwnerChanged", (path), \
+ _sbus_dbus_invoke_in_sss_out__send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.DBus.Introspectable */
+#define SBUS_IFACE_org_freedesktop_DBus_Introspectable(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.DBus.Introspectable", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.DBus.Introspectable.Introspect */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_Introspectable_Introspect(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char **); \
+ sbus_method_sync("Introspect", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Introspectable_Introspect, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_s_send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_Introspectable_Introspect(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data)); \
+ SBUS_CHECK_RECV((handler_recv), const char **); \
+ sbus_method_async("Introspect", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Introspectable_Introspect, \
+ NULL, \
+ _sbus_dbus_invoke_in__out_s_send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Interface: org.freedesktop.DBus.Properties */
+#define SBUS_IFACE_org_freedesktop_DBus_Properties(methods, signals, properties) ({ \
+ sbus_interface("org.freedesktop.DBus.Properties", NULL, \
+ (methods), (signals), (properties)); \
+})
+
+/* Method: org.freedesktop.DBus.Properties.Get */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_Properties_Get(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, const char *, DBusMessageIter *); \
+ sbus_method_sync("Get", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Properties_Get, \
+ NULL, \
+ _sbus_dbus_invoke_in_ss_out_raw_send, \
+ _sbus_dbus_key_ss_0_1, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_Properties_Get(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, const char *, DBusMessageIter *); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_method_async("Get", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Properties_Get, \
+ NULL, \
+ _sbus_dbus_invoke_in_ss_out_raw_send, \
+ _sbus_dbus_key_ss_0_1, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.Properties.GetAll */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_Properties_GetAll(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), const char *, DBusMessageIter *); \
+ sbus_method_sync("GetAll", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Properties_GetAll, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_raw_send, \
+ _sbus_dbus_key_s_0, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_Properties_GetAll(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), const char *, DBusMessageIter *); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_method_async("GetAll", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Properties_GetAll, \
+ NULL, \
+ _sbus_dbus_invoke_in_s_out_raw_send, \
+ _sbus_dbus_key_s_0, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+/* Method: org.freedesktop.DBus.Properties.Set */
+#define SBUS_METHOD_SYNC_org_freedesktop_DBus_Properties_Set(handler, data) ({ \
+ SBUS_CHECK_SYNC((handler), (data), DBusMessageIter *); \
+ sbus_method_sync("Set", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Properties_Set, \
+ NULL, \
+ _sbus_dbus_invoke_in_raw_out__send, \
+ NULL, \
+ (handler), (data)); \
+})
+
+#define SBUS_METHOD_ASYNC_org_freedesktop_DBus_Properties_Set(handler_send, handler_recv, data) ({ \
+ SBUS_CHECK_SEND((handler_send), (data), DBusMessageIter *); \
+ SBUS_CHECK_RECV((handler_recv)); \
+ sbus_method_async("Set", \
+ &_sbus_dbus_args_org_freedesktop_DBus_Properties_Set, \
+ NULL, \
+ _sbus_dbus_invoke_in_raw_out__send, \
+ NULL, \
+ (handler_send), (handler_recv), (data)); \
+})
+
+#endif /* _SBUS_DBUS_INTERFACE_H_ */
diff --git a/src/sbus/interface_dbus/sbus_dbus_invokers.c b/src/sbus/interface_dbus/sbus_dbus_invokers.c
new file mode 100644
index 0000000..effa336
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_invokers.c
@@ -0,0 +1,2145 @@
+/*
+ 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/sbus_private.h"
+#include "sbus/sbus_interface_declarations.h"
+#include "sbus/interface_dbus/sbus_dbus_arguments.h"
+#include "sbus/interface_dbus/sbus_dbus_invokers.h"
+
+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;
+}
+
+struct _sbus_dbus_invoke_in__out_as_state {
+ struct _sbus_dbus_invoker_args_as out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char ***);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in__out_as_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in__out_as_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in__out_as_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_dbus_invoke_in__out_as_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in__out_as_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;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in__out_as_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_dbus_invoke_in__out_as_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in__out_as_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_dbus_invoke_in__out_as_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, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_write_as(state->write_iterator, &state->out);
+ 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);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in__out_as_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_dbus_invoke_in__out_as_done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in__out_as_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_dbus_invoke_in__out_as_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_dbus_invoker_write_as(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in__out_s_state {
+ struct _sbus_dbus_invoker_args_s out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char **);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in__out_s_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in__out_s_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in__out_s_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_dbus_invoke_in__out_s_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in__out_s_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;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in__out_s_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_dbus_invoke_in__out_s_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in__out_s_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_dbus_invoke_in__out_s_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, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_write_s(state->write_iterator, &state->out);
+ 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);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in__out_s_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_dbus_invoke_in__out_s_done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in__out_s_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_dbus_invoke_in__out_s_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_dbus_invoker_write_s(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_raw_out__state {
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, DBusMessageIter *);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, DBusMessageIter *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_raw_out__step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_raw_out__done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_raw_out__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_dbus_invoke_in_raw_out__state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_raw_out__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;
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_raw_out__step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_dbus_invoke_in_raw_out__step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_raw_out__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_dbus_invoke_in_raw_out__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, state->read_iterator);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ 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, state->read_iterator);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_raw_out__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_dbus_invoke_in_raw_out__done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_raw_out__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_dbus_invoke_in_raw_out__state);
+
+ ret = state->handler.recv(state, subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_s_out__state {
+ struct _sbus_dbus_invoker_args_s *in;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_s_out__step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_s_out__done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_s_out__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_dbus_invoke_in_s_out__state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_s_out__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;
+
+ state->in = talloc_zero(state, struct _sbus_dbus_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_s_out__step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_dbus_invoke_in_s_out__step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_s_out__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_dbus_invoke_in_s_out__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, state->in->arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ 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, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_s_out__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_dbus_invoke_in_s_out__done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_s_out__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_dbus_invoke_in_s_out__state);
+
+ ret = state->handler.recv(state, subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_s_out_as_state {
+ struct _sbus_dbus_invoker_args_s *in;
+ struct _sbus_dbus_invoker_args_as out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char ***);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_s_out_as_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_s_out_as_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_s_out_as_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_dbus_invoke_in_s_out_as_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_s_out_as_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;
+
+ state->in = talloc_zero(state, struct _sbus_dbus_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_s_out_as_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_dbus_invoke_in_s_out_as_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_s_out_as_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_dbus_invoke_in_s_out_as_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, state->in->arg0, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_write_as(state->write_iterator, &state->out);
+ 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, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_s_out_as_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_dbus_invoke_in_s_out_as_done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_s_out_as_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_dbus_invoke_in_s_out_as_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_dbus_invoker_write_as(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_s_out_b_state {
+ struct _sbus_dbus_invoker_args_s *in;
+ struct _sbus_dbus_invoker_args_b out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, bool*);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, bool*);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_s_out_b_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_s_out_b_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_s_out_b_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_dbus_invoke_in_s_out_b_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_s_out_b_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;
+
+ state->in = talloc_zero(state, struct _sbus_dbus_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_s_out_b_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_dbus_invoke_in_s_out_b_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_s_out_b_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_dbus_invoke_in_s_out_b_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, state->in->arg0, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_write_b(state->write_iterator, &state->out);
+ 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, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_s_out_b_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_dbus_invoke_in_s_out_b_done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_s_out_b_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_dbus_invoke_in_s_out_b_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_dbus_invoker_write_b(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_s_out_raw_state {
+ struct _sbus_dbus_invoker_args_s *in;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, DBusMessageIter *);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, DBusMessageIter *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_s_out_raw_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_s_out_raw_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_s_out_raw_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_dbus_invoke_in_s_out_raw_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_s_out_raw_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;
+
+ state->in = talloc_zero(state, struct _sbus_dbus_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_s_out_raw_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_dbus_invoke_in_s_out_raw_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_s_out_raw_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_dbus_invoke_in_s_out_raw_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, state->in->arg0, state->write_iterator);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ 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, state->in->arg0, state->write_iterator);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_s_out_raw_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_dbus_invoke_in_s_out_raw_done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_s_out_raw_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_dbus_invoke_in_s_out_raw_state);
+
+ ret = state->handler.recv(state, subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_s_out_s_state {
+ struct _sbus_dbus_invoker_args_s *in;
+ struct _sbus_dbus_invoker_args_s out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char **);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_s_out_s_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_s_out_s_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_s_out_s_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_dbus_invoke_in_s_out_s_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_s_out_s_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;
+
+ state->in = talloc_zero(state, struct _sbus_dbus_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_s_out_s_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_dbus_invoke_in_s_out_s_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_s_out_s_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_dbus_invoke_in_s_out_s_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, state->in->arg0, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_write_s(state->write_iterator, &state->out);
+ 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, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_s_out_s_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_dbus_invoke_in_s_out_s_done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_s_out_s_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_dbus_invoke_in_s_out_s_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_dbus_invoker_write_s(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_s_out_u_state {
+ struct _sbus_dbus_invoker_args_s *in;
+ struct _sbus_dbus_invoker_args_u out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, uint32_t*);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, uint32_t*);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_s_out_u_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_s_out_u_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_s_out_u_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_dbus_invoke_in_s_out_u_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_s_out_u_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;
+
+ state->in = talloc_zero(state, struct _sbus_dbus_invoker_args_s);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_read_s(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_s_out_u_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_dbus_invoke_in_s_out_u_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_s_out_u_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_dbus_invoke_in_s_out_u_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, state->in->arg0, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_write_u(state->write_iterator, &state->out);
+ 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, state->in->arg0);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_s_out_u_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_dbus_invoke_in_s_out_u_done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_s_out_u_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_dbus_invoke_in_s_out_u_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_dbus_invoker_write_u(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_ss_out_raw_state {
+ struct _sbus_dbus_invoker_args_ss *in;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char *, DBusMessageIter *);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, const char *, DBusMessageIter *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_ss_out_raw_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_ss_out_raw_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_ss_out_raw_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_dbus_invoke_in_ss_out_raw_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_ss_out_raw_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;
+
+ state->in = talloc_zero(state, struct _sbus_dbus_invoker_args_ss);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_read_ss(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_ss_out_raw_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_dbus_invoke_in_ss_out_raw_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_ss_out_raw_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_dbus_invoke_in_ss_out_raw_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, state->in->arg0, state->in->arg1, state->write_iterator);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ 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, state->in->arg0, state->in->arg1, state->write_iterator);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_ss_out_raw_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_dbus_invoke_in_ss_out_raw_done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_ss_out_raw_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_dbus_invoke_in_ss_out_raw_state);
+
+ ret = state->handler.recv(state, subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_sss_out__state {
+ struct _sbus_dbus_invoker_args_sss *in;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char *, const char *);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, const char *, const char *);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_sss_out__step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_sss_out__done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_sss_out__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_dbus_invoke_in_sss_out__state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_sss_out__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;
+
+ state->in = talloc_zero(state, struct _sbus_dbus_invoker_args_sss);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_read_sss(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_sss_out__step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_dbus_invoke_in_sss_out__step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_sss_out__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_dbus_invoke_in_sss_out__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, state->in->arg0, state->in->arg1, state->in->arg2);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ 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, state->in->arg0, state->in->arg1, state->in->arg2);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_sss_out__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_dbus_invoke_in_sss_out__done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_sss_out__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_dbus_invoke_in_sss_out__state);
+
+ ret = state->handler.recv(state, subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+struct _sbus_dbus_invoke_in_su_out_u_state {
+ struct _sbus_dbus_invoker_args_su *in;
+ struct _sbus_dbus_invoker_args_u out;
+ struct {
+ enum sbus_handler_type type;
+ void *data;
+ errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, uint32_t, uint32_t*);
+ struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, uint32_t);
+ errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, uint32_t*);
+ } handler;
+
+ struct sbus_request *sbus_req;
+ DBusMessageIter *read_iterator;
+ DBusMessageIter *write_iterator;
+};
+
+static void
+_sbus_dbus_invoke_in_su_out_u_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+static void
+_sbus_dbus_invoke_in_su_out_u_done
+ (struct tevent_req *subreq);
+
+struct tevent_req *
+_sbus_dbus_invoke_in_su_out_u_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_dbus_invoke_in_su_out_u_state *state;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct _sbus_dbus_invoke_in_su_out_u_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;
+
+ state->in = talloc_zero(state, struct _sbus_dbus_invoker_args_su);
+ if (state->in == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to allocate space for input parameters!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_read_su(state, read_iterator, state->in);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_invoker_schedule(state, ev, _sbus_dbus_invoke_in_su_out_u_step, req);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_dbus_invoke_in_su_out_u_step
+ (struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct _sbus_dbus_invoke_in_su_out_u_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_dbus_invoke_in_su_out_u_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, state->in->arg0, state->in->arg1, &state->out.arg0);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = _sbus_dbus_invoker_write_u(state->write_iterator, &state->out);
+ 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, state->in->arg0, state->in->arg1);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, _sbus_dbus_invoke_in_su_out_u_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_dbus_invoke_in_su_out_u_done(struct tevent_req *subreq)
+{
+ struct _sbus_dbus_invoke_in_su_out_u_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_dbus_invoke_in_su_out_u_state);
+
+ ret = state->handler.recv(state, subreq, &state->out.arg0);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = _sbus_dbus_invoker_write_u(state->write_iterator, &state->out);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
diff --git a/src/sbus/interface_dbus/sbus_dbus_invokers.h b/src/sbus/interface_dbus/sbus_dbus_invokers.h
new file mode 100644
index 0000000..39bfc8b
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_invokers.h
@@ -0,0 +1,55 @@
+/*
+ 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 _SBUS_DBUS_INVOKERS_H_
+#define _SBUS_DBUS_INVOKERS_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "sbus/sbus_interface_declarations.h"
+#include "sbus/sbus_request.h"
+
+#define _sbus_dbus_declare_invoker(input, output) \
+ struct tevent_req * \
+ _sbus_dbus_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)
+
+_sbus_dbus_declare_invoker(, as);
+_sbus_dbus_declare_invoker(, s);
+_sbus_dbus_declare_invoker(raw, );
+_sbus_dbus_declare_invoker(s, );
+_sbus_dbus_declare_invoker(s, as);
+_sbus_dbus_declare_invoker(s, b);
+_sbus_dbus_declare_invoker(s, raw);
+_sbus_dbus_declare_invoker(s, s);
+_sbus_dbus_declare_invoker(s, u);
+_sbus_dbus_declare_invoker(ss, raw);
+_sbus_dbus_declare_invoker(sss, );
+_sbus_dbus_declare_invoker(su, u);
+
+#endif /* _SBUS_DBUS_INVOKERS_H_ */
diff --git a/src/sbus/interface_dbus/sbus_dbus_keygens.c b/src/sbus/interface_dbus/sbus_dbus_keygens.c
new file mode 100644
index 0000000..8e2d53a
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_keygens.c
@@ -0,0 +1,90 @@
+/*
+ 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/sbus_request.h"
+#include "sbus/interface_dbus/sbus_dbus_arguments.h"
+#include "sbus/interface_dbus/sbus_dbus_keygens.h"
+
+const char *
+_sbus_dbus_key_
+ (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);
+}
+
+const char *
+_sbus_dbus_key_s_0
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_dbus_invoker_args_s *args)
+{
+ if (sbus_req->sender == NULL) {
+ return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s:%s",
+ sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0);
+ }
+
+ return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s:%s",
+ sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0);
+}
+
+const char *
+_sbus_dbus_key_ss_0_1
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_dbus_invoker_args_ss *args)
+{
+ if (sbus_req->sender == NULL) {
+ return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s:%s:%s",
+ sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0, args->arg1);
+ }
+
+ return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s:%s:%s",
+ sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0, args->arg1);
+}
+
+const char *
+_sbus_dbus_key_su_0
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_dbus_invoker_args_su *args)
+{
+ if (sbus_req->sender == NULL) {
+ return talloc_asprintf(mem_ctx, "-:%u:%s.%s:%s:%s",
+ sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0);
+ }
+
+ return talloc_asprintf(mem_ctx, "%"PRIi64":%u:%s.%s:%s:%s",
+ sbus_req->sender->uid, sbus_req->type, sbus_req->interface, sbus_req->member,
+ sbus_req->path, args->arg0);
+}
diff --git a/src/sbus/interface_dbus/sbus_dbus_keygens.h b/src/sbus/interface_dbus/sbus_dbus_keygens.h
new file mode 100644
index 0000000..a12cd11
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_keygens.h
@@ -0,0 +1,51 @@
+/*
+ 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 _SBUS_DBUS_KEYGENS_H_
+#define _SBUS_DBUS_KEYGENS_H_
+
+#include <talloc.h>
+
+#include "sbus/sbus_request.h"
+#include "sbus/interface_dbus/sbus_dbus_arguments.h"
+
+const char *
+_sbus_dbus_key_
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req);
+
+const char *
+_sbus_dbus_key_s_0
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_dbus_invoker_args_s *args);
+
+const char *
+_sbus_dbus_key_ss_0_1
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_dbus_invoker_args_ss *args);
+
+const char *
+_sbus_dbus_key_su_0
+ (TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct _sbus_dbus_invoker_args_su *args);
+
+#endif /* _SBUS_DBUS_KEYGENS_H_ */
diff --git a/src/sbus/interface_dbus/sbus_dbus_server.h b/src/sbus/interface_dbus/sbus_dbus_server.h
new file mode 100644
index 0000000..fffc509
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_server.h
@@ -0,0 +1,27 @@
+/*
+ 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 _SBUS_DBUS_SERVER_H_
+#define _SBUS_DBUS_SERVER_H_
+
+#include "sbus/sbus.h"
+#include "sbus/sbus_interface.h"
+#include "sbus/interface_dbus/sbus_dbus_interface.h"
+
+#endif /* _SBUS_DBUS_SERVER_H_ */
diff --git a/src/sbus/interface_dbus/sbus_dbus_symbols.c b/src/sbus/interface_dbus/sbus_dbus_symbols.c
new file mode 100644
index 0000000..f2fda0a
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_symbols.c
@@ -0,0 +1,254 @@
+/*
+ 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/sbus_interface_declarations.h"
+#include "sbus/interface_dbus/sbus_dbus_symbols.h"
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_AddMatch = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "rule"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_GetConnectionUnixProcessID = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "u", .name = "pid"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_GetConnectionUnixUser = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "u", .name = "uid"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_GetId = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "s", .name = "bus_name"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_GetNameOwner = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "s", .name = "unique_name"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Hello = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_ListActivatableNames = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "as", .name = "names"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_ListNames = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "as", .name = "names"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_ListQueuedOwners = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "as", .name = "unique_names"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_NameHasOwner = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "b", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_ReleaseName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "u", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_RemoveMatch = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "rule"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_RequestName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {.type = "u", .name = "flags"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "u", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_StartServiceByName = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "name"},
+ {.type = "u", .name = "flags"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "u", .name = "result"},
+ {NULL}
+ }
+};
+
+const struct sbus_argument
+_sbus_dbus_args_org_freedesktop_DBus_NameAcquired[] = {
+ {.type = "s", .name = "name"},
+ {NULL}
+};
+
+const struct sbus_argument
+_sbus_dbus_args_org_freedesktop_DBus_NameLost[] = {
+ {.type = "s", .name = "name"},
+ {NULL}
+};
+
+const struct sbus_argument
+_sbus_dbus_args_org_freedesktop_DBus_NameOwnerChanged[] = {
+ {.type = "s", .name = "name"},
+ {.type = "s", .name = "new_owner"},
+ {.type = "s", .name = "old_owner"},
+ {NULL}
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Introspectable_Introspect = {
+ .input = (const struct sbus_argument[]){
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "s", .name = "introspection"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Properties_Get = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "interface_name"},
+ {.type = "s", .name = "property_name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "v", .name = "property_value"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Properties_GetAll = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "interface_name"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {.type = "a{sv}", .name = "properties"},
+ {NULL}
+ }
+};
+
+const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Properties_Set = {
+ .input = (const struct sbus_argument[]){
+ {.type = "s", .name = "interface_name"},
+ {.type = "s", .name = "property_name"},
+ {.type = "v", .name = "new_value"},
+ {NULL}
+ },
+ .output = (const struct sbus_argument[]){
+ {NULL}
+ }
+};
diff --git a/src/sbus/interface_dbus/sbus_dbus_symbols.h b/src/sbus/interface_dbus/sbus_dbus_symbols.h
new file mode 100644
index 0000000..fcba97b
--- /dev/null
+++ b/src/sbus/interface_dbus/sbus_dbus_symbols.h
@@ -0,0 +1,88 @@
+/*
+ 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 _SBUS_DBUS_SYMBOLS_H_
+#define _SBUS_DBUS_SYMBOLS_H_
+
+#include "sbus/sbus_interface_declarations.h"
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_AddMatch;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_GetConnectionUnixProcessID;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_GetConnectionUnixUser;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_GetId;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_GetNameOwner;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Hello;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_ListActivatableNames;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_ListNames;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_ListQueuedOwners;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_NameHasOwner;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_ReleaseName;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_RemoveMatch;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_RequestName;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_StartServiceByName;
+
+extern const struct sbus_argument
+_sbus_dbus_args_org_freedesktop_DBus_NameAcquired[];
+
+extern const struct sbus_argument
+_sbus_dbus_args_org_freedesktop_DBus_NameLost[];
+
+extern const struct sbus_argument
+_sbus_dbus_args_org_freedesktop_DBus_NameOwnerChanged[];
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Introspectable_Introspect;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Properties_Get;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Properties_GetAll;
+
+extern const struct sbus_method_arguments
+_sbus_dbus_args_org_freedesktop_DBus_Properties_Set;
+
+#endif /* _SBUS_DBUS_SYMBOLS_H_ */
diff --git a/src/sbus/request/sbus_message.c b/src/sbus/request/sbus_message.c
new file mode 100644
index 0000000..cd7d6da
--- /dev/null
+++ b/src/sbus/request/sbus_message.c
@@ -0,0 +1,583 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <errno.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_errors.h"
+#include "sbus/sbus_message.h"
+#include "sbus/sbus_sync_private.h"
+#include "sbus/interface/sbus_iterator_writers.h"
+
+/* Data slot that is used for message data. The slot is shared for all
+ * messages, i.e. when a data slot is allocated all messages have the
+ * slot available. */
+dbus_int32_t global_data_slot = -1;
+
+struct sbus_talloc_msg {
+ DBusMessage *msg;
+ bool in_talloc_destructor;
+};
+
+static int sbus_talloc_msg_destructor(struct sbus_talloc_msg *talloc_msg)
+{
+ talloc_msg->in_talloc_destructor = true;
+
+ if (talloc_msg->msg == NULL) {
+ return 0;
+ }
+
+ /* There may exist more references to this message but this talloc
+ * context is no longer valid. We remove dbus message data to invoke
+ * dbus destructor now. */
+ dbus_message_set_data(talloc_msg->msg, global_data_slot, NULL, NULL);
+ dbus_message_unref(talloc_msg->msg);
+ return 0;
+}
+
+static void sbus_msg_data_destructor(void *ctx)
+{
+ struct sbus_talloc_msg *talloc_msg;
+
+ talloc_msg = talloc_get_type(ctx, struct sbus_talloc_msg);
+
+ /* Decrement ref counter on data slot. */
+ dbus_message_free_data_slot(&global_data_slot);
+
+ if (!talloc_msg->in_talloc_destructor) {
+ /* References to this message dropped to zero but through
+ * dbus_message_unref(), not by calling talloc_free(). We need to free
+ * the talloc context and avoid running talloc destructor. */
+ talloc_set_destructor(talloc_msg, NULL);
+ talloc_free(talloc_msg);
+ }
+}
+
+errno_t
+sbus_message_bound(TALLOC_CTX *mem_ctx, DBusMessage *msg)
+{
+ struct sbus_talloc_msg *talloc_msg;
+ DBusFreeFunction free_fn;
+ dbus_bool_t bret;
+
+ if (mem_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Warning: bounding to NULL context!\n");
+ return EINVAL;
+ }
+
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Message can not be NULL!\n");
+ return EINVAL;
+ }
+
+ /* Create a talloc context that will unreference this message when
+ * the parent context is freed. */
+ talloc_msg = talloc(mem_ctx, struct sbus_talloc_msg);
+ if (talloc_msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to bound D-Bus message with talloc context!\n");
+ return ENOMEM;
+ }
+
+ /* Allocate a dbus message data slot that will contain pointer to the
+ * talloc context so we can pick up cases when the dbus message is
+ * freed through dbus api. */
+
+ bret = dbus_message_allocate_data_slot(&global_data_slot);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate data slot!\n");
+ talloc_free(talloc_msg);
+ return ENOMEM;
+ }
+
+ free_fn = sbus_msg_data_destructor;
+ bret = dbus_message_set_data(msg, global_data_slot, talloc_msg, free_fn);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set message data!\n");
+ talloc_free(talloc_msg);
+ dbus_message_free_data_slot(&global_data_slot);
+ return ENOMEM;
+ }
+
+ talloc_msg->msg = msg;
+ talloc_msg->in_talloc_destructor = false;
+
+ talloc_set_destructor(talloc_msg, sbus_talloc_msg_destructor);
+
+ return EOK;
+}
+
+errno_t
+sbus_message_bound_steal(TALLOC_CTX *mem_ctx, DBusMessage *msg)
+{
+ struct sbus_talloc_msg *talloc_msg;
+ void *data;
+
+ if (mem_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Warning: bounding to NULL context!\n");
+ return EINVAL;
+ }
+
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Message can not be NULL!\n");
+ return EINVAL;
+ }
+
+ if (global_data_slot < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "This message is not talloc-bound! "
+ "(data slot < 0)\n");
+ return ERR_INTERNAL;
+ }
+
+ data = dbus_message_get_data(msg, global_data_slot);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "This message is not talloc-bound! "
+ "(returned data is NULL)\n");
+ return ERR_INTERNAL;
+ }
+
+ talloc_msg = talloc_get_type(data, struct sbus_talloc_msg);
+ if (talloc_msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "This message is not talloc-bound! "
+ "(invalid data)\n");
+ return ERR_INTERNAL;
+ }
+
+ talloc_steal(mem_ctx, talloc_msg);
+
+ return EOK;
+}
+
+DBusMessage *
+sbus_method_create_empty(TALLOC_CTX *mem_ctx,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method)
+{
+ DBusMessage *msg;
+ errno_t ret;
+
+ msg = dbus_message_new_method_call(bus, path, iface, method);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create message\n");
+ return NULL;
+ }
+
+ if (mem_ctx != NULL) {
+ ret = sbus_message_bound(mem_ctx, msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to bound message with talloc context!\n");
+ dbus_message_unref(msg);
+ return NULL;
+ }
+ }
+
+ return msg;
+}
+
+static DBusMessage *
+sbus_method_create_valist(TALLOC_CTX *mem_ctx,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ int first_arg_type,
+ va_list va)
+{
+ DBusMessage *msg;
+ dbus_bool_t bret;
+
+ msg = sbus_method_create_empty(mem_ctx, bus, path, iface, method);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ bret = dbus_message_append_args_valist(msg, first_arg_type, va);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+DBusMessage *
+_sbus_method_create(TALLOC_CTX *mem_ctx,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ int first_arg_type,
+ ...)
+{
+ DBusMessage *msg;
+ va_list va;
+
+ va_start(va, first_arg_type);
+ msg = sbus_method_create_valist(mem_ctx, bus, path, iface, method,
+ first_arg_type, va);
+ va_end(va);
+
+ return msg;
+}
+
+DBusMessage *
+sbus_signal_create_empty(TALLOC_CTX *mem_ctx,
+ const char *path,
+ const char *iface,
+ const char *signame)
+{
+ DBusMessage *msg;
+ errno_t ret;
+
+ msg = dbus_message_new_signal(path, iface, signame);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create message\n");
+ return NULL;
+ }
+
+ if (mem_ctx != NULL) {
+ ret = sbus_message_bound(mem_ctx, msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to bound message with talloc context!\n");
+ dbus_message_unref(msg);
+ return NULL;
+ }
+ }
+
+ return msg;
+}
+
+static DBusMessage *
+sbus_signal_create_valist(TALLOC_CTX *mem_ctx,
+ const char *path,
+ const char *iface,
+ const char *signame,
+ int first_arg_type,
+ va_list va)
+{
+ DBusMessage *msg;
+ dbus_bool_t bret;
+
+ msg = sbus_signal_create_empty(mem_ctx, path, iface, signame);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ bret = dbus_message_append_args_valist(msg, first_arg_type, va);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+DBusMessage *
+_sbus_signal_create(TALLOC_CTX *mem_ctx,
+ const char *path,
+ const char *iface,
+ const char *method,
+ int first_arg_type,
+ ...)
+{
+ DBusMessage *msg;
+ va_list va;
+
+ va_start(va, first_arg_type);
+ msg = sbus_signal_create_valist(mem_ctx, path, iface, method,
+ first_arg_type, va);
+ va_end(va);
+
+ return msg;
+}
+
+static errno_t
+sbus_message_parse_valist(DBusMessage *msg,
+ int first_arg_type,
+ va_list va)
+{
+ DBusError error;
+ dbus_bool_t bret;
+ errno_t ret;
+
+ dbus_error_init(&error);
+
+ bret = dbus_message_get_args_valist(msg, &error, first_arg_type, va);
+ if (bret == false) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse D-Bus message\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_error_to_errno(&error);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse D-Bus message [%s]: %s\n",
+ error.name, error.message);
+ goto done;
+ }
+
+done:
+ dbus_error_free(&error);
+ return ret;
+}
+
+errno_t
+_sbus_reply_parse(DBusMessage *msg,
+ int first_arg_type,
+ ...)
+{
+ errno_t ret;
+ va_list va;
+
+ ret = sbus_reply_check(msg);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ va_start(va, first_arg_type);
+ ret = sbus_message_parse_valist(msg, first_arg_type, va);
+ va_end(va);
+
+ return ret;
+}
+
+errno_t
+sbus_reply_check(DBusMessage *reply)
+{
+ dbus_bool_t bret;
+ DBusError error;
+ errno_t ret;
+ int type;
+
+ dbus_error_init(&error);
+
+ type = dbus_message_get_type(reply);
+ switch (type) {
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ ret = EOK;
+ goto done;
+
+ case DBUS_MESSAGE_TYPE_ERROR:
+ bret = dbus_set_error_from_message(&error, reply);
+ if (bret == false) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read error from message\n");
+ ret = EIO;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "D-Bus error [%s]: %s\n", error.name,
+ (error.message == NULL ? "<no-message>" : error.message));
+ ret = sbus_error_to_errno(&error);
+ goto done;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected D-Bus message type [%d]\n",
+ type);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+done:
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+errno_t
+sbus_write_input(DBusMessage *msg,
+ sbus_invoker_writer_fn writer,
+ void *input)
+{
+ DBusMessageIter write_iterator;
+ errno_t ret;
+
+ if (writer == NULL) {
+ return EOK;
+ }
+
+ dbus_message_iter_init_append(msg, &write_iterator);
+
+ ret = writer(&write_iterator, input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write message data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+errno_t
+sbus_read_output(TALLOC_CTX *mem_ctx,
+ DBusMessage *msg,
+ sbus_invoker_reader_fn reader,
+ void *output)
+{
+ DBusMessageIter read_iterator;
+ errno_t ret;
+
+ if (reader == NULL) {
+ return EOK;
+ }
+
+ dbus_message_iter_init(msg, &read_iterator);
+
+ ret = reader(mem_ctx, &read_iterator, output);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read message data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+DBusMessage *
+sbus_create_method_call(TALLOC_CTX *mem_ctx,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ void *input)
+{
+ DBusMessage *msg;
+ errno_t ret;
+
+ if (raw_message != NULL) {
+ return raw_message;
+ }
+
+ msg = sbus_method_create_empty(mem_ctx, bus, path, iface, method);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ ret = sbus_write_input(msg, writer, input);
+ if (ret != EOK) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+DBusMessage *
+sbus_create_signal_call(TALLOC_CTX *mem_ctx,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *path,
+ const char *iface,
+ const char *signal_name,
+ void *input)
+{
+ DBusMessage *msg;
+ errno_t ret;
+
+ if (raw_message != NULL) {
+ return raw_message;
+ }
+
+ msg = sbus_signal_create_empty(mem_ctx, path, iface, signal_name);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ ret = sbus_write_input(msg, writer, input);
+ if (ret != EOK) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+DBusMessage *
+sbus_create_set_call(TALLOC_CTX *mem_ctx,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ const char *type,
+ void *input)
+{
+ DBusMessageIter iter;
+ DBusMessageIter variant;
+ DBusMessage *msg;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ if (writer == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: writer cannot be NULL\n");
+ return NULL;
+ }
+
+ msg = sbus_method_create_empty(mem_ctx, bus, path,
+ DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ ret = sbus_iterator_write_s(&iter, iface);
+ if (ret != EOK) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ ret = sbus_iterator_write_s(&iter, property);
+ if (ret != EOK) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ dbret = dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ type, &variant);
+ if (!dbret) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ ret = writer(&variant, input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write message data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ dbus_message_iter_abandon_container(&iter, &variant);
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ dbret = dbus_message_iter_close_container(&iter, &variant);
+ if (!dbret) {
+ dbus_message_iter_abandon_container(&iter, &variant);
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
diff --git a/src/sbus/request/sbus_request.c b/src/sbus/request/sbus_request.c
new file mode 100644
index 0000000..8d49259
--- /dev/null
+++ b/src/sbus/request/sbus_request.c
@@ -0,0 +1,818 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+#include <dhash.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "util/sss_chain_id.h"
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_private.h"
+
+typedef errno_t
+(*sbus_request_messages_fn)(struct tevent_req *req,
+ TALLOC_CTX **_reply_mem_ctx,
+ DBusMessage **_client_message,
+ DBusMessage ***_reply);
+
+struct sbus_active_requests *
+sbus_active_requests_init(TALLOC_CTX *mem_ctx)
+{
+ struct sbus_active_requests *requests;
+
+ requests = talloc_zero(mem_ctx, struct sbus_active_requests);
+ if (requests == NULL) {
+ return NULL;
+ }
+
+ requests->incoming = sbus_requests_init(requests);
+ if (requests->incoming == NULL) {
+ goto fail;
+ }
+
+ requests->outgoing = sbus_requests_init(requests);
+ if (requests->outgoing == NULL) {
+ goto fail;
+ }
+
+ return requests;
+
+fail:
+ talloc_free(requests);
+ return NULL;
+}
+
+struct sbus_request *
+sbus_request_create(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *destination,
+ const char *interface,
+ const char *member,
+ const char *path)
+{
+ struct sbus_request *request;
+
+ request = talloc_zero(mem_ctx, struct sbus_request);
+ if (request == NULL) {
+ return NULL;
+ }
+
+ request->conn = conn;
+ request->type = type;
+ request->sender = NULL;
+
+ request->destination = talloc_strdup(request, destination);
+ if (destination != NULL && request->destination == NULL) {
+ goto fail;
+ }
+
+ request->interface = talloc_strdup(request, interface);
+ if (request->interface == NULL) {
+ goto fail;
+ }
+
+ request->member = talloc_strdup(request, member);
+ if (request->member == NULL) {
+ goto fail;
+ }
+
+ request->path = talloc_strdup(request, path);
+ if (request->path == NULL) {
+ goto fail;
+ }
+
+ return request;
+
+fail:
+ talloc_free(request);
+ return NULL;
+}
+
+static errno_t
+sbus_request_prepare_reply(TALLOC_CTX *mem_ctx,
+ enum sbus_request_type type,
+ DBusMessage *msg,
+ DBusMessage **_reply,
+ DBusMessageIter **_reply_iterator)
+{
+ DBusMessageIter *iterator;
+ DBusMessage *reply;
+ errno_t ret;
+
+ if (type == SBUS_REQUEST_SIGNAL) {
+ /* Signals don't send reply*/
+ *_reply = NULL;
+ *_reply_iterator = NULL;
+ return EOK;
+ }
+
+ iterator = talloc_zero(mem_ctx, DBusMessageIter);
+ if (iterator == NULL) {
+ return ENOMEM;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL) {
+ talloc_free(iterator);
+ return ENOMEM;
+ }
+
+ ret = sbus_message_bound(mem_ctx, reply);
+ if (ret != EOK) {
+ talloc_free(iterator);
+ dbus_message_unref(reply);
+ return ret;
+ }
+
+ dbus_message_iter_init_append(reply, iterator);
+
+ *_reply = reply;
+ *_reply_iterator = iterator;
+
+ return EOK;
+}
+
+static errno_t
+sbus_request_switch_reply(DBusMessage *reply,
+ struct tevent_req *req,
+ sbus_request_messages_fn messages_fn)
+{
+ DBusMessage *client_message;
+ DBusMessage **reply_pointer;
+ TALLOC_CTX *reply_mem_ctx;
+ const char *sender;
+ dbus_uint32_t serial;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ ret = messages_fn(req, &reply_mem_ctx, &client_message, &reply_pointer);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Copy reply to location in a state of this request. */
+
+ *reply_pointer = dbus_message_copy(reply);
+ if (*reply_pointer == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sbus_message_bound(reply_mem_ctx, *reply_pointer);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (client_message == NULL) {
+ ret = EOK;
+ goto done;
+ }
+
+ /* We set destination and serial in reply to point to the original
+ * client request. */
+
+ sender = dbus_message_get_sender(client_message);
+ serial = dbus_message_get_serial(client_message);
+
+ dbret = dbus_message_set_destination(*reply_pointer, sender);
+ if (dbret == false) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set reply sender!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ dbret = dbus_message_set_reply_serial(*reply_pointer, serial);
+ if (dbret == false) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set reply serial!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ dbus_message_unref(*reply_pointer);
+ *reply_pointer = NULL;
+ }
+
+ return ret;
+}
+
+static void
+sbus_request_notify_error(hash_table_t *table,
+ const char *key,
+ struct tevent_req *req,
+ errno_t error)
+{
+ struct sbus_request_list *mainreq = NULL;
+ struct sbus_request_list *list;
+ struct sbus_request_list *item;
+
+ list = sbus_requests_lookup(table, key);
+ if (list == NULL) {
+ /* This was the only request with no key generator available. */
+ tevent_req_error(req, error);
+ return;
+ }
+
+ /* First notify all chained D-Bus requests. */
+ DLIST_FOR_EACH(item, list) {
+ /* Remember the main request. */
+ if (item->req == req) {
+ mainreq = item;
+ continue;
+ }
+
+ /* We don't want to notify current, invalid or non D-Bus request. */
+ if (!item->is_dbus || item->is_invalid) {
+ continue;
+ }
+
+ sbus_requests_finish(item, error);
+ }
+
+ /* Now we finish the main request. */
+ sbus_requests_finish(mainreq, error);
+
+ /* And as last, we notify all await requests. */
+ DLIST_FOR_EACH(item, list) {
+ if (item->is_dbus) {
+ continue;
+ }
+
+ sbus_requests_finish(item, error);
+ }
+
+ sbus_requests_delete(list);
+}
+
+static void
+sbus_request_notify_success(hash_table_t *table,
+ const char *key,
+ struct tevent_req *req,
+ sbus_request_messages_fn messages_fn,
+ DBusMessage *reply)
+{
+ struct sbus_request_list *mainreq = NULL;
+ struct sbus_request_list *list;
+ struct sbus_request_list *item;
+ errno_t ret;
+
+ list = sbus_requests_lookup(table, key);
+ if (list == NULL) {
+ /* This was the only request with no key generator available. */
+ tevent_req_done(req);
+ return;
+ }
+
+ /* First notify all chained D-Bus requests so we can steal the reply. */
+ DLIST_FOR_EACH(item, list) {
+ /* Remember the main request. */
+ if (item->req == req) {
+ mainreq = item;
+ continue;
+ }
+
+ /* We don't want to notify current, invalid or non D-Bus request. */
+ if (!item->is_dbus || item->is_invalid) {
+ continue;
+ }
+
+ ret = sbus_request_switch_reply(reply, item->req, messages_fn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to switch reply for %p, "
+ "terminating this request!\n", item->req);
+ sbus_requests_finish(item, ret);
+ continue;
+ }
+
+ sbus_requests_finish(item, EOK);
+ }
+
+ /* Now we finish the main request. */
+ sbus_requests_finish(mainreq, EOK);
+
+ /* And as last, we notify all await requests. */
+ DLIST_FOR_EACH(item, list) {
+ if (item->is_dbus) {
+ continue;
+ }
+
+ sbus_requests_finish(item, EOK);
+ }
+
+ sbus_requests_delete(list);
+}
+
+struct sbus_incoming_request_state {
+ struct tevent_context *ev;
+ const struct sbus_invoker *invoker;
+ const struct sbus_handler *handler;
+ struct sbus_connection *conn;
+ struct sbus_request *request;
+ DBusMessageIter *read_iter;
+ DBusMessage *reply;
+ DBusMessage *msg;
+ const char *key;
+};
+
+static errno_t
+sbus_request_messages(struct tevent_req *req,
+ TALLOC_CTX **_reply_mem_ctx,
+ DBusMessage **_client_message,
+ DBusMessage ***_reply)
+{
+ struct sbus_incoming_request_state *state;
+
+ state = tevent_req_data(req, struct sbus_incoming_request_state);
+ if (state == NULL) {
+ return ERR_INTERNAL;
+ }
+
+ *_reply_mem_ctx = state;
+ *_client_message = state->msg;
+ *_reply = &state->reply;
+
+ return EOK;
+}
+
+static void sbus_incoming_request_sender_done(struct tevent_req *subreq);
+static void sbus_incoming_request_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_incoming_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_connection *conn,
+ struct sbus_request *request,
+ const struct sbus_invoker *invoker,
+ const struct sbus_handler *handler,
+ const char *sender_name,
+ DBusMessageIter *read_iter,
+ DBusMessage *msg)
+{
+ struct sbus_incoming_request_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_incoming_request_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ if (invoker->issue == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "There is no invoker set!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ switch (handler->type) {
+ case SBUS_HANDLER_SYNC:
+ if (handler->sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "There is no handler set!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ break;
+ case SBUS_HANDLER_ASYNC:
+ if (handler->async_send == NULL || handler->async_recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "There is no handler set!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ break;
+ }
+
+ state->ev = ev;
+ state->msg = msg;
+ state->conn = conn;
+ state->request = request;
+ state->invoker = invoker;
+ state->handler = handler;
+ state->read_iter = read_iter;
+ state->reply = NULL;
+
+ subreq = sbus_sender_resolve_send(mem_ctx, ev, conn, request->type,
+ request->destination, request->path,
+ request->interface, request->member,
+ sender_name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_incoming_request_sender_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_incoming_request_sender_done(struct tevent_req *subreq)
+{
+ struct sbus_incoming_request_state *state;
+ DBusMessageIter *write_iter = NULL;
+ struct sbus_sender *sender;
+ struct tevent_req *req;
+ bool key_exists;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_incoming_request_state);
+
+ ret = sbus_sender_resolve_recv(state, subreq, &sender);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->request->sender = talloc_steal(state->request, sender);
+
+ ret = sbus_check_access(state->conn, state->request);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_prepare_reply(state, state->request->type, state->msg,
+ &state->reply, &write_iter);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /**
+ * Invoke a read invoker. This function will read method arguments
+ * from message. Then it will use these arguments to create a key
+ * describing this method and at last, it will schedule method handler
+ * to be issued in next loop.
+ */
+ subreq = state->invoker->issue(state, state->ev, state->request,
+ state->invoker->keygen, state->handler,
+ state->read_iter, write_iter,
+ &state->key);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ /**
+ * Now when we have the key, we will search table to see if the same
+ * request is not already in progress. If it is, we cancel this one
+ * and register ourselves for notification when it is finished.
+ *
+ * Otherwise we add ourselves as the first request of this type and
+ * set a tevent callback that is triggered when the method handler is done.
+ */
+ ret = sbus_requests_add(state->conn->requests->incoming, state->key,
+ state->conn, req, true, &key_exists);
+ if (ret != EOK || key_exists) {
+ /* Cancel the sub request. Since there was either an error or the
+ * sub request was chained. */
+ talloc_zfree(subreq);
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_incoming_request_done, req);
+ return;
+
+done:
+ talloc_zfree(write_iter);
+
+ if (state->reply != NULL) {
+ dbus_message_unref(state->reply);
+ state->reply = NULL;
+ }
+
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void sbus_incoming_request_done(struct tevent_req *subreq)
+{
+ struct sbus_incoming_request_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_incoming_request_state);
+
+ /* state->reply is filled through iterator in the subrequest. */
+ ret = sbus_invoker_recv(subreq);
+ talloc_zfree(subreq);
+
+ if (ret != EOK) {
+ sbus_request_notify_error(state->conn->requests->incoming,
+ state->key, req, ret);
+ return;
+ }
+
+ sbus_request_notify_success(state->conn->requests->incoming,
+ state->key, req, sbus_request_messages,
+ state->reply);
+}
+
+errno_t
+sbus_incoming_request_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ struct sbus_incoming_request_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sbus_incoming_request_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ /* Signals have no reply so this is ok. */
+ if (state->reply == NULL) {
+ *_reply = NULL;
+ return EOK;
+ }
+
+ 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;
+
+ return EOK;
+}
+
+struct sbus_outgoing_request_state {
+ const char *key;
+ struct sbus_connection *conn;
+ DBusMessage *reply;
+ uint64_t chain_id;
+};
+
+static errno_t
+sbus_outgoing_request_messages(struct tevent_req *req,
+ TALLOC_CTX **_reply_mem_ctx,
+ DBusMessage **_client_message,
+ DBusMessage ***_reply)
+{
+ struct sbus_outgoing_request_state *state;
+
+ state = tevent_req_data(req, struct sbus_outgoing_request_state);
+ if (state == NULL) {
+ return ERR_INTERNAL;
+ }
+
+ *_reply_mem_ctx = state;
+ *_client_message = NULL;
+ *_reply = &state->reply;
+
+ return EOK;
+}
+
+static void sbus_outgoing_request_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_outgoing_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_connection *conn,
+ const char *key,
+ DBusMessage *msg)
+{
+ struct sbus_outgoing_request_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ bool key_exists;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_outgoing_request_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->conn = conn;
+
+ /*
+ * The message is sent over top level dbus tevent code. This means that
+ * the chain id information is lost and is not restored when we get reply
+ * from dbus. Therefore we need to remember it and restore it manually
+ * when this request is done.
+ */
+ state->chain_id = sss_chain_id_get();
+
+ if (key != NULL) {
+ state->key = talloc_strdup(state, key);
+ if (state->key == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ /**
+ * We will search table to see if the same request is not already
+ * in progress. If it is, we register ourselves for notification
+ * when it is finished.
+ *
+ * Otherwise we add ourselves as the first request of this type and
+ * set a tevent callback that is triggered when the method handler is done.
+ */
+ ret = sbus_requests_add(conn->requests->outgoing, key,
+ conn, req, true, &key_exists);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (key_exists) {
+ return req;
+ }
+
+ subreq = sbus_message_send(state, conn, msg, SBUS_MESSAGE_TIMEOUT);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_outgoing_request_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_outgoing_request_done(struct tevent_req *subreq)
+{
+ struct sbus_outgoing_request_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_outgoing_request_state);
+
+ sss_chain_id_set(state->chain_id);
+
+ ret = sbus_message_recv(state, subreq, &state->reply);
+ talloc_zfree(subreq);
+
+ if (ret != EOK) {
+ sbus_request_notify_error(state->conn->requests->outgoing,
+ state->key, req, ret);
+ return;
+ }
+
+ sbus_request_notify_success(state->conn->requests->outgoing,
+ state->key, req,
+ sbus_outgoing_request_messages,
+ state->reply);
+}
+
+errno_t
+sbus_outgoing_request_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ struct sbus_outgoing_request_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sbus_outgoing_request_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ 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;
+
+ return EOK;
+}
+
+struct sbus_request_await_state {
+ int dummy;
+};
+
+struct tevent_req *
+sbus_request_await_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *object_path,
+ const char *interface,
+ const char *member,
+ const char *additional_key)
+{
+ struct sbus_request_await_state *state;
+ struct sbus_request_list *list;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_request_await_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ key = talloc_asprintf(state, "-:%u:%s.%s:%s%s%s",
+ type, interface, member, object_path,
+ additional_key == NULL ? "" : ":",
+ additional_key == NULL ? "" : additional_key);
+ if (key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ list = sbus_requests_lookup(conn->requests->outgoing, key);
+ if (list == NULL) {
+ /* No active request with this key exists. */
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise attach to this request. */
+ ret = sbus_requests_add(conn->requests->outgoing, key, conn,
+ req, false, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to attach to the request list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ 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;
+}
+
+errno_t sbus_request_await_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static errno_t sbus_unwanted_reply_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+void sbus_unwanted_reply(struct tevent_req *subreq)
+{
+ errno_t ret;
+
+ ret = sbus_unwanted_reply_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK && ret != ERR_SBUS_UNKNOWN_SERVICE) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error sending sbus message [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+}
diff --git a/src/sbus/request/sbus_request_call.c b/src/sbus/request/sbus_request_call.c
new file mode 100644
index 0000000..cf2a6e5
--- /dev/null
+++ b/src/sbus/request/sbus_request_call.c
@@ -0,0 +1,160 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <tevent.h>
+#include <talloc.h>
+
+#include "sbus/sbus_private.h"
+
+struct sbus_call_method_state {
+ DBusMessage *reply;
+};
+
+static void sbus_call_method_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_call_method_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_keygen keygen,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ void *input)
+{
+ struct sbus_call_method_state *state;
+ struct sbus_request *sbus_req;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ const char *key = NULL;
+ DBusMessage *msg;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_call_method_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ sbus_req = sbus_request_create(state, conn, SBUS_REQUEST_METHOD,
+ bus, iface, method, path);
+ if (sbus_req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg = sbus_create_method_call(state, raw_message, writer, bus, path,
+ iface, method, input);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, input, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ subreq = sbus_outgoing_request_send(state, conn->ev, conn, key, msg);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_call_method_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, conn->ev);
+ }
+
+ return req;
+}
+
+static void sbus_call_method_done(struct tevent_req *subreq)
+{
+ struct sbus_call_method_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_call_method_state);
+
+ ret = sbus_outgoing_request_recv(state, subreq, &state->reply);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+sbus_call_method_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ struct sbus_call_method_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sbus_call_method_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ 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;
+
+ return EOK;
+}
+
+void
+sbus_call_signal_send(struct sbus_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *path,
+ const char *iface,
+ const char *signal_name,
+ void *input)
+{
+ DBusMessage *msg;
+
+ msg = sbus_create_signal_call(NULL, raw_message, writer, path, iface,
+ signal_name, input);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create signal message!\n");
+ return;
+ }
+
+ sbus_emit_signal(conn, msg);
+}
diff --git a/src/sbus/request/sbus_request_hash.c b/src/sbus/request/sbus_request_hash.c
new file mode 100644
index 0000000..0ddad03
--- /dev/null
+++ b/src/sbus/request/sbus_request_hash.c
@@ -0,0 +1,325 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "util/sss_ptr_hash.h"
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_private.h"
+
+static void
+sbus_requests_disable_spies(struct sbus_request_list *item);
+
+static void
+sbus_requests_validate(struct sbus_request_list *list);
+
+struct sbus_request_spy {
+ struct sbus_request_list *item;
+};
+
+static int
+sbus_requests_spy_destructor(struct sbus_request_spy *spy)
+{
+ struct sbus_request_list *item;
+
+ item = spy->item;
+
+ if (item->spy.conn == spy) {
+ item->spy.conn = NULL;
+ item->conn = NULL;
+ } else {
+ item->spy.req = NULL;
+ item->req = NULL;
+ }
+
+ sbus_requests_finish(item, ERR_TERMINATED);
+ sbus_requests_validate(item);
+
+ return 0;
+}
+
+static struct sbus_request_spy *
+sbus_requests_spy_create(TALLOC_CTX *mem_ctx,
+ struct sbus_request_list *item)
+{
+ struct sbus_request_spy *spy;
+
+ spy = talloc_zero(mem_ctx, struct sbus_request_spy);
+ if (spy == NULL) {
+ return NULL;
+ }
+
+ spy->item = item;
+
+ talloc_set_destructor(spy, sbus_requests_spy_destructor);
+
+ return spy;
+}
+
+static errno_t
+sbus_requests_attach_spies(struct sbus_request_list *item)
+{
+ item->spy.conn = sbus_requests_spy_create(item->conn, item);
+ if (item->spy.conn == NULL) {
+ return ENOMEM;
+ }
+
+ item->spy.req = sbus_requests_spy_create(item->req, item);
+ if (item->spy.req == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static void
+sbus_requests_disable_spies(struct sbus_request_list *item)
+{
+ if (item->spy.req != NULL) {
+ talloc_set_destructor(item->spy.req, NULL);
+ }
+
+ if (item->spy.conn != NULL) {
+ talloc_set_destructor(item->spy.conn, NULL);
+ }
+
+ talloc_zfree(item->spy.req);
+ talloc_zfree(item->spy.conn);
+}
+
+hash_table_t *
+sbus_requests_init(TALLOC_CTX *mem_ctx)
+{
+ return sss_ptr_hash_create(mem_ctx, NULL, NULL);
+}
+
+errno_t
+sbus_requests_add(hash_table_t *table,
+ const char *key,
+ struct sbus_connection *conn,
+ struct tevent_req *req,
+ bool is_dbus,
+ bool *_key_exists)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_request_list *list;
+ struct sbus_request_list *item;
+ bool key_exists = false;
+ errno_t ret;
+
+ if (key == NULL) {
+ /* This is ok, since not all request are supposed to be multicasted.
+ * The caller will continue as this was a new request.
+ * And it simplifies the code. */
+ *_key_exists = false;
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ item = talloc_zero(tmp_ctx, struct sbus_request_list);
+ if (item == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item->req = req;
+ item->conn = conn;
+ item->is_dbus = is_dbus;
+
+ ret = sbus_requests_attach_spies(item);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* First, check if the key already exist. If yes, check if the list
+ * is valid and just append the item to the list if so. Otherwise,
+ * the list is internally deleted and we can create a new one. */
+ list = sss_ptr_hash_lookup(table, key, struct sbus_request_list);
+ if (list != NULL) {
+ key_exists = true;
+ DLIST_ADD_END(list, item, struct sbus_request_list *);
+ DEBUG(SSSDBG_TRACE_ALL, "Chaining request: %s\n", key);
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise create new hash entry and new list. */
+ list = item;
+ ret = sss_ptr_hash_add(table, key, list, struct sbus_request_list);
+
+done:
+ if (ret == EOK) {
+ if (_key_exists != NULL) {
+ *_key_exists = key_exists;
+ }
+
+ talloc_steal(table, item);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+struct sbus_request_list *
+sbus_requests_lookup(hash_table_t *table,
+ const char *key)
+{
+ if (key == NULL) {
+ /* This is ok, since not all request are supposed to be multicasted.
+ * The caller will have an empty list ot send notification to.
+ * And it simplifies the code. */
+ return NULL;
+ }
+
+ return sss_ptr_hash_lookup(table, key, struct sbus_request_list);
+}
+
+void
+sbus_requests_delete(struct sbus_request_list *list)
+{
+ struct sbus_request_list *current, *next;
+
+ if (list == NULL) {
+ return;
+ }
+
+ /* Find head of the list. */
+ while (list->prev != NULL) {
+ list = list->prev;
+ }
+
+ /* Freeing the first item will remove the list also from the table. */
+ DLIST_FOR_EACH_SAFE(current, next, list) {
+ sbus_requests_disable_spies(current);
+ talloc_zfree(current);
+ }
+}
+
+static void
+sbus_requests_validate(struct sbus_request_list *list)
+{
+ struct sbus_request_list *current, *next;
+
+ /* Find head of the list. */
+ while (list->prev != NULL) {
+ list = list->prev;
+ }
+
+ /* An item is invalid if either its request or associated connection
+ * is freed before this sbus request has finished.
+ *
+ * The list is invalid only if all items are invalid or if the first
+ * item that holds the actual request is invalid. If this is the case
+ * we will remove this list and report it to the caller.
+ *
+ * The sbus request is always associated with the first item. If it
+ * is invalid we must also terminate all other requests. */
+
+ if (list->is_invalid) {
+ DLIST_FOR_EACH_SAFE(current, next, list->next) {
+ if (current->is_invalid) {
+ continue;
+ }
+
+ sbus_requests_disable_spies(current);
+ tevent_req_error(current->req, ERR_TERMINATED);
+ }
+ } else {
+ DLIST_FOR_EACH_SAFE(current, next, list) {
+ if (!current->is_invalid) {
+ return;
+ }
+ }
+ }
+
+ sbus_requests_delete(list);
+}
+
+void
+sbus_requests_finish(struct sbus_request_list *item,
+ errno_t error)
+{
+ if (item == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Bug: item is NULL\n");
+ return;
+ }
+
+ if (item->is_invalid) {
+ return;
+ }
+
+ /* Make sure that spies are disabled and this item is not handled
+ * anymore. */
+ sbus_requests_disable_spies(item);
+ item->is_invalid = true;
+
+ if (item->req == NULL) {
+ return;
+ }
+
+ if (error != EOK) {
+ tevent_req_error(item->req, error);
+ return;
+ }
+
+ tevent_req_done(item->req);
+
+ item->req = NULL;
+}
+
+void
+sbus_requests_terminate_all(hash_table_t *table,
+ errno_t error)
+{
+ struct sbus_request_list *list;
+ struct sbus_request_list *item;
+ hash_value_t *values;
+ unsigned long int num;
+ unsigned long int i;
+ int hret;
+
+ hret = hash_values(table, &num, &values);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get list of active requests "
+ "[%d]: %s\n", hret, hash_error_string(hret));
+ return;
+ }
+
+ for (i = 0; i < num; i++) {
+ list = sss_ptr_get_value(&values[i], struct sbus_request_list);
+
+ DLIST_FOR_EACH(item, list) {
+ sbus_requests_finish(item, error);
+ }
+
+ sbus_requests_delete(list);
+ }
+
+ talloc_free(values);
+}
diff --git a/src/sbus/request/sbus_request_sender.c b/src/sbus/request/sbus_request_sender.c
new file mode 100644
index 0000000..39cdec0
--- /dev/null
+++ b/src/sbus/request/sbus_request_sender.c
@@ -0,0 +1,340 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <dhash.h>
+#include <stdint.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "sbus/sbus_private.h"
+#include "sbus/interface_dbus/sbus_dbus_client_async.h"
+
+struct sbus_sender *
+sbus_sender_create(TALLOC_CTX *mem_ctx,
+ const char *name,
+ int64_t uid)
+{
+ struct sbus_sender sender = {.name = name, .uid = uid};
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ return sbus_sender_copy(mem_ctx, &sender);
+}
+
+struct sbus_sender *
+sbus_sender_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_sender *input)
+{
+ struct sbus_sender *copy;
+
+ copy = talloc_zero(mem_ctx, struct sbus_sender);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ copy->name = talloc_strdup(copy, input->name);
+ if (copy->name == NULL) {
+ talloc_free(copy);
+ return NULL;
+ }
+
+ copy->uid = input->uid;
+
+ return copy;
+}
+
+hash_table_t *
+sbus_senders_init(TALLOC_CTX *mem_ctx)
+{
+ return sss_ptr_hash_create(mem_ctx, NULL, NULL);
+}
+
+static errno_t
+sbus_senders_add(hash_table_t *table,
+ struct sbus_sender *sender)
+{
+ struct sbus_sender *copy;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Inserting identity of sender [%s]: %"PRIi64"\n",
+ sender->name, sender->uid);
+
+ copy = sbus_sender_copy(table, sender);
+ if (copy == NULL) {
+ return ENOMEM;
+ }
+
+ return sss_ptr_hash_add(table, sender->name, copy, struct sbus_sender);
+}
+
+static struct sbus_sender *
+sbus_senders_lookup(hash_table_t *table,
+ const char *name)
+{
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Looking for identity of sender [%s]\n",
+ name);
+
+ return sss_ptr_hash_lookup(table, name, struct sbus_sender);
+}
+
+void
+sbus_senders_delete(hash_table_t *table,
+ const char *name)
+{
+ if (sss_ptr_hash_has_key(table, name)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Removing identity of sender [%s]\n",
+ name);
+ sss_ptr_hash_delete(table, name, true);
+ }
+}
+
+errno_t
+sbus_sender_check_input(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *destination,
+ const char *object_path,
+ const char *interface,
+ const char *member,
+ const char *name,
+ struct sbus_sender **_sender)
+{
+ /* This is a server call. We do not need to resolve sender in this case. */
+ if (destination != NULL && strcmp(destination, DBUS_SERVICE_DBUS) == 0) {
+ return EOK;
+ }
+
+ /* Hello is a special method that is used by clients to register on the
+ * bus. Upon registration server assigns unique name to the clients.
+ * Therefore it is not actually possible to resolve a sender name
+ * prior this call. */
+ if (name == NULL && type == SBUS_REQUEST_METHOD
+ && strcmp(object_path, DBUS_PATH_DBUS) == 0
+ && strcmp(interface, DBUS_INTERFACE_DBUS) == 0
+ && strcmp(member, "Hello") == 0) {
+
+ *_sender = sbus_sender_create(mem_ctx, name, SBUS_SENDER_HELLO);
+ if (*_sender == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+ }
+
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Can not resolve empty name!\n");
+ return EINVAL;
+ }
+
+ /* Got signal from bus, this is OK. This name is not really resolvable. */
+ if (strcmp(name, DBUS_SERVICE_DBUS) == 0) {
+ *_sender = sbus_sender_create(mem_ctx, name, SBUS_SENDER_DBUS);
+ if (*_sender == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+ }
+
+ return EAGAIN;
+}
+
+struct sbus_sender_resolve_state {
+ struct sbus_connection *conn;
+ enum sbus_request_type type;
+ struct sbus_sender *sender;
+ const char *name;
+};
+
+static void sbus_sender_resolve_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_sender_resolve_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *destination,
+ const char *object_path,
+ const char *interface,
+ const char *member,
+ const char *name)
+{
+ struct sbus_sender_resolve_state *state;
+ struct sbus_sender *sender;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_sender_resolve_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->conn = conn;
+ state->type = type;
+ state->sender = NULL;
+
+ ret = sbus_sender_check_input(state, conn, type, destination, object_path,
+ interface, member, name, &state->sender);
+ if (ret != EAGAIN) {
+ goto done;
+ }
+
+ /* Check if the sender is already known. If yes, we must create a copy
+ * of it since it may be asynchronously deleted through NameOwnerChanged
+ * signal. */
+ sender = sbus_senders_lookup(conn->senders, name);
+ if (sender != NULL) {
+ state->sender = sbus_sender_copy(state, sender);
+ if (state->sender == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+ goto done;
+ }
+
+ state->name = talloc_strdup(state, name);
+ if (state->name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = sbus_call_DBus_GetConnectionUnixUser_send(state, conn,
+ DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_sender_resolve_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_sender_resolve_done(struct tevent_req *subreq)
+{
+ struct sbus_sender_resolve_state *state;
+ struct sbus_sender *sender;
+ struct tevent_req *req;
+ uint32_t uid;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_sender_resolve_state);
+
+ ret = sbus_call_DBus_GetConnectionUnixUser_recv(subreq, &uid);
+ talloc_zfree(subreq);
+ if (ret == ERR_SBUS_UNKNOWN_OWNER && state->type == SBUS_REQUEST_SIGNAL) {
+ /* If the caller of the signal exits before we translate the name,
+ * it is possible that the name is no longer known on the bus.
+ * E.g. when the signal is sent via dbus-send. */
+ DEBUG(SSSDBG_MINOR_FAILURE, "Identity of signal sender "
+ "[%s] is not known. Continue without it.\n", state->name);
+
+ state->sender = sbus_sender_create(state, state->name,
+ SBUS_SENDER_SIGNAL);
+ if (state->sender == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ /* We don't have request chaining on this level so it is possible that
+ * a concurrent lookup finished first. If this is this case, we return
+ * the previous lookup result and just finish.
+ *
+ * We must create a copy of the result since it may be asynchronously
+ * deleted through NameOwnerChanged signal. */
+ sender = sbus_senders_lookup(state->conn->senders, state->name);
+ if (sender != NULL) {
+ state->sender = sbus_sender_copy(state, sender);
+ if (state->sender == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise we insert this result into the table. The add operation
+ * will create a copy of this structure so we can return state->sender
+ * directly in the result. */
+ state->sender = sbus_sender_create(state, state->name, uid);
+ if (state->sender == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_senders_add(state->conn->senders, state->sender);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t
+sbus_sender_resolve_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sbus_sender **_sender)
+{
+ struct sbus_sender_resolve_state *state;
+ state = tevent_req_data(req, struct sbus_sender_resolve_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_sender) {
+ *_sender = talloc_steal(mem_ctx, state->sender);
+ }
+
+ return EOK;
+}
diff --git a/src/sbus/request/sbus_request_util.c b/src/sbus/request/sbus_request_util.c
new file mode 100644
index 0000000..c9bddbc
--- /dev/null
+++ b/src/sbus/request/sbus_request_util.c
@@ -0,0 +1,67 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <tevent.h>
+#include <talloc.h>
+
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_private.h"
+
+errno_t
+sbus_invoker_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+errno_t
+sbus_request_key(TALLOC_CTX *mem_ctx,
+ sbus_invoker_keygen keygen,
+ struct sbus_request *sbus_req,
+ void *input,
+ const char **_key)
+{
+ const char *(*args_fn)(TALLOC_CTX *, struct sbus_request *, void *);
+ const char *(*noargs_fn)(TALLOC_CTX *, struct sbus_request *);
+ const char *key;
+
+ if (keygen == NULL) {
+ *_key = NULL;
+ return EOK;
+ }
+
+ if (input == NULL) {
+ noargs_fn = keygen;
+ key = noargs_fn(mem_ctx, sbus_req);
+ } else {
+ args_fn = keygen;
+ key = args_fn(mem_ctx, sbus_req, input);
+ }
+
+ if (key == NULL) {
+ return ENOMEM;
+ }
+
+ *_key = key;
+
+ return EOK;
+
+}
diff --git a/src/sbus/router/sbus_router.c b/src/sbus/router/sbus_router.c
new file mode 100644
index 0000000..ed8464b
--- /dev/null
+++ b/src/sbus/router/sbus_router.c
@@ -0,0 +1,387 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_private.h"
+
+static errno_t
+sbus_router_register_std(struct sbus_router *router)
+{
+ errno_t ret;
+
+ ret = sbus_register_introspection(router);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unable to register org.freedesktop.DBus.Introspectable.\n");
+ return ret;
+ }
+
+ ret = sbus_register_properties(router);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unable to register org.freedesktop.DBus.Properties.\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t
+sbus_router_add_path(struct sbus_router *router,
+ const char *path,
+ struct sbus_interface *iface)
+{
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Registering interface %s on path %s\n",
+ iface->name, path);
+
+ ret = sbus_router_paths_add(router->paths, path, iface);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add new path [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t
+sbus_router_add_path_map(struct sbus_router *router,
+ struct sbus_path *map)
+{
+ errno_t ret;
+ int i;
+
+ for (i = 0; map[i].path != NULL; i++) {
+ ret = sbus_router_add_path(router, map[i].path, map[i].iface);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+char *
+sbus_router_signal_rule(TALLOC_CTX *mem_ctx,
+ const char *interface,
+ const char *signal_name)
+{
+ return talloc_asprintf(mem_ctx, "type='signal',interface='%s',member='%s'",
+ interface, signal_name);
+}
+
+errno_t
+sbus_router_signal_parse(TALLOC_CTX *mem_ctx,
+ const char *qualified_signal,
+ char **_interface,
+ char **_signal_name)
+{
+ char *signal_name;
+ char *dot;
+ char *dup;
+
+ dup = talloc_strdup(mem_ctx, qualified_signal);
+ if (dup == NULL) {
+ return ENOMEM;
+ }
+
+ /* Split the duplicate into interface and signal name parts. */
+ dot = strrchr(dup, '.');
+ if (dot == NULL) {
+ talloc_free(dup);
+ return EINVAL;
+ }
+ *dot = '\0';
+
+ signal_name = talloc_strdup(mem_ctx, dot + 1);
+ if (signal_name == NULL) {
+ talloc_free(dup);
+ return ENOMEM;
+ }
+
+ *_interface = dup;
+ *_signal_name = signal_name;
+
+ return EOK;
+}
+
+static void
+sbus_router_signal_match(struct sbus_router *router,
+ DBusConnection *conn,
+ const char *interface,
+ const char *signal_name)
+{
+ char *rule;
+
+ rule = sbus_router_signal_rule(NULL, interface, signal_name);
+ if (rule == NULL) {
+ /* There is nothing we can do. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return;
+ }
+
+ /* If error is not NULL D-Bus will block. There is nothing to do anyway,
+ * so we just won't detect errors here. */
+ dbus_bus_add_match(conn, rule, NULL);
+ talloc_free(rule);
+}
+
+errno_t
+sbus_router_listen(struct sbus_connection *conn,
+ struct sbus_listener *listener)
+{
+ bool signal_known;
+ errno_t ret;
+
+ /* We can't register signal listener on this connection. */
+ if (conn->type == SBUS_CONNECTION_CLIENT) {
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Registering signal listener %s.%s on path %s\n",
+ listener->interface, listener->signal_name,
+ (listener->object_path == NULL ? "<ALL>" : listener->object_path));
+
+ ret = sbus_router_listeners_add(conn->router->listeners,
+ listener->interface,
+ listener->signal_name,
+ listener, &signal_known);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add new listener [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (signal_known) {
+ /* This signal listener is already registered. */
+ return EOK;
+ }
+
+ sbus_router_signal_match(conn->router, conn->connection,
+ listener->interface, listener->signal_name);
+
+ return ret;
+}
+
+errno_t
+sbus_router_listen_map(struct sbus_connection *conn,
+ struct sbus_listener *map)
+{
+ errno_t ret;
+ int i;
+
+ for (i = 0; map[i].interface != NULL; i++) {
+ ret = sbus_router_listen(conn, &map[i]);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+errno_t
+sbus_router_add_node(struct sbus_connection *conn,
+ struct sbus_node *node)
+{
+ errno_t ret;
+
+ if (node->path == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: path cannot be NULL!\n");
+ return ERR_INTERNAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Adding new node: %s\n", node->path);
+
+ ret = sbus_router_nodes_add(conn->router->nodes, node);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add node %s [%d]: %s\n",
+ node->path, ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+errno_t
+sbus_router_add_node_map(struct sbus_connection *conn,
+ struct sbus_node *map)
+{
+ errno_t ret;
+ int i;
+
+ for (i = 0; map[i].path != NULL; i++) {
+ ret = sbus_router_add_node(conn, &map[i]);
+ if (ret != EOK) {
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static bool
+sbus_router_filter_add(struct sbus_router *router)
+{
+ dbus_bool_t dbret;
+
+ /* Add a connection filter that is used to process input messages. */
+ dbret = dbus_connection_add_filter(router->conn->connection,
+ sbus_connection_filter,
+ router->conn, NULL);
+ if (dbret == false) {
+ return false;
+ }
+
+ return true;
+}
+
+int sbus_router_destructor(struct sbus_router *router)
+{
+ dbus_connection_remove_filter(router->conn->connection,
+ sbus_connection_filter, router->conn);
+
+ return 0;
+}
+
+struct sbus_router *
+sbus_router_init(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn)
+{
+ struct sbus_router *router;
+ errno_t ret;
+ bool bret;
+
+ router = talloc_zero(mem_ctx, struct sbus_router);
+ if (router == NULL) {
+ return NULL;
+ }
+
+ router->conn = conn;
+
+ router->paths = sbus_router_paths_init(router);
+ if (router->paths == NULL) {
+ goto fail;
+ }
+
+ router->nodes = sbus_router_nodes_init(router);
+ if (router->paths == NULL) {
+ goto fail;
+ }
+
+ /* Register standard interfaces. */
+ ret = sbus_router_register_std(router);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ /* This is a server-side router. */
+ if (conn == NULL) {
+ return router;
+ }
+
+ router->listeners = sbus_router_listeners_init(router, conn);
+ if (router->listeners == NULL) {
+ goto fail;
+ }
+
+ bret = sbus_router_filter_add(router);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register message filter!\n");
+ goto fail;
+ }
+
+ talloc_set_destructor(router, sbus_router_destructor);
+
+ return router;
+
+fail:
+ talloc_free(router);
+ return NULL;
+}
+
+static errno_t
+sbus_router_reset_listeners(struct sbus_connection *conn)
+{
+ TALLOC_CTX *tmp_ctx;
+ hash_key_t *keys;
+ char *interface;
+ char *name;
+ unsigned long count;
+ unsigned long i;
+ errno_t ret;
+ int hret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ hret = hash_keys(conn->router->listeners, &count, &keys);
+ if (hret != HASH_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_steal(tmp_ctx, keys);
+
+ for (i = 0; i < count; i++) {
+ ret = sbus_router_signal_parse(tmp_ctx, keys[i].str, &interface, &name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sbus_router_signal_match(conn->router, conn->connection,
+ interface, name);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sbus_router_reset(struct sbus_connection *conn)
+{
+ errno_t ret;
+ bool bret;
+
+ bret = sbus_router_filter_add(conn->router);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register message filter!\n");
+ return EFAULT;
+ }
+
+ ret = sbus_router_reset_listeners(conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to reset router listeners "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
diff --git a/src/sbus/router/sbus_router_handler.c b/src/sbus/router/sbus_router_handler.c
new file mode 100644
index 0000000..7b6c244
--- /dev/null
+++ b/src/sbus/router/sbus_router_handler.c
@@ -0,0 +1,324 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <string.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <sys/types.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "util/sss_chain_id.h"
+#include "sbus/sbus_private.h"
+
+struct sbus_message_meta {
+ int type;
+ const char *destination;
+ const char *interface;
+ const char *member;
+ const char *sender;
+ const char *path;
+};
+
+static void
+sbus_message_meta_read(DBusMessage *message,
+ struct sbus_message_meta *meta)
+{
+ meta->type = dbus_message_get_type(message);
+ meta->destination = dbus_message_get_destination(message);
+ meta->interface = dbus_message_get_interface(message);
+ meta->member = dbus_message_get_member(message);
+ meta->sender = dbus_message_get_sender(message);
+ meta->path = dbus_message_get_path(message);
+}
+
+struct sbus_issue_request_state {
+ struct sbus_connection *conn;
+ DBusMessageIter message_iter;
+ DBusMessage *message;
+ enum sbus_request_type type;
+};
+
+static void sbus_issue_request_done(struct tevent_req *subreq);
+
+static errno_t
+sbus_issue_request(TALLOC_CTX *mem_ctx,
+ struct sbus_message_meta *meta,
+ struct sbus_connection *conn,
+ DBusMessage *message,
+ enum sbus_request_type type,
+ const struct sbus_invoker *invoker,
+ const struct sbus_handler *handler)
+{
+ struct sbus_issue_request_state *state;
+ struct sbus_request *request;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ state = talloc_zero(mem_ctx, struct sbus_issue_request_state);
+ if (state == NULL) {
+ return ENOMEM;
+ }
+
+ state->conn = conn;
+ state->message = dbus_message_ref(message);
+ state->type = type;
+
+ ret = sbus_message_bound(state, state->message);
+ if (ret != EOK) {
+ dbus_message_unref(state->message);
+ goto done;
+ }
+
+ dbus_message_iter_init(message, &state->message_iter);
+
+ request = sbus_request_create(state, conn, type, meta->destination,
+ meta->interface, meta->member, meta->path);
+ if (request == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create request data!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = sbus_incoming_request_send(state, conn->ev, conn, request,
+ invoker, handler, meta->sender,
+ &state->message_iter, message);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create request!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_issue_request_done, state);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(state);
+ }
+
+ return ret;
+}
+
+static void sbus_issue_request_done(struct tevent_req *subreq)
+{
+ struct sbus_issue_request_state *state;
+ struct sbus_message_meta meta;
+ const char *error_name;
+ const char *error_msg;
+ uint64_t old_chain_id;
+ DBusMessage *reply;
+ errno_t ret;
+
+ /* This is a top level request and a place where we loose tracking of the
+ * correct chain id. We got here from sbus_incoming_request_done
+ * which may finish multiple identical requests at once but we know chain
+ * id only of the one requests that actually run its handler.
+ *
+ * Therefore we need to set the id to 0 since it is not known at this
+ * moment, but it is ok. */
+ old_chain_id = sss_chain_id_set(0);
+
+ state = tevent_req_callback_data(subreq, struct sbus_issue_request_state);
+ sbus_message_meta_read(state->message, &meta);
+
+ ret = sbus_incoming_request_recv(state, subreq, &reply);
+ talloc_zfree(subreq);
+
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC, "%s.%s: Success\n",
+ meta.interface, meta.member);
+ } else {
+ int msg_level = SSSDBG_OP_FAILURE;
+ if (ret == ERR_MISSING_DP_TARGET) msg_level = SSSDBG_FUNC_DATA;
+ DEBUG(msg_level, "%s.%s: Error [%d]: %s\n",
+ meta.interface, meta.member, ret, sss_strerror(ret));
+ }
+
+ /* Signals do not send a reply. */
+ if (state->type == SBUS_REQUEST_SIGNAL) {
+ goto done;
+ }
+
+ if (ret == EOK) {
+ /* sbus_reply decreases the refcount of @reply. This usuall means that
+ * refcount drops to zero and the message is freed. However, under
+ * special circumstances the refcount is increased inside libdbus,
+ * the refcount will be 1 when we leave the function and we drop it
+ * to zero in talloc_free(state) later in this function. This will
+ * leave an invalid message to be send inside dbus connection and
+ * eventually crash.
+ *
+ * Increasing the refcount here makes sure that the refcount is always
+ * correct. */
+ dbus_message_ref(reply);
+ sbus_reply(state->conn, reply);
+ } else {
+ sbus_errno_to_error(state, ret, &error_name, &error_msg);
+ sbus_reply_error(state->conn, state->message, error_name, error_msg);
+ }
+
+done:
+ if (ret == ERR_SBUS_KILL_CONNECTION) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Handler requested to kill the connection!\n");
+ sbus_connection_free(state->conn);
+ }
+
+ talloc_free(state);
+
+ sss_chain_id_set(old_chain_id);
+}
+
+DBusHandlerResult
+sbus_method_handler(struct sbus_connection *conn,
+ struct sbus_router *router,
+ struct sbus_message_meta *meta,
+ DBusMessage *message)
+{
+ const struct sbus_method *method;
+ struct sbus_interface *iface;
+ TALLOC_CTX *error_ctx;
+ const char *error_name;
+ const char *error_msg;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Received D-Bus method %s.%s on %s\n",
+ meta->interface, meta->member, meta->path);
+
+ /* Mark this connection as active. */
+ sbus_connection_mark_active(conn);
+
+ iface = sbus_router_paths_lookup(router->paths, meta->path,
+ meta->interface);
+ if (iface == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown interface!\n");
+ sbus_reply_error(conn, message, DBUS_ERROR_UNKNOWN_INTERFACE,
+ meta->interface);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ method = sbus_interface_find_method(iface, meta->member);
+ if (method == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown method!\n");
+ sbus_reply_error(conn, message, DBUS_ERROR_UNKNOWN_METHOD,
+ meta->member);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ sbus_annotation_warn(iface, method);
+
+ ret = sbus_issue_request(conn, meta, conn, message, SBUS_REQUEST_METHOD,
+ &method->invoker, &method->handler);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to issue request [%d]: %s\n",
+ ret, sss_strerror(ret));
+ if (ret == ENOMEM) {
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ error_ctx = talloc_new(NULL);
+ if (error_ctx == NULL) {
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ sbus_errno_to_error(error_ctx, ret, &error_name, &error_msg);
+ sbus_reply_error(conn, message, error_name, error_msg);
+ talloc_free(error_ctx);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+DBusHandlerResult
+sbus_signal_handler(struct sbus_connection *conn,
+ struct sbus_router *router,
+ struct sbus_message_meta *meta,
+ DBusMessage *message)
+{
+ struct sbus_listener_list *list;
+ struct sbus_listener_list *item;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Received D-Bus signal %s.%s on %s\n",
+ meta->interface, meta->member, meta->path);
+
+ list = sbus_router_listeners_lookup(router->listeners, meta->interface,
+ meta->member);
+ if (list == NULL) {
+ /* Most probably not fully initialized yet */
+ DEBUG(SSSDBG_FUNC_DATA, "We do not listen to this signal!\n");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ DLIST_FOR_EACH(item, list) {
+ ret = sbus_issue_request(conn, meta, conn, message,
+ SBUS_REQUEST_SIGNAL,
+ &item->listener->invoker,
+ &item->listener->handler);
+ if (ret != EOK) {
+ /* Nothing to do, try the next one. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to issue request [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+DBusHandlerResult
+sbus_router_filter(struct sbus_connection *conn,
+ struct sbus_router *router,
+ DBusMessage *message)
+{
+ struct sbus_message_meta meta;
+
+ sbus_message_meta_read(message, &meta);
+
+ switch (meta.type) {
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ return sbus_signal_handler(conn, router, &meta, message);
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ return sbus_method_handler(conn, router, &meta, message);
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ case DBUS_MESSAGE_TYPE_ERROR:
+ /* This will be processed by the caller. */
+ return DBUS_HANDLER_RESULT_HANDLED;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid message type: %d\n", meta.type);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+DBusHandlerResult
+sbus_connection_filter(DBusConnection *dbus_conn,
+ DBusMessage *message,
+ void *handler_data)
+{
+ struct sbus_connection *conn;
+
+ conn = talloc_get_type(handler_data, struct sbus_connection);
+
+ return sbus_router_filter(conn, conn->router, message);
+}
diff --git a/src/sbus/router/sbus_router_hash.c b/src/sbus/router/sbus_router_hash.c
new file mode 100644
index 0000000..2d407b2
--- /dev/null
+++ b/src/sbus/router/sbus_router_hash.c
@@ -0,0 +1,547 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <dhash.h>
+#include <string.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "sbus/sbus_opath.h"
+#include "sbus/sbus_private.h"
+#include "util/sss_ptr_hash.h"
+
+static struct sbus_interface *
+sbus_interface_list_lookup(struct sbus_interface_list *list,
+ const char *name)
+{
+ struct sbus_interface_list *item;
+
+ DLIST_FOR_EACH(item, list) {
+ if (strcmp(item->interface->name, name) == 0) {
+ return item->interface;
+ }
+ }
+
+ return NULL;
+}
+
+static errno_t
+sbus_interface_list_copy(TALLOC_CTX *mem_ctx,
+ struct sbus_interface_list *list,
+ struct sbus_interface_list **_copy)
+{
+ TALLOC_CTX *list_ctx;
+ struct sbus_interface_list *list_copy;
+ struct sbus_interface_list *item_copy;
+ struct sbus_interface_list *item;
+ struct sbus_interface *iface;
+ errno_t ret;
+
+ if (list == NULL) {
+ *_copy = NULL;
+ return EOK;
+ }
+
+ /* Create a memory context that will be used as a parent for copies. */
+ list_ctx = talloc_new(mem_ctx);
+ if (list_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ /* Start with an empty list. */
+ list_copy = NULL;
+ DLIST_FOR_EACH(item, list) {
+ iface = sbus_interface_list_lookup(list_copy, item->interface->name);
+ if (iface != NULL) {
+ /* This interface already exist in the list. */
+ continue;
+ }
+
+ /* Create a copy of this item and insert it into the list. */
+ item_copy = talloc_zero(list_ctx, struct sbus_interface_list);
+ if (item_copy == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item_copy->interface = item->interface;
+ DLIST_ADD(list_copy, item_copy);
+ }
+
+ *_copy = list_copy;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(list_ctx);
+ }
+
+ return ret;
+}
+
+hash_table_t *
+sbus_router_paths_init(TALLOC_CTX *mem_ctx)
+{
+ return sss_ptr_hash_create(mem_ctx, NULL, NULL);
+}
+
+errno_t
+sbus_router_paths_add(hash_table_t *table,
+ const char *path,
+ struct sbus_interface *iface)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_interface_list *list;
+ struct sbus_interface_list *item;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ item = talloc_zero(tmp_ctx, struct sbus_interface_list);
+ if (item == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item->interface = sbus_interface_copy(item, iface);
+ if (item->interface == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First, check if the path already exist and just append the interface
+ * to the list if it does (but only if the interface does not exist). */
+ list = sss_ptr_hash_lookup(table, path, struct sbus_interface_list);
+ if (list != NULL) {
+ if (sbus_interface_list_lookup(list, iface->name) != NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Trying to register the same interface"
+ " twice: iface=%s, opath=%s\n", iface->name, path);
+ ret = EEXIST;
+ goto done;
+ }
+
+ DLIST_ADD_END(list, item, struct sbus_interface_list *);
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise create new hash entry and new list. */
+ list = item;
+
+ ret = sss_ptr_hash_add(table, path, list, struct sbus_interface_list);
+
+done:
+ if (ret == EOK) {
+ talloc_steal(table, item);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+/**
+ * First @object_path is looked up in @table, if it is not found it steps up
+ * in the path hierarchy and try to lookup the parent node. This continues
+ * until the root is reached.
+ */
+struct sbus_interface *
+sbus_router_paths_lookup(hash_table_t *table,
+ const char *path,
+ const char *iface_name)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_interface_list *list;
+ struct sbus_interface *iface;
+ const char *lookup_path;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ iface = NULL;
+ lookup_path = path;
+ while (lookup_path != NULL) {
+ list = sss_ptr_hash_lookup(table, lookup_path,
+ struct sbus_interface_list);
+ if (list != NULL) {
+ iface = sbus_interface_list_lookup(list, iface_name);
+ if (iface != NULL) {
+ goto done;
+ }
+ }
+
+ /* We will not free lookup path since it is freed with tmp_ctx
+ * and the object paths are supposed to be small. */
+ lookup_path = sbus_opath_subtree_parent(tmp_ctx, lookup_path);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return iface;
+}
+
+/**
+ * Acquire list of all interfaces that are supported on given object path.
+ */
+errno_t
+sbus_router_paths_supported(TALLOC_CTX *mem_ctx,
+ hash_table_t *table,
+ const char *path,
+ struct sbus_interface_list **_list)
+{
+ TALLOC_CTX *tmp_ctx;
+ TALLOC_CTX *list_ctx;
+ struct sbus_interface_list *list;
+ struct sbus_interface_list *list_copy;
+ struct sbus_interface_list *list_output;
+ const char *lookup_path;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ list_ctx = talloc_new(tmp_ctx);
+ if (list_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Start with an empty list. */
+ list_output = NULL;
+ lookup_path = path;
+ while (lookup_path != NULL) {
+ list = sss_ptr_hash_lookup(table, lookup_path,
+ struct sbus_interface_list);
+ if (list != NULL) {
+ ret = sbus_interface_list_copy(list_ctx, list, &list_copy);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DLIST_CONCATENATE(list_output, list_copy,
+ struct sbus_interface_list *);
+ }
+
+ /* We will not free lookup path since it is freed with tmp_ctx
+ * and the object paths are supposed to be small. */
+ lookup_path = sbus_opath_subtree_parent(tmp_ctx, lookup_path);
+ }
+
+ talloc_steal(mem_ctx, list_ctx);
+ *_list = list_output;
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+const char **
+sbus_router_paths_nodes(TALLOC_CTX *mem_ctx,
+ hash_table_t *table)
+{
+ const char **paths = NULL;
+ hash_key_t *keys;
+ unsigned long count;
+ unsigned long i, j;
+ char *basepath;
+ errno_t ret;
+ int hret;
+
+ hret = hash_keys(table, &count, &keys);
+ if (hret != HASH_SUCCESS) {
+ return NULL;
+ }
+
+ paths = talloc_zero_array(mem_ctx, const char *, count + 2);
+ if (paths == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0, j = 0; i < count; i++) {
+ /* Do not include subtree paths. The must have node factory. */
+ basepath = keys[i].str;
+ if (sbus_opath_is_subtree(basepath)) {
+ basepath = sbus_opath_subtree_base(paths, basepath);
+ if (basepath == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (sbus_router_paths_exist(table, basepath)) {
+ talloc_free(basepath);
+ continue;
+ }
+ }
+
+ if (strcmp(basepath, "/") == 0) {
+ continue;
+ }
+
+ /* All paths starts with / that is not part of the node name. */
+ paths[j] = basepath + 1;
+ j++;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(keys);
+
+ if (ret != EOK) {
+ talloc_zfree(paths);
+ }
+
+ return paths;
+}
+
+bool
+sbus_router_paths_exist(hash_table_t *table,
+ const char *object_path)
+{
+ return sss_ptr_hash_has_key(table, object_path);
+}
+
+static struct sbus_listener *
+sbus_listener_list_lookup(struct sbus_listener_list *list,
+ struct sbus_listener *a)
+{
+ struct sbus_listener_list *item;
+ struct sbus_listener *b;
+
+ /* We know that interface and signal name already match. We need to check
+ * handlers and object paths. */
+ DLIST_FOR_EACH(item, list) {
+ b = item->listener;
+
+ if (memcmp(&a->handler, &b->handler, sizeof(struct sbus_handler)) != 0) {
+ continue;
+ }
+
+ if (a->object_path == NULL && b->object_path == NULL) {
+ return b;
+ }
+
+ if (a->object_path == NULL && b->object_path != NULL) {
+ continue;
+ }
+
+ if (a->object_path != NULL && b->object_path == NULL) {
+ continue;
+ }
+
+ if (strcmp(a->object_path, b->object_path) != 0) {
+ continue;
+ }
+
+ return b;
+ }
+
+ return NULL;
+}
+
+static void
+sbus_router_listeners_delete_cb(hash_entry_t *item,
+ hash_destroy_enum deltype,
+ void *pvt)
+{
+ struct sbus_connection *conn;
+ char *signal_name;
+ char *interface;
+ char *rule;
+ errno_t ret;
+
+ conn = talloc_get_type(pvt, struct sbus_connection);
+ if (conn->connection == NULL) {
+ return;
+ }
+
+ if (conn->disconnecting) {
+ return;
+ }
+
+ /* If we still have the D-Bus connection available, we try to unregister
+ * the previously registered listener when its removed from table. */
+
+ ret = sbus_router_signal_parse(NULL, item->key.str,
+ &interface, &signal_name);
+ if (ret != EOK) {
+ /* There is nothing we can do. */
+ return;
+ }
+
+ rule = sbus_router_signal_rule(NULL, interface, signal_name);
+ talloc_free(interface);
+ talloc_free(signal_name);
+ if (rule == NULL) {
+ /* There is nothing we can do. */
+ return;
+ }
+
+ dbus_bus_remove_match(conn->connection, rule, NULL);
+
+ talloc_free(rule);
+}
+
+hash_table_t *
+sbus_router_listeners_init(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn)
+{
+ return sss_ptr_hash_create(mem_ctx, sbus_router_listeners_delete_cb, conn);
+}
+
+errno_t
+sbus_router_listeners_add(hash_table_t *table,
+ const char *interface,
+ const char *signal_name,
+ struct sbus_listener *listener,
+ bool *_signal_known)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_listener_list *list;
+ struct sbus_listener_list *item;
+ bool signal_known = false;
+ const char *key;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ key = talloc_asprintf(tmp_ctx, "%s.%s", interface, signal_name);
+ if (key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item = talloc_zero(tmp_ctx, struct sbus_listener_list);
+ if (item == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item->listener = sbus_listener_copy(item, listener);
+ if (item->listener == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* First, check if the listener already exist and just append it to the
+ * list if it does (but only if this listener doesn't already exist. */
+ list = sss_ptr_hash_lookup(table, key, struct sbus_listener_list);
+ if (list != NULL) {
+ signal_known = true;
+
+ if (sbus_listener_list_lookup(list, listener) != NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Trying to register the same listener"
+ " twice: iface=%s, signal=%s, path=%s\n",
+ interface, signal_name, (listener->object_path == NULL ?
+ "<null>": listener->object_path));
+ ret = EEXIST;
+ goto done;
+ }
+
+ DLIST_ADD_END(list, item, struct sbus_listener_list *);
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise create new hash entry and new list. */
+ signal_known = false;
+ list = item;
+
+ ret = sss_ptr_hash_add(table, key, list, struct sbus_listener_list);
+
+done:
+ if (ret == EOK) {
+ talloc_steal(table, item);
+ *_signal_known = signal_known;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+struct sbus_listener_list *
+sbus_router_listeners_lookup(hash_table_t *table,
+ const char *interface,
+ const char *signal_name)
+{
+ struct sbus_listener_list *list;
+ char *key;
+
+ key = talloc_asprintf(NULL, "%s.%s", interface, signal_name);
+ if (key == NULL) {
+ return NULL;
+ }
+
+ list = sss_ptr_hash_lookup(table, key, struct sbus_listener_list);
+ talloc_free(key);
+
+ return list;
+}
+
+hash_table_t *
+sbus_router_nodes_init(TALLOC_CTX *mem_ctx)
+{
+ return sss_ptr_hash_create(mem_ctx, NULL, NULL);
+}
+
+errno_t
+sbus_router_nodes_add(hash_table_t *table,
+ struct sbus_node *node)
+{
+ struct sbus_node *copy;
+ errno_t ret;
+
+ copy = sbus_node_copy(table, node);
+ if (copy == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ptr_hash_add(table, copy->path, copy, struct sbus_node);
+ if (ret != EOK) {
+ talloc_free(copy);
+ return ret;
+ }
+
+ return EOK;
+}
+
+struct sbus_node *
+sbus_router_nodes_lookup(hash_table_t *table,
+ const char *path)
+{
+ return sss_ptr_hash_lookup(table, path, struct sbus_node);
+}
diff --git a/src/sbus/sbus.h b/src/sbus/sbus.h
new file mode 100644
index 0000000..0983879
--- /dev/null
+++ b/src/sbus/sbus.h
@@ -0,0 +1,460 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_H_
+#define _SBUS_H_
+
+#include <dhash.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_typeof.h"
+#include "sbus/sbus_declarations.h"
+#include "sbus/sbus_interface.h"
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_errors.h"
+
+struct sbus_listener;
+struct sbus_connection;
+struct sbus_server;
+struct sbus_node;
+
+/**
+ * Connect to D-Bus system bus, naming this end-point @dbus_name.
+ *
+ * If @last_activity_time pointer is given, it is updated with current time
+ * each time an important event (such as method or property call) on the bus
+ * occurs. It is not updated when an signal arrives.
+ *
+ * @param mem_ctx Memory context.
+ * @param ev Tevent context.
+ * @param dbus_name Name of this end-point.
+ * @param last_activity_time Pointer to a time that is updated each time
+ * an event occurs.
+ *
+ * @return New sbus connection or NULL on error.
+ */
+struct sbus_connection *
+sbus_connect_system(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *dbus_name,
+ time_t *last_activity_time);
+
+/**
+ * Connect to a private D-Bus bus at @address.
+ *
+ * If @last_activity_time pointer is given, it is updated with current time
+ * each time an important event (such as method or property call) on the bus
+ * occurs. It is not updated when an signal arrives.
+ *
+ * @param mem_ctx Memory context.
+ * @param ev Tevent context.
+ * @param address Remote end-point address.
+ * @param dbus_name Name of this end-point.
+ * @param last_activity_time Pointer to a time that is updated each time
+ * an event occurs.
+ *
+ * @return New sbus connection or NULL on error.
+ *
+ * @see sbus_server_create
+ */
+struct sbus_connection *
+sbus_connect_private(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *address,
+ const char *dbus_name,
+ time_t *last_activity_time);
+
+/**
+ * Connect to a private D-Bus bus at @address an perform its initialization
+ * asynchronously. Usually, you can just call @sbus_connect_private which
+ * will block for a while during Hello and RequestName calls, which is mostly
+ * ok since it is done during process initialization. However, you have to
+ * use asynchronous call if you are connecting to a server which runs on the
+ * same process, otherwise it will end up in dead lock.
+ *
+ * If @last_activity_time pointer is given, it is updated with current time
+ * each time an important event (such as method or property call) on the bus
+ * occurs. It is not updated when an signal arrives.
+ *
+ * @param mem_ctx Memory context.
+ * @param ev Tevent context.
+ * @param address Remote end-point address.
+ * @param dbus_name Name of this end-point.
+ * @param last_activity_time Pointer to a time that is updated each time
+ * an event occurs.
+ *
+ * @return Tevent request or NULL on error.
+ *
+ * @see sbus_server_create
+ */
+struct tevent_req *
+sbus_connect_private_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *address,
+ const char *dbus_name,
+ time_t *last_activity_time);
+
+/**
+ * Recieve reply from @sbus_connect_private_send.
+ *
+ * @param mem_ctx Memory context.
+ * @param req Tevent request.
+ * @param _conn Established sbus connection.
+ *
+ * @return EOK on success, other errno code on failure.
+ *
+ * @see sbus_server_create
+ */
+errno_t sbus_connect_private_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sbus_connection **_conn);
+
+/**
+ * Create a new sbus server at socket address @address.
+ *
+ * @param mem_ctx Memory context.
+ * @param ev Tevent context.
+ * @param address Socket address.
+ * @param use_symlink If a symlink to @address should be created.
+ * @param uid Socket owner uid.
+ * @param gid Socket owner gid.
+ * @param on_conn_cb On new connection callback function.
+ * @param on_conn_data Private data passed to the callback.
+ *
+ * @return New sbus server or NULL on error.
+ */
+struct sbus_server *
+sbus_server_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *address,
+ bool use_symlink,
+ uint32_t max_connections,
+ uid_t uid,
+ gid_t gid,
+ sbus_server_on_connection_cb on_conn_cb,
+ sbus_server_on_connection_data on_conn_data);
+
+/**
+ * Create a new sbus server at socket address @address and connect to it.
+ *
+ * @param mem_ctx Memory context.
+ * @param ev Tevent context.
+ * @param dbus_name Name of the connection.
+ * @param last_activity_time Pointer to a time that is updated each time
+ * an event occurs on connection.
+ * @param address Socket address.
+ * @param use_symlink If a symlink to @address should be created.
+ * @param uid Socket owner uid.
+ * @param gid Socket owner gid.
+ * @param on_conn_cb On new connection callback function.
+ * @param on_conn_data Private data passed to the callback.
+ *
+ * @return Tevent request or NULL on error.
+ */
+struct tevent_req *
+sbus_server_create_and_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *dbus_name,
+ time_t *last_activity_time,
+ const char *address,
+ bool use_symlink,
+ uint32_t max_connections,
+ uid_t uid,
+ gid_t gid,
+ sbus_server_on_connection_cb on_conn_cb,
+ sbus_server_on_connection_data on_conn_data);
+
+/**
+ * Receive reply from @sbus_server_create_and_connect_send.
+ *
+ * @param mem_ctx Memory context.
+ * @param req Tevent request.
+ * @param _server Created sbus server.
+ * @param _conn Established sbus connection.
+ *
+ * @return EOK on success, other errno code on failure.
+ *
+ * @see sbus_server_create_and_connect_send
+ */
+errno_t
+sbus_server_create_and_connect_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sbus_server **_server,
+ struct sbus_connection **_conn);
+
+/**
+ * Find active sbus connection by its name.
+ *
+ * @param server An sbus server.
+ * @param name Connection unique or well-known name.
+ *
+ * @return The sbus connection associated with name or NULL if not found.
+ */
+struct sbus_connection *
+sbus_server_find_connection(struct sbus_server *server, const char *name);
+
+/**
+ * Set server callback that is run everytime a new connection is established
+ * with the server.
+ *
+ * Callback is of type:
+ * errno_t callback(struct sbus_connection *conn,
+ * data_type *data)
+ *
+ * where @conn is the newly established sbus connection. If other error code
+ * than EOK is returned by the callback, the connection is killed.
+ *
+ * @param server An sbus server.
+ * @param callback Callback function.
+ * @param data Private data passed to the callback.
+ */
+#define sbus_server_set_on_connection(server, callback, data) do { \
+ SBUS_CHECK_FUNCTION(callback, errno_t, \
+ struct sbus_connection *, \
+ SBUS_TYPEOF(data)); \
+ _sbus_server_set_on_connection((server), #callback, \
+ (sbus_server_on_connection_cb)callback, \
+ (sbus_server_on_connection_data)data); \
+} while(0)
+
+/**
+ * Set custom destructor on an sbus connection.
+ *
+ * This destructor is called when a connection is being freed after it
+ * is finalized. It is not allowed to use further manipulate with this
+ * connection within the destructor.
+ *
+ * Destructor is of type:
+ * void my_destructor(data_type *data)
+ *
+ * @param conn An sbus connection.
+ * @param destructor Destructor function.
+ * @param data Private data passed to the destructor.
+ */
+#define sbus_connection_set_destructor(conn, destructor, data) do { \
+ SBUS_CHECK_FUNCTION(destructor, void, SBUS_TYPEOF(data)); \
+ _sbus_connection_set_destructor((conn), #destructor, \
+ (sbus_connection_destructor_fn)destructor, \
+ (sbus_connection_destructor_data)data); \
+} while(0)
+
+/**
+ * Set custom access check function on an sbus connection.
+ *
+ * This function is called on each incoming sbus request to check whether
+ * the caller has enough permissions to run such request.
+ *
+ * Access check function is of type:
+ * errno_t my_access_check(struct sbus_request *sbus_request, data_type *data)
+ *
+ * The function shall return EOK if access is granted, EPERM if access is
+ * denied and other errno code on error.
+ *
+ * @param conn An sbus connection.
+ * @param check_fn Access check function.
+ * @param data Private data passed to the access check function.
+ */
+#define sbus_connection_set_access_check(conn, check_fn, data) do { \
+ SBUS_CHECK_FUNCTION(check_fn, errno_t, \
+ struct sbus_request *, \
+ SBUS_TYPEOF(data)); \
+ _sbus_connection_set_access_check((conn), #check_fn, \
+ (sbus_connection_access_check_fn)check_fn, \
+ (sbus_connection_access_check_data)data); \
+} while(0)
+
+/**
+ * Set connection private data.
+ *
+ * @param conn An sbus connection.
+ * @param data Private data.
+ */
+void sbus_connection_set_data(struct sbus_connection *conn,
+ void *data);
+
+/**
+ * Retrieve connection private data.
+ *
+ * @param conn An sbus connection.
+ * @param type Private data type.
+ */
+#define sbus_connection_get_data(conn, type) \
+ talloc_get_type(_sbus_connection_get_data(conn), type)
+
+/**
+ * Reconnection status that is pass to a reconnection callback.
+ */
+enum sbus_reconnect_status {
+ /**
+ * Reconnection was successful.
+ */
+ SBUS_RECONNECT_SUCCESS,
+
+ /**
+ * Reconnection failed because maximum number of retires was exceeded.
+ */
+ SBUS_RECONNECT_EXCEEDED_RETRIES,
+
+ /**
+ * Reconnection failed due to unspecified error.
+ */
+ SBUS_RECONNECT_ERROR
+};
+
+/**
+ * Enable automatic reconnection when an sbus connection is dropped.
+ *
+ * You can also set a callback that is called upon successful or
+ * unsuccessful reconnection.
+ *
+ * Callback is of type:
+ * void callback(struct sbus_connection *conn,
+ * enum sbus_reconnect_status status,
+ * data_type *data)
+ *
+ * @param conn An sbus connection.
+ * @param max_retries Maximum number of reconnection retries.
+ * @param callback Callback function.
+ * @param data Private data passed to the callback.
+ */
+#define sbus_reconnect_enable(conn, max_retries, callback, data) do { \
+ SBUS_CHECK_FUNCTION(callback, void, \
+ struct sbus_connection *, \
+ enum sbus_reconnect_status, \
+ SBUS_TYPEOF(data)); \
+ _sbus_reconnect_enable((conn), max_retries, \
+ (sbus_reconnect_cb)callback, (sbus_reconnect_data)data); \
+} while(0)
+
+/**
+ * Associate an object path with an sbus interface. The object @path may also
+ * contain an asterisk at the end to indicate that the interface should be
+ * applied for the path subtree.
+ */
+struct sbus_path {
+ const char *path;
+ struct sbus_interface *iface;
+};
+
+/**
+ * Add new object or subtree path to the connection router.
+ *
+ * The specified interface will be associated with this path. You can add
+ * single path multiple times if you want this path to have more interfaces
+ * associated.
+ *
+ * @param conn An sbus connection.
+ * @param path Object or subtree path.
+ * @param iface An sbus interface.
+ *
+ * @return EOK or other error code on failure.
+ */
+errno_t
+sbus_connection_add_path(struct sbus_connection *conn,
+ const char *path,
+ struct sbus_interface *iface);
+
+/**
+ * Associate multiple object paths with interfaces at once.
+ *
+ * The paths and interfaces are associated through @map which is
+ * NULL terminated array of @sbus_router_path.
+ *
+ * @param conn An sbus connection.
+ * @param map <path, interface> pairs to add into router.
+ *
+ * @return EOK or other error code on failure.
+ */
+errno_t
+sbus_connection_add_path_map(struct sbus_connection *conn,
+ struct sbus_path *map);
+
+/**
+ * Add new signal listener to the router.
+ *
+ * Create a new listener with @SBUS_LISTEN_SYNC or @SBUS_LISTEN_ASYNC.
+ *
+ * @param conn An sbus connection.
+ * @param listener An sbus signal listerner.
+ *
+ * @return EOK or other error code on failure.
+ *
+ * @see SBUS_LISTENERS, SBUS_LISTEN_SYNC, SBUS_LISTEN_ASYNC
+ */
+errno_t
+sbus_router_listen(struct sbus_connection *conn,
+ struct sbus_listener *listener);
+
+/**
+ * Add multiple signal listeners to the router at once.
+ *
+ * @param conn An sbus connection.
+ * @param listener An sbus signal listener array.
+ *
+ * @return EOK or other error code on failure.
+ *
+ * @see SBUS_LISTENERS, SBUS_LISTEN_SYNC, SBUS_LISTEN_ASYNC
+ */
+errno_t
+sbus_router_listen_map(struct sbus_connection *conn,
+ struct sbus_listener *map);
+
+/**
+ * Register new node with the router.
+ *
+ * Each node is associated with a node factory which is a function that
+ * returns list of node object names for given object path.
+ *
+ * Create a new node with @SBUS_NODE_SYNC or @SBUS_NODE_ASYNC.
+ *
+ * @param conn An sbus connection.
+ * @param node An sbus node description.
+ *
+ * @return EOK or other error code on failure
+ *
+ * @see SBUS_NODES, SBUS_NODE_SYNC, SBUS_NODE_ASYNC
+ */
+errno_t
+sbus_router_add_node(struct sbus_connection *conn,
+ struct sbus_node *node);
+
+/**
+ * Register multiple nodes with the router at once. Each node is associated
+ * with a node factory which is a function that returns list of node object
+ * names or given object path.
+ *
+ * @param conn An sbus connection.
+ * @param node An sbus node description array.
+ *
+ * @return EOK or other error code on failure.
+ *
+ * @see SBUS_NODES, SBUS_NODE_SYNC, SBUS_NODE_ASYNC
+ */
+errno_t
+sbus_router_add_node_map(struct sbus_connection *conn,
+ struct sbus_node *map);
+
+/* Get connection name, well known name is preferred. */
+const char * sbus_connection_get_name(struct sbus_connection *conn);
+
+#endif /* _SBUS_H_ */
diff --git a/src/sbus/sbus_annotations.h b/src/sbus/sbus_annotations.h
new file mode 100644
index 0000000..7319855
--- /dev/null
+++ b/src/sbus/sbus_annotations.h
@@ -0,0 +1,27 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_ANNOTATIONS_H_
+#define _SBUS_ANNOTATIONS_H_
+
+#define SBUS_ANNOTATION_DEPRECATED "org.freedesktop.DBus.Deprecated"
+#define SBUS_ANNOTATION_DEPRECATED_BY "org.freedesktop.sbus.DeprecatedBy"
+
+#endif /* _SBUS_ANNOTATIONS_H_ */
diff --git a/src/sbus/sbus_declarations.h b/src/sbus/sbus_declarations.h
new file mode 100644
index 0000000..facf0e6
--- /dev/null
+++ b/src/sbus/sbus_declarations.h
@@ -0,0 +1,112 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_DECLARATIONS_H_
+#define _SBUS_DECLARATIONS_H_
+
+#include <sys/types.h>
+#include <tevent.h>
+#include <talloc.h>
+
+#include "util/util.h"
+
+/*****************************************************************************
+ *
+ * This file contains declarations of symbols that must be generally available
+ * to the user but that must not be used on their own so they should not be
+ * present in sbus.h or other header files.
+ *
+ * Do not include this file directly.
+ *
+ *****************************************************************************/
+
+struct sbus_request;
+struct sbus_connection;
+struct sbus_server;
+enum sbus_reconnect_status;
+
+/* Connection custom destructor function. */
+typedef void * sbus_connection_destructor_data;
+typedef void
+(*sbus_connection_destructor_fn)(sbus_connection_destructor_data);
+
+/* Reconnection callback. */
+typedef void * sbus_reconnect_data;
+typedef void
+(*sbus_reconnect_cb)(struct sbus_connection *,
+ enum sbus_reconnect_status,
+ sbus_reconnect_data);
+
+/* Access check function. */
+typedef void * sbus_connection_access_check_data;
+typedef errno_t
+(*sbus_connection_access_check_fn)(struct sbus_request *,
+ sbus_connection_access_check_data);
+
+/* On new server connection function. */
+typedef void * sbus_server_on_connection_data;
+typedef errno_t
+(*sbus_server_on_connection_cb)(struct sbus_connection *,
+ sbus_server_on_connection_data);
+
+/**
+ * This function is wrapped with sbus_connection_set_destructor macro.
+ * Please, use this macro instead.
+ *
+ * @see sbus_connection_set_destructor
+ */
+void _sbus_connection_set_destructor(struct sbus_connection *conn,
+ const char *name,
+ sbus_connection_destructor_fn fn,
+ sbus_connection_destructor_data data);
+
+/**
+ * @see sbus_connection_set_access_check
+ */
+void
+_sbus_connection_set_access_check(struct sbus_connection *conn,
+ const char *name,
+ sbus_connection_access_check_fn check_fn,
+ sbus_connection_access_check_data data);
+
+/**
+ * @see sbus_connection_get_data
+ */
+void *_sbus_connection_get_data(struct sbus_connection *conn);
+
+/**
+ * @see sbus_reconnect_enable
+ */
+void
+_sbus_reconnect_enable(struct sbus_connection *conn,
+ unsigned int max_retries,
+ sbus_reconnect_cb callback,
+ sbus_reconnect_data callback_data);
+
+/**
+ * @see sbus_server_set_on_connection
+ */
+void
+_sbus_server_set_on_connection(struct sbus_server *server,
+ const char *name,
+ sbus_server_on_connection_cb on_connection_cb,
+ sbus_server_on_connection_data data);
+
+#endif /* _SBUS_DECLARATIONS_H_ */
diff --git a/src/sbus/sbus_errors.c b/src/sbus/sbus_errors.c
new file mode 100644
index 0000000..c6449dd
--- /dev/null
+++ b/src/sbus/sbus_errors.c
@@ -0,0 +1,127 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <errno.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/util_errors.h"
+#include "util/strtonum.h"
+#include "sbus/sbus_errors.h"
+
+static const struct {
+ const char *name;
+ errno_t ret;
+} sbus_error_table[] = {
+ /* Custom errors. */
+ { SBUS_ERROR_INTERNAL, ERR_INTERNAL },
+ { SBUS_ERROR_NOT_FOUND, ENOENT },
+ { SBUS_ERROR_KILLED, ERR_SBUS_KILL_CONNECTION },
+ { SBUS_ERROR_NO_CA, ERR_CA_DB_NOT_FOUND},
+
+ /* D-Bus standard errors. Some errno values may overlap, but when
+ * finding its D-Bus pair the first match is returned. */
+ { DBUS_ERROR_SERVICE_UNKNOWN, ERR_SBUS_UNKNOWN_SERVICE},
+ { DBUS_ERROR_UNKNOWN_INTERFACE, ERR_SBUS_UNKNOWN_INTERFACE},
+ { DBUS_ERROR_UNKNOWN_PROPERTY, ERR_SBUS_UNKNOWN_PROPERTY},
+ { DBUS_ERROR_NAME_HAS_NO_OWNER, ERR_SBUS_UNKNOWN_OWNER},
+ { DBUS_ERROR_NO_REPLY, ERR_SBUS_NO_REPLY},
+ { DBUS_ERROR_FAILED, EFAULT},
+ { DBUS_ERROR_NO_MEMORY, ENOMEM},
+ { DBUS_ERROR_TIMEOUT, ETIMEDOUT},
+ { DBUS_ERROR_NO_REPLY, ETIMEDOUT},
+ { DBUS_ERROR_IO_ERROR, EIO},
+ { DBUS_ERROR_BAD_ADDRESS, EFAULT},
+ { DBUS_ERROR_NOT_SUPPORTED, ENOTSUP},
+ { DBUS_ERROR_LIMITS_EXCEEDED, ERANGE},
+ { DBUS_ERROR_ACCESS_DENIED, EPERM},
+ { DBUS_ERROR_AUTH_FAILED, EACCES},
+ { DBUS_ERROR_NO_NETWORK, ENONET},
+ { DBUS_ERROR_DISCONNECTED, ERR_OFFLINE},
+ { DBUS_ERROR_INVALID_ARGS, EINVAL},
+
+ /* Should not happen so it can be as last item. */
+ { SBUS_ERROR_SUCCESS, EOK },
+ { NULL, -1 }
+};
+
+errno_t sbus_error_to_errno(DBusError *error)
+{
+ uint32_t ret;
+ int i;
+
+ if (!dbus_error_is_set(error)) {
+ return EOK;
+ }
+
+ if (dbus_error_has_name(error, SBUS_ERROR_ERRNO)) {
+ ret = strtouint32(error->message, NULL, 10);
+ if (errno != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected error format: [%s]\n",
+ error->message);
+ return ERR_INTERNAL;
+ } else if (ret == EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "An error was send but it indicates "
+ "success: [%s]\n", error->message);
+ return ERR_INTERNAL;
+ }
+
+ return ret;
+ }
+
+ for (i = 0; sbus_error_table[i].name != NULL; i++) {
+ if (dbus_error_has_name(error, sbus_error_table[i].name)) {
+ return sbus_error_table[i].ret;
+ }
+ }
+
+ return EIO;
+}
+
+void
+sbus_errno_to_error(TALLOC_CTX *mem_ctx,
+ errno_t ret,
+ const char **_error_name,
+ const char **_error_message)
+{
+ char *message;
+ int i;
+
+ for (i = 0; sbus_error_table[i].ret != -1; i++) {
+ if (sbus_error_table[i].ret == ret) {
+ *_error_name = sbus_error_table[i].name;
+ *_error_message = sss_strerror(ret);
+ return;
+ }
+ }
+
+ /* Error code was not translated. Create generic errno message. */
+ message = talloc_asprintf(mem_ctx, "%u: %s", ret, sss_strerror(ret));
+ if (message == NULL) {
+ *_error_name = DBUS_ERROR_NO_MEMORY;
+ *_error_message = sss_strerror(ENOMEM);
+ return;
+ }
+
+ *_error_name = SBUS_ERROR_ERRNO;
+ *_error_message = message;
+
+ return;
+}
diff --git a/src/sbus/sbus_errors.h b/src/sbus/sbus_errors.h
new file mode 100644
index 0000000..e005f05
--- /dev/null
+++ b/src/sbus/sbus_errors.h
@@ -0,0 +1,45 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_ERRORS_H_
+#define _SBUS_ERRORS_H_
+
+#include <errno.h>
+#include <dbus/dbus.h>
+
+#include "util/util_errors.h"
+
+#define SBUS_ERROR_SUCCESS "sbus.Error.Success"
+
+#define SBUS_ERROR_INTERNAL "sbus.Error.Internal"
+#define SBUS_ERROR_NOT_FOUND "sbus.Error.NotFound"
+#define SBUS_ERROR_KILLED "sbus.Error.ConnectionKilled"
+#define SBUS_ERROR_NO_CA "sbus.Error.NoCA"
+#define SBUS_ERROR_ERRNO "sbus.Error.Errno"
+
+errno_t sbus_error_to_errno(DBusError *error);
+
+void
+sbus_errno_to_error(TALLOC_CTX *mem_ctx,
+ errno_t ret,
+ const char **_error_name,
+ const char **_error_message);
+
+#endif /* _SBUS_ERRORS_H_ */
diff --git a/src/sbus/sbus_interface.h b/src/sbus/sbus_interface.h
new file mode 100644
index 0000000..2312fde
--- /dev/null
+++ b/src/sbus/sbus_interface.h
@@ -0,0 +1,522 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_INTERFACE_H_
+#define _SBUS_INTERFACE_H_
+
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_interface_declarations.h"
+
+struct sbus_interface;
+struct sbus_listener;
+struct sbus_node;
+
+/**
+ * Indicate that the interface has no methods.
+ */
+#define SBUS_NO_METHODS SBUS_INTERFACE_SENTINEL
+
+/**
+ * Indicate that the interface has no signals.
+ */
+#define SBUS_NO_SIGNALS SBUS_INTERFACE_SENTINEL
+
+/**
+ * Indicate that the interface has no properties.
+ */
+#define SBUS_NO_PROPERTIES SBUS_INTERFACE_SENTINEL
+
+/**
+ * Add sbus methods into the interface. If the interface does not contain any
+ * methods, please use SBUS_NO_METHODS or SBUS_WITHOUT_METHODS.
+ *
+ * @see SBUS_SYNC, SBUS_ASYNC, SBUS_NO_METHODS, SBUS_WITHOUT_METHODS
+ *
+ * The following examples demonstrate the intended usage of this macro.
+ * Do not use it in any other way.
+ *
+ * @example Interface with two methods, one with synchronous handler,
+ * one with asynchronous handler.
+ *
+ * SBUS_INTERFACE(
+ * iface_variable,
+ * org_freedesktop_sssd,
+ * SBUS_METHODS(
+ * SBUS_SYNC (METHOD, org_freedekstop_sssd, UpdateMembers,
+ * update_members_sync, pvt_data),
+ * SBUS_ASYNC(METHOD, org_freedekstop_sssd, UpdateMembersAsync,
+ * update_members_send, update_members_recv,
+ * pvt_data)
+ * ),
+ * @signals,
+ * @properties
+ * );
+ *
+ * @example Interface with no methods.
+ *
+ * SBUS_INTERFACE(
+ * iface_variable,
+ * org_freedesktop_sssd,
+ * SBUS_METHODS(
+ * SBUS_NO_METHODS
+ * ),
+ * @signals,
+ * @properties
+ * );
+ *
+ * or
+ *
+ * SBUS_INTERFACE(
+ * iface_variable,
+ * org_freedesktop_sssd,
+ * SBUS_WITHOUT_METHODS,
+ * @signals,
+ * @properties
+ * );
+ */
+#define SBUS_METHODS(...) \
+ { \
+ __VA_ARGS__, \
+ SBUS_INTERFACE_SENTINEL \
+ }
+
+/**
+ * Add sbus signals into the interface. If the interface does not contain any
+ * signals, please use SBUS_NO_METHODS or SBUS_WITHOUT_METHODS.
+ *
+ * @see SBUS_EMIT, SBUS_NO_SIGNALS, SBUS_WITHOUT_SIGNALS
+ *
+ * The following examples demonstrate the intended usage of this macro.
+ * Do not use it in any other way.
+ *
+ * @example Interface that can emit a PropertyChanged signal.
+ *
+ * SBUS_INTERFACE(
+ * iface_variable,
+ * org_freedesktop_sssd,
+ * @methods,
+ * SBUS_SIGNALS(
+ * SBUS_EMIT(org_freedekstop_sssd, PropertyChanged)
+ * ),
+ * @properties
+ * );
+ *
+ * @example Interface with no signals.
+ *
+ * SBUS_INTERFACE(
+ * iface_variable,
+ * org_freedesktop_sssd,
+ * @methods,
+ * SBUS_SIGNALS(
+ * SBUS_NO_SIGNALS
+ * ),
+ * @properties
+ * );
+ *
+ * or
+ *
+ * SBUS_INTERFACE(
+ * iface_variable,
+ * org_freedesktop_sssd,
+ * @methods,
+ * SBUS_WITHOUT_SIGNALS,
+ * @properties
+ * );
+ */
+#define SBUS_SIGNALS(...) \
+ { \
+ __VA_ARGS__, \
+ SBUS_INTERFACE_SENTINEL \
+ }
+
+/**
+ * Add sbus properties into the interface. If the interface does not contain any
+ * property, please use SBUS_NO_PROPERTIES or SBUS_WITHOUT_PROPERTIES.
+ *
+ * @see SBUS_SYNC, SBUS_ASYNC, SBUS_NO_PROPERTIES, SBUS_WITHOUT_PROPERTIES
+ *
+ * The following examples demonstrate the intended usage of this macro.
+ * Do not use it in any other way.
+ *
+ * @example Interface with one property with asynchronous getter and
+ * synchronous setter.
+ *
+ * SBUS_INTERFACE(
+ * iface_variable,
+ * org_freedesktop_sssd,
+ * @methods,
+ * @signals,
+ * SBUS_PROPERTIES(
+ * SBUS_SYNC (GETTER, org_freedekstop_sssd, domain_name,
+ * set_domain_name, pvt_data),
+ * SBUS_ASYNC(GETTER, org_freedekstop_sssd, domain_name,
+ * get_domain_name_send, get_domain_name_recv,
+ * pvt_data)
+ * )
+ * );
+ *
+ * @example Interface with no properties.
+ *
+ * SBUS_INTERFACE(
+ * iface_variable,
+ * org_freedesktop_sssd,
+ * @methods,
+ * @signals,
+ * SBUS_PROPERTIES(
+ * SBUS_NO_PROPERTIES
+ * )
+ * );
+ *
+ * or
+ *
+ * SBUS_INTERFACE(
+ * iface_variable,
+ * org_freedesktop_sssd,
+ * @methods,
+ * @signals,
+ * SBUS_WITHOUT_PROPERTIES
+ * );
+ */
+#define SBUS_PROPERTIES(...) \
+ { \
+ __VA_ARGS__, \
+ SBUS_INTERFACE_SENTINEL \
+ }
+
+/**
+ * Create list of sbus signal listeners. You can register more than one
+ * handler for a single signal.
+ *
+ * @see SBUS_LISTEN_SYNC, SBUS_LISTEN_ASYNC
+ *
+ * @example Listen to two signal -- PropertyChanged and DomainEnabled.
+ *
+ * struct sbus_listener listeners[] = SBUS_LISTENERS(
+ * SBUS_LISTEN_SYNC (org_freedesktop_sssd, PropertyChanged,
+ * "/org/freedesktop/sssd/User1",
+ * on_propert_changed, pvt_data)
+ * SBUS_LISTEN_ASYNC(org_freedesktop_sssd, DomainEnabled,
+ * "/org/freedesktop/sssd/ad@pb",
+ * on_domain_enabled_send,
+ * on_domain_enabled_recv,
+ * pvt_data)
+ * );
+ */
+#define SBUS_LISTENERS(...) \
+ { \
+ __VA_ARGS__, \
+ SBUS_INTERFACE_SENTINEL \
+ }
+
+/**
+ * Create list of sbus nodes.
+ *
+ * @see SBUS_NODE_SYNC, SBUS_NODE_ASYNC
+ *
+ * @example Users node with a factory method that will list all the users.
+ *
+ * struct sbus_node nodes[] = SBUS_NODES(
+ * SBUS_NODE_SYNC("/org/freedesktop/sssd/Users",
+ * list_of_users, pvt_data)
+ * );
+ */
+#define SBUS_NODES(...) \
+ { \
+ __VA_ARGS__, \
+ SBUS_INTERFACE_SENTINEL \
+ }
+
+/**
+ * Indicate that the interface has no methods.
+ */
+#define SBUS_WITHOUT_METHODS \
+ SBUS_METHODS(SBUS_NO_METHODS)
+
+/**
+ * Indicate that the interface has no signals.
+ */
+#define SBUS_WITHOUT_SIGNALS \
+ SBUS_SIGNALS(SBUS_NO_SIGNALS)
+
+/**
+ * Indicate that the interface has no properties.
+ */
+#define SBUS_WITHOUT_PROPERTIES \
+ SBUS_PROPERTIES(SBUS_NO_PROPERTIES)
+
+/**
+ * Create and sbus interface.
+ *
+ * @param varname Name of the variable that will hold the interface
+ * description. It is created as:
+ * struct sbus_interface varname;
+ * You can refer to it later when creating 'sbus_path'
+ * structure as &varname.
+ * @param iface Name of the interface with dots replaced
+ * with underscore. (token, not a string)
+ * @param methods Methods on the interface.
+ * @param signals Signals on the interface.
+ * @param properties Properties on the interface.
+ *
+ * Please note that the following macro introduced to the scope these variables:
+ * - __varname_m
+ * - __varname_s
+ * - __varname_p
+ *
+ * These variables are intended for internal purpose only and should not be
+ * used outside this macro. They are allocated on stack and will be destroyed
+ * with it.
+ *
+ * Additionally, it creates 'struct sbus_interface varname'. This variable
+ * holds the information about the interfaces you created. The structure and
+ * all its data are allocated on stack and will be destroyed with it.
+ *
+ * The only intended usage of this variable is to assign it to an sbus path
+ * and then register this path inside the same function where the interface
+ * is defined. It should not be used in any other way.
+ *
+ * The following example demonstrates the intended usage of this macro.
+ * Do not use it in any other way.
+ *
+ * @example
+ * SBUS_INTERFACE(
+ * iface_bus,
+ * org_freedesktop_DBus,
+ * SBUS_METHODS(
+ * SBUS_SYNC(METHOD, org_freedesktop_DBus, Hello, sbus_server_bus_hello, server),
+ * SBUS_SYNC(METHOD, org_freedesktop_DBus, RequestName, sbus_server_bus_request_name, server),
+ * ),
+ * SBUS_SIGNALS(
+ * SBUS_EMITS(org_freedesktop_DBus, NameOwnerChanged),
+ * SBUS_EMITS(org_freedesktop_DBus, NameAcquired),
+ * SBUS_EMITS(org_freedesktop_DBus, NameLost)
+ * ),
+ * SBUS_WITHOUT_PROPERTIES
+ * );
+ *
+ * struct sbus_path paths[] = {
+ * {"/org/freedesktop/dbus", &iface_bus},
+ * {NULL, NULL}
+ * };
+ *
+ * ret = sbus_router_add_path_map(server->router, paths);
+ * if (ret != EOK) {
+ * DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add paths [%d]: %s\n",
+ * ret, sss_strerror(ret));
+ * return ret;
+ * }
+ *
+ * @see SBUS_METHODS, SBUS_SIGNALS, SBUS_PROPERTIES to create those arguments.
+ */
+#define SBUS_INTERFACE(varname, iface, methods, signals, properties) \
+ const struct sbus_method __ ## varname ## _m[] = methods; \
+ const struct sbus_signal __ ## varname ## _s[] = signals; \
+ const struct sbus_property __ ## varname ## _p[] = properties; \
+ struct sbus_interface varname = SBUS_IFACE_ ## iface( \
+ (__ ## varname ## _m), \
+ (__ ## varname ## _s), \
+ (__ ## varname ## _p) \
+ )
+
+/**
+ * Create a new sbus synchronous handler.
+ *
+ * @param type Handler type. One of:
+ * METHOD, GETTER, SETTER.
+ * @param iface Name of the interface with dots replaced
+ * with underscore. (token, not a string)
+ * @param name Name of the sbus method, property (token, not a string).
+ * @param handler Synchronous handler.
+ * @param data Private data that are passed to the handler.
+ *
+ * Synchronous handler type is:
+ * errno_t handler(TALLOC_CTX *mem_ctx,
+ * struct sbus_request *sbus_req,
+ * data_type private_data,
+ * input parameters,
+ * output parameters)
+ *
+ * @example
+ * SBUS_SYNC(SETTER, org_freedesktop_sssd, name, setter_name, pvt_data)
+ *
+ * @see SBUS_ASYNC
+ */
+#define SBUS_SYNC(type, iface, method, handler, data) \
+ SBUS_ ## type ## _SYNC_ ## iface ## _ ## method(handler, data)
+
+/**
+ * Create a new sbus asynchronous handler.
+ *
+ * @param type Handler type. One of:
+ * METHOD, GETTER, SETTER.
+ * @param iface Name of the interface with dots replaced
+ * with underscore. (token, not a string)
+ * @param name Name of the sbus method, property (token, not a string).
+ * @param handler_send Handler for _send tevent function.
+ * @param handler_recv Handler for _recv tevent function.
+ * @param data Private data that are passed to the handler.
+ *
+ * Asynchronous handler type is:
+ * struct tevent_req * _send(TALLOC_CTX *mem_ctx,
+ * struct tevent_context *ev,
+ * struct sbus_request *sbus_req,
+ * data_type private_data,
+ * input parameters)
+ *
+ * errno_t _recv(TALLOC_CTX *mem_ctx,
+ * struct tevent_req *req,
+ * output parameters)
+ *
+ * @example
+ * SBUS_ASYNC(SETTER, org_freedesktop_sssd, name,
+* setter_name_send,
+ * setter_name_recv,
+ * pvt_data)
+ *
+ * @see SBUS_SYNC
+ */
+#define SBUS_ASYNC(type, iface, property, handler_send, handler_recv, data) \
+ SBUS_ ## type ## _ASYNC_ ## iface ## _ ## property(handler_send, handler_recv, data)
+
+/**
+ * Create a new sbus listener with synchronous handler.
+ *
+ * @param iface Name of the interface with dots replaced
+ * with underscore. (token, not a string)
+ * @param name Name of the sbus signal (token, not a string).
+ * @param path Object path to listen at. May be NULL.
+ * @param handler Synchronous handler.
+ * @param data Private data that are passed to the handler.
+ *
+ * Synchronous handler type for signal listener is:
+ * errno_t handler(TALLOC_CTX *mem_ctx,
+ * struct sbus_request *sbus_req,
+ * data_type private_data,
+ * input parameters)
+ *
+ * @example
+ * SBUS_LISTEN_SYNC(org_freedesktop_sssd, PropertyChanged,
+ * "/org/freedesktop/sssd/User1",
+ * signal_handler, pvt_data)
+ *
+ * @see SBUS_LISTENERS, SBUS_LISTEN_ASYNC
+ */
+#define SBUS_LISTEN_SYNC(iface, signal, path, handler, data) \
+ SBUS_SIGNAL_SYNC_ ## iface ## _ ## signal(path, handler, data)
+
+/**
+ * Create a new sbus listener with asynchronous handler
+ *
+ * @param iface Name of the interface with dots replaced
+ * with underscore. (token, not a string)
+ * @param name Name of the sbus signal (token, not a string).
+ * @param path Object path to listen at. May be NULL.
+ * @param handler_send Handler for _send tevent function.
+ * @param handler_recv Handler for _recv tevent function.
+ * @param data Private data that are passed to the handler.
+ *
+ * Asynchronous handler type for signal listener is:
+ * struct tevent_req * _send(TALLOC_CTX *mem_ctx,
+ * struct tevent_context *ev,
+ * struct sbus_request *sbus_req,
+ * data_type private_data,
+ * input parameters)
+ *
+ * errno_t _recv(TALLOC_CTX *mem_ctx, struct tevent_req *req)
+ *
+ * @example
+ * SBUS_LISTEN_ASYNC(org_freedesktop_sssd, PropertyChanged,
+ * "/org/freedesktop/sssd/User1",
+ * on_property_changed_send,
+ * on_property_changed_recv,
+ * pvt_data)
+ *
+ * @see SBUS_LISTENERS, SBUS_LISTEN_SYNC
+ */
+#define SBUS_LISTEN_ASYNC(iface, property, path, handler_send, handler_recv, data) \
+ SBUS_SIGNAL_ASYNC_ ## iface ## _ ## property(path, handler_send, handler_recv, data)
+
+/**
+ * Add a signal that can be emitted into the sbus interface.
+ *
+ * @param iface Name of the interface with dots replaced
+ * with underscore. (token, not a string)
+ * @param signal Signal name (token, not a string).
+ *
+ * @example
+ * SBUS_SIGNAL(org_freedesktop_sssd, PropertyChanged)
+ *
+ * @see SBUS_SIGNALS, SBUS_NO_SIGNALS
+ */
+#define SBUS_EMITS(iface, signal) \
+ SBUS_SIGNAL_EMITS_ ## iface ## _ ## signal()
+
+/**
+ * Create a new sbus node with a synchronous node factory.
+ *
+ * @param path Node's object path.
+ * @param factory Synchronous factory function.
+ * @param data Private data that are passed to the factory function.
+ *
+ * Synchronous handler type for node factory is:
+ * errno_t factory(TALLOC_CTX *mem_ctx,
+ * const char *object_path,
+ * data_type pvt_data,
+ * const char ***_nodes)
+ *
+ * @example
+ * SBUS_NODE_SYNC(/org/freedesktop/sssd/Users",
+ * list_of_users, pvt_data)
+ *
+ * @see SBUS_NODES, SBUS_NODE_ASYNC
+ */
+#define SBUS_NODE_SYNC(path, factory, data) \
+ _SBUS_NODE_SYNC(path, factory, data)
+
+/**
+ * Create a new sbus node with an asynchronous node factory.
+ *
+ * @param path Node's object path.
+ * @param factory_send Factory function for _send tevent function.
+ * @param factory_recv Factory function for _recv tevent function.
+ * @param data Private data that are passed to the factory function.
+ *
+ * Asynchronous handler type for signal listener is:
+ * struct tevent_req *send(TALLOC_CTX *mem_ctx,
+ * struct tevent_context *ev,
+ * const char *object_path,
+ * data_type pvt_data)
+ *
+ * errno_t recv(TALLOC_CTX *mem_ctx,
+ * struct tevent_req *req,
+ * const char ***_nodes)
+ *
+ * @example
+ * SBUS_NODE_ASYNC("/org/freedesktop/sssd/Users",
+ * list_users_send,
+ * list_users_recv,
+ * pvt_data)
+ *
+ * @see SBUS_NODES, SBUS_NODE_SYNC
+ */
+#define SBUS_NODE_ASYNC(path, factory_send, factory_recv, data) \
+ _SBUS_NODE_ASYNC(path, handler_send, factory_recv, data)
+
+#endif /* _SBUS_INTERFACE_H_ */
diff --git a/src/sbus/sbus_interface_declarations.h b/src/sbus/sbus_interface_declarations.h
new file mode 100644
index 0000000..fe62fc2
--- /dev/null
+++ b/src/sbus/sbus_interface_declarations.h
@@ -0,0 +1,497 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_INTERFACE_DECLARATIONS_H_
+#define _SBUS_INTERFACE_DECLARATIONS_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_typeof.h"
+#include "sbus/sbus_request.h"
+
+/*****************************************************************************
+ *
+ * This file contains declarations of symbols that must be generally available
+ * to the user but that must not be used on their own so they should not be
+ * present in sbus.h or other header files.
+ *
+ * Do not include this file directly.
+ *
+ *****************************************************************************/
+
+/**
+ * Terminator for the arrays in struct sbus_interface.
+ */
+#define SBUS_INTERFACE_SENTINEL {0}
+
+enum sbus_handler_type {
+ /**
+ * Synchronous handler.
+ *
+ * Control is immediately returned from this type of handler.
+ */
+ SBUS_HANDLER_SYNC,
+
+ /**
+ * Asynchronous handler.
+ *
+ * Control is given to tevent.
+ */
+ SBUS_HANDLER_ASYNC
+};
+
+enum sbus_property_access {
+ /**
+ * The property is readable.
+ */
+ SBUS_PROPERTY_READABLE = 1,
+
+ /**
+ * The property is writable.
+ */
+ SBUS_PROPERTY_WRITABLE = 2,
+};
+
+struct sbus_handler;
+struct sbus_invoker;
+
+typedef void * sbus_handler_send_fn;
+typedef void * sbus_handler_recv_fn;
+typedef void * sbus_handler_sync_fn;
+typedef void * sbus_handler_data;
+typedef void * sbus_invoker_keygen;
+
+typedef struct tevent_req *
+(*sbus_invoker_issue)(TALLOC_CTX *,
+ struct tevent_context *,
+ struct sbus_request *,
+ sbus_invoker_keygen,
+ const struct sbus_handler *,
+ DBusMessageIter *read_iterator,
+ DBusMessageIter *write_iterator,
+ const char **_key);
+
+struct sbus_invoker {
+ sbus_invoker_issue issue;
+ sbus_invoker_keygen keygen;
+};
+
+struct sbus_annotation {
+ const char *name;
+ const char *value;
+};
+
+struct sbus_handler {
+ enum sbus_handler_type type;
+
+ /* Asynchronous handler functions. */
+ sbus_handler_send_fn async_send;
+ sbus_handler_recv_fn async_recv;
+
+ /* Synchronous handler functions. */
+ sbus_handler_sync_fn sync;
+
+ /* Handler private data that are passed to it. */
+ sbus_handler_data data;
+};
+
+/**
+ * An sbus argument of a method or signal.
+ */
+struct sbus_argument {
+ /**
+ * D-Bus type of the argument.
+ */
+ const char *type;
+
+ /**
+ * Argument name.
+ */
+ const char *name;
+};
+
+struct sbus_method_arguments {
+ const struct sbus_argument *input;
+ const struct sbus_argument *output;
+};
+
+/**
+ * An sbus method.
+ */
+struct sbus_method {
+ /**
+ * Method name.
+ */
+ const char *name;
+
+ /**
+ * Method handler.
+ */
+ const struct sbus_handler handler;
+
+ /**
+ * Internal function that will issue the handler.
+ */
+ const struct sbus_invoker invoker;
+
+ /**
+ * Input and output arguments, used to generate introspection.
+ */
+ const struct sbus_method_arguments *arguments;
+
+ /**
+ * Method annotations.
+ */
+ const struct sbus_annotation *annotations;
+};
+
+/**
+ * An sbus signal.
+ */
+struct sbus_signal {
+ /**
+ * Signal name (without the interface part).
+ */
+ const char *name;
+
+ /**
+ * Signal annotations.
+ */
+ const struct sbus_annotation *annotations;
+
+ /**
+ * Input arguments, used to generate introspection.
+ */
+ const struct sbus_argument *arguments;
+};
+
+/**
+ * An sbus property.
+ */
+struct sbus_property {
+ /**
+ * Property name.
+ */
+ const char *name;
+
+ /**
+ * D-Bus type of the property, used in introspection.
+ */
+ const char *type;
+
+ /**
+ * Property access type.
+ */
+ const enum sbus_property_access access;
+
+ /**
+ * Property annotations.
+ */
+ const struct sbus_annotation *annotations;
+
+ /**
+ * Property handler. If the property is readable, a setter. If it is
+ * writable, a getter.
+ */
+ const struct sbus_handler handler;
+
+ /**
+ * Internal function that will issue the handler.
+ */
+ const struct sbus_invoker invoker;
+};
+
+/**
+ * Object describing D-Bus interface for sbus implementation.
+ */
+struct sbus_interface {
+ /**
+ * Interface name.
+ */
+ const char *name;
+
+ /**
+ * Interface annotations.
+ */
+ const struct sbus_annotation *annotations;
+
+ /**
+ * Methods implemented on this interface.
+ */
+ const struct sbus_method *methods;
+
+ /**
+ * Signals that can be emitted on this interface.
+ */
+ const struct sbus_signal *signals;
+
+ /**
+ * Properties implemented on this interface.
+ */
+ const struct sbus_property *properties;
+};
+
+/**
+ * Object describing D-Bus signal listener.
+ */
+struct sbus_listener {
+ /**
+ * Interface name.
+ */
+ const char *interface;
+
+ /**
+ * Signal name on the interface to listen for.
+ */
+ const char *signal_name;
+
+ /**
+ * Object path to listen at, may be NULL to listen on all paths.
+ */
+ const char *object_path;
+
+ /**
+ * Signal handler.
+ */
+ const struct sbus_handler handler;
+
+ /**
+ * Internal function that will issue the handler.
+ */
+ const struct sbus_invoker invoker;
+};
+
+/**
+ * Provide a D-Bus node factory for given object path.
+ */
+struct sbus_node {
+ /**
+ * D-Bus object path.
+ */
+ const char *path;
+
+ /**
+ * Node factory is an sbus handler of following type:
+ */
+ struct sbus_handler factory;
+};
+
+/**
+ * Return an sbus interface object.
+ */
+struct sbus_interface
+sbus_interface(const char *name,
+ const struct sbus_annotation *annotations,
+ const struct sbus_method *methods,
+ const struct sbus_signal *signals,
+ const struct sbus_property *properties);
+
+
+/**
+ * Return an sbus interface method object with a synchronous handler.
+ */
+struct sbus_method
+sbus_method_sync(const char *name,
+ const struct sbus_method_arguments *arguments,
+ const struct sbus_annotation *annotations,
+ sbus_invoker_issue invoker_issue,
+ sbus_invoker_keygen invoker_keygen,
+ sbus_handler_sync_fn handler,
+ sbus_handler_data data);
+
+/**
+ * Return an sbus interface method object with an asynchronous handler.
+ */
+struct sbus_method
+sbus_method_async(const char *name,
+ const struct sbus_method_arguments *arguments,
+ const struct sbus_annotation *annotations,
+ sbus_invoker_issue invoker_issue,
+ sbus_invoker_keygen invoker_keygen,
+ sbus_handler_send_fn handler_send,
+ sbus_handler_recv_fn handler_recv,
+ sbus_handler_data data);
+
+/**
+ * Return an sbus interface signal object.
+ */
+struct sbus_signal
+sbus_signal(const char *name,
+ const struct sbus_argument *arguments,
+ const struct sbus_annotation *annotations);
+
+/**
+ * Return an sbus interface property object with a synchronous handler.
+ */
+struct sbus_property
+sbus_property_sync(const char *name,
+ const char *type,
+ enum sbus_property_access access,
+ const struct sbus_annotation *annotations,
+ sbus_invoker_issue invoker_issue,
+ sbus_handler_sync_fn handler,
+ sbus_handler_data data);
+
+/**
+ * Return an sbus interface property object with an asynchronous handler.
+ */
+struct sbus_property
+sbus_property_async(const char *name,
+ const char *type,
+ enum sbus_property_access access,
+ const struct sbus_annotation *annotations,
+ sbus_invoker_issue invoker_issue,
+ sbus_handler_send_fn handler_send,
+ sbus_handler_recv_fn handler_recv,
+ sbus_handler_data data);
+
+/**
+ * Return an sbus signal listener object with n synchronous handler.
+ */
+struct sbus_listener
+sbus_listener_sync(const char *interface,
+ const char *signal_name,
+ const char *object_path,
+ sbus_invoker_issue invoker_issue,
+ sbus_invoker_keygen invoker_keygen,
+ sbus_handler_sync_fn handler,
+ sbus_handler_data data);
+
+/**
+ * Return an sbus signal listener object with an asynchronous handler.
+ */
+struct sbus_listener
+sbus_listener_async(const char *interface,
+ const char *signal_name,
+ const char *object_path,
+ sbus_invoker_issue invoker_issue,
+ sbus_invoker_keygen invoker_keygen,
+ sbus_handler_send_fn handler_send,
+ sbus_handler_recv_fn handler_recv,
+ sbus_handler_data data);
+
+/**
+ * Associate an object path with a synchronous node factory function.
+ */
+struct sbus_node
+sbus_node_sync(const char *path,
+ sbus_handler_sync_fn factory,
+ sbus_handler_data data);
+
+/**
+ * Associate an object path with an asynchronous node factory function.
+ */
+struct sbus_node
+sbus_node_async(const char *path,
+ sbus_handler_send_fn factory_send,
+ sbus_handler_recv_fn factory_recv,
+ sbus_handler_data data);
+
+/**
+ * Check type of request synchronous handler. The expected type is:
+ * errno_t handler(struct sbus_request *request_data, data_type data
+ * [, method input arg, ...] [, method output arg, ...])
+ *
+ * For example:
+ * errno_t handler(struct sbus_request *, struct ctx * data,
+ * const char *input, int *_output)
+ */
+#define SBUS_CHECK_SYNC(handler, data, ...) \
+ SBUS_CHECK_FUNCTION((handler), \
+ /* return type */ \
+ errno_t, \
+ /* input parameters types */ \
+ TALLOC_CTX *, \
+ struct sbus_request *, \
+ SBUS_TYPEOF(data), \
+ /* method specific parameters types */ \
+ ## __VA_ARGS__)
+
+/**
+ * Check type of request asynchronous send handler. The expected type is:
+ * struct tevent_req * handler(TALLOC_CTX *mem_ctx,
+ * struct tevent_context *ev,
+ * struct sbus_request *request_data,
+ * data_type data
+ * [, method input arg, ...])
+ *
+ * For example:
+ * struct tevent_req * handler_send(TALLOC_CTX *mem_ctx,
+ * struct tevent_context *ev,
+ * struct sbus_request *sbus_req,
+ * struct ctx *data,
+ * const char *input)
+ */
+#define SBUS_CHECK_SEND(handler, data, ...) \
+ SBUS_CHECK_FUNCTION((handler), \
+ /* return type */ \
+ struct tevent_req *, \
+ /* input parameters types */ \
+ TALLOC_CTX *, \
+ struct tevent_context *, \
+ struct sbus_request *, \
+ SBUS_TYPEOF(data), \
+ /* method specific input parameters types */ \
+ ## __VA_ARGS__) \
+
+/**
+ * Check type of asynchronous recv handler. The expected type is:
+ * errno_t handler(TALLOC_CTX *mem_ctx, struct tevent_req *req
+ * [, method output arg, ...])
+ *
+ * For example:
+ * errno_t handler_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req,
+ * int *_output)
+ */
+#define SBUS_CHECK_RECV(handler, ...) \
+ SBUS_CHECK_FUNCTION((handler), \
+ /* return type */ \
+ errno_t, \
+ /* input parameters types */ \
+ TALLOC_CTX *, \
+ struct tevent_req *, \
+ /* method specific output parameters types */ \
+ ## __VA_ARGS__)
+
+
+#define _SBUS_NODE_SYNC(path, factory, data) ({ \
+ SBUS_CHECK_FUNCTION((factory), \
+ /* return type */ errno_t, \
+ TALLOC_CTX *, const char *, SBUS_TYPEOF(data), \
+ const char ***); \
+ sbus_node_sync((path), (factory), (data)); \
+})
+
+#define _SBUS_NODE_ASYNC(path, factory_send, factory_recv, data) ({ \
+ SBUS_CHECK_FUNCTION((factory_send), \
+ /* return type */ struct tevent_req *, \
+ TALLOC_CTX *, struct tevent_context *ev, \
+ const char *, SBUS_TYPEOF(data)); \
+ SBUS_CHECK_FUNCTION((factory_recv), \
+ /* return type */ errno_t, \
+ TALLOC_CTX *, struct tevent_req *, \
+ const char ***); \
+ sbus_node_async((path), (factory_send), (factory_recv), (data)); \
+})
+
+#endif /* _SBUS_INTERFACE_DECLARATIONS_H_ */
diff --git a/src/sbus/sbus_message.h b/src/sbus/sbus_message.h
new file mode 100644
index 0000000..7ae634e
--- /dev/null
+++ b/src/sbus/sbus_message.h
@@ -0,0 +1,175 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_MESSAGE_H_
+#define _SBUS_MESSAGE_H_
+
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_errors.h"
+
+/* Use longer default timeout than libdbus default due to expensive
+ * selinux operation: see https://bugzilla.redhat.com/show_bug.cgi?id=1654537
+ */
+#define SBUS_MESSAGE_TIMEOUT 120000
+
+/**
+ * Bound message with a talloc context.
+ *
+ * The message is unreferenced when the talloc context is freed or when
+ * the message is unreferenced with dbus_message_unref.
+ *
+ * @param mem_ctx Memory context to bound the message with. It can not be NULL.
+ * @param msg Message to be bound with memory context.
+ *
+ * @return EOK on success, other errno code on error.
+ */
+errno_t
+sbus_message_bound(TALLOC_CTX *mem_ctx, DBusMessage *msg);
+
+/**
+ * Steal previously bound D-Bus message to a new talloc parent.
+ *
+ * @param mem_ctx Memory context to bound the message with. It can not be NULL.
+ * @param msg Message to be bound with memory context.
+ *
+ * @return EOK on success, other errno code on error.
+ */
+errno_t
+sbus_message_bound_steal(TALLOC_CTX *mem_ctx, DBusMessage *msg);
+
+/**
+ * Create an empty D-Bus method call.
+ *
+ * @param bus Destination bus name.
+ * @param path Object path on which the method should be executed.
+ * @param iface Interface name.
+ * @param method Method name.
+ *
+ * @return D-Bus message.
+ */
+DBusMessage *
+sbus_method_create_empty(TALLOC_CTX *mem_ctx,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method);
+
+/* @see sbus_method_create */
+DBusMessage *
+_sbus_method_create(TALLOC_CTX *mem_ctx,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ int first_arg_type,
+ ...);
+
+/**
+ * Create a new D-Bus method call and append some arguments to it.
+ *
+ * See dbus_message_append_args to see the argument format.
+ *
+ * @param bus Destination bus name.
+ * @param path Object path on which the method should be executed.
+ * @param iface Interface name.
+ * @param method Method name.
+ * @param ... Argument tuples.
+ *
+ * @return D-Bus message.
+ */
+#define sbus_method_create(mem_ctx, bus, path, iface, method, ...) \
+ _sbus_method_create(mem_ctx, bus, path, iface, method, \
+ ##__VA_ARGS__, DBUS_TYPE_INVALID)
+
+/**
+ * Create an empty D-Bus signal call.
+ *
+ * @param path Object path on which the method should be executed.
+ * @param iface Interface name.
+ * @param signame Signal name.
+ *
+ * @return D-Bus message.
+ */
+DBusMessage *
+sbus_signal_create_empty(TALLOC_CTX *mem_ctx,
+ const char *path,
+ const char *iface,
+ const char *signame);
+
+/* @see sbus_signal_create */
+DBusMessage *
+_sbus_signal_create(TALLOC_CTX *mem_ctx,
+ const char *path,
+ const char *iface,
+ const char *signame,
+ int first_arg_type,
+ ...);
+
+/**
+ * Create a new D-Bus signal call and append some arguments to it.
+ *
+ * See dbus_message_append_args to see the argument format.
+ *
+ * @param path Object path on which the method should be executed.
+ * @param iface Interface name.
+ * @param signame Signal name.
+ * @param ... Argument tuples.
+ *
+ * @return D-Bus message.
+ */
+#define sbus_signal_create(mem_ctx, path, iface, signame, ...) \
+ _sbus_signal_create(mem_ctx, path, iface, signame, \
+ ##__VA_ARGS__, DBUS_TYPE_INVALID)
+
+/* @see sbus_reply_parse */
+errno_t
+_sbus_reply_parse(DBusMessage *msg,
+ int first_arg_type,
+ ...);
+
+/**
+ * Check a method call reply and parse it into output arguments if successful.
+ *
+ * See dbus_message_get_args to see the argument format.
+ *
+ * @param msg Method call reply.
+ * @param ... Argument tuples.
+ *
+ * @return EOK on success, other errno code on failure.
+ */
+#define sbus_reply_parse(msg, ...) \
+ _sbus_reply_parse(msg, ##__VA_ARGS__, DBUS_TYPE_INVALID)
+
+/**
+ * Check if the method call was successful or if it replied with an error
+ * message. If it is an error message, the return value will be errno code
+ * equivalent to the error.
+ *
+ * @param reply Method call reply.
+ *
+ * @return EOK on success, other errno code on failure.
+ */
+errno_t
+sbus_reply_check(DBusMessage *reply);
+
+#endif /* _SBUS_MESSAGE_H_ */
diff --git a/src/sbus/sbus_opath.c b/src/sbus/sbus_opath.c
new file mode 100644
index 0000000..ce621d3
--- /dev/null
+++ b/src/sbus/sbus_opath.c
@@ -0,0 +1,417 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <talloc.h>
+#include <string.h>
+
+#include "util/util.h"
+#include "sbus/sbus_opath.h"
+
+char *
+_sbus_opath_compose(TALLOC_CTX *mem_ctx,
+ const char *base,
+ const char *part, ...)
+{
+ char *safe_part;
+ char *path = NULL;
+ va_list va;
+
+ if (base == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Wrong object path base!\n");
+ return NULL;
+ }
+
+ path = talloc_strdup(mem_ctx, base);
+ if (path == NULL) {
+ return NULL;
+ }
+
+ va_start(va, part);
+ while (part != NULL) {
+ safe_part = sbus_opath_escape(mem_ctx, part);
+ if (safe_part == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not add [%s] to objpath\n", part);
+ goto fail;
+ }
+
+ path = talloc_asprintf_append(path, "/%s", safe_part);
+ talloc_free(safe_part);
+ if (path == NULL) {
+ goto fail;
+ }
+
+ part = va_arg(va, const char *);
+ }
+ va_end(va);
+
+ return path;
+
+fail:
+ va_end(va);
+ talloc_free(path);
+ return NULL;
+}
+
+errno_t
+sbus_opath_decompose(TALLOC_CTX *mem_ctx,
+ const char *object_path,
+ const char *prefix,
+ char ***_components,
+ size_t *_num_components)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *path;
+ char **decomposed;
+ char **unescaped;
+ errno_t ret;
+ int len;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ /* Strip prefix from the path. */
+ if (prefix != NULL) {
+ path = sbus_opath_strip_prefix(object_path, prefix);
+ if (path == NULL) {
+ ret = ERR_SBUS_INVALID_PATH;
+ goto done;
+ }
+ } else {
+ path = object_path;
+ }
+
+ if (path[0] == '\0') {
+ *_components = NULL;
+ *_num_components = 0;
+ ret = EOK;
+ goto done;
+ }
+
+ /* Split the string using / as delimiter. */
+ ret = split_on_separator(tmp_ctx, path, '/', true, true, &decomposed, &len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Unescape parts. */
+ unescaped = talloc_zero_array(tmp_ctx, char *, len + 1);
+ if (unescaped == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < len; i++) {
+ unescaped[i] = sbus_opath_unescape(unescaped, decomposed[i]);
+ if (unescaped[i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (_components != NULL) {
+ *_components = talloc_steal(mem_ctx, unescaped);
+ }
+
+ if (_num_components != NULL) {
+ *_num_components = len;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sbus_opath_decompose_expected(TALLOC_CTX *mem_ctx,
+ const char *object_path,
+ const char *prefix,
+ size_t expected,
+ char ***_components)
+{
+ char **components;
+ size_t len;
+ errno_t ret;
+
+ ret = sbus_opath_decompose(mem_ctx, object_path, prefix,
+ &components, &len);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (len != expected) {
+ talloc_free(components);
+ return ERR_SBUS_INVALID_PATH;
+ }
+
+ if (_components != NULL) {
+ *_components = components;
+ }
+
+ return EOK;
+}
+
+char *
+sbus_opath_object_name(TALLOC_CTX *mem_ctx,
+ const char *object_path,
+ const char *prefix)
+{
+ char **components;
+ char *name;
+ errno_t ret;
+
+ ret = sbus_opath_decompose_expected(mem_ctx, object_path, prefix,
+ 1, &components);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ name = talloc_steal(mem_ctx, components[0]);
+ talloc_free(components);
+ return name;
+}
+
+char *
+sbus_opath_escape(TALLOC_CTX *mem_ctx,
+ const char *component)
+{
+ size_t n;
+ char *safe_path = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ /* The path must be valid */
+ if (component == NULL) {
+ return NULL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ safe_path = talloc_strdup(tmp_ctx, "");
+ if (safe_path == NULL) {
+ goto done;
+ }
+
+ /* Special case for an empty string */
+ if (strcmp(component, "") == 0) {
+ /* the for loop would just fall through */
+ safe_path = talloc_asprintf_append_buffer(safe_path, "_");
+ if (safe_path == NULL) {
+ goto done;
+ }
+ }
+
+ for (n = 0; component[n]; n++) {
+ int c = component[n];
+ /* D-Bus spec says:
+ * *
+ * * Each element must only contain the ASCII characters
+ * "[A-Z][a-z][0-9]_"
+ * */
+ if ((c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9')) {
+ safe_path = talloc_asprintf_append_buffer(safe_path, "%c", c);
+ if (safe_path == NULL) {
+ goto done;
+ }
+ } else {
+ safe_path = talloc_asprintf_append_buffer(safe_path, "_%02x", c);
+ if (safe_path == NULL) {
+ goto done;
+ }
+ }
+ }
+
+ safe_path = talloc_steal(mem_ctx, safe_path);
+
+done:
+ talloc_free(tmp_ctx);
+ return safe_path;
+}
+
+static inline int unhexchar(char c)
+{
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ }
+
+ if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ }
+
+ if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ }
+
+ return -1;
+}
+
+char *
+sbus_opath_unescape(TALLOC_CTX *mem_ctx,
+ const char *component)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *safe_path;
+ const char *p;
+ int a, b, c;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ safe_path = talloc_strdup(tmp_ctx, "");
+ if (safe_path == NULL) {
+ goto done;
+ }
+
+ /* Special case for the empty string */
+ if (strcmp(component, "_") == 0) {
+ safe_path = talloc_steal(mem_ctx, safe_path);
+ goto done;
+ }
+
+ for (p = component; *p; p++) {
+ if (*p == '_') {
+ /* There must be at least two more chars after underscore */
+ if (p[1] == '\0' || p[2] == '\0') {
+ safe_path = NULL;
+ goto done;
+ }
+
+ if ((a = unhexchar(p[1])) < 0
+ || (b = unhexchar(p[2])) < 0) {
+ /* Invalid escape code, let's take it literal then */
+ c = '_';
+ } else {
+ c = ((a << 4) | b);
+ p += 2;
+ }
+ } else {
+ c = *p;
+ }
+
+ safe_path = talloc_asprintf_append_buffer(safe_path, "%c", c);
+ if (safe_path == NULL) {
+ goto done;
+ }
+ }
+
+ safe_path = talloc_steal(mem_ctx, safe_path);
+
+ done:
+ talloc_free(tmp_ctx);
+ return safe_path;
+}
+
+const char *
+sbus_opath_strip_prefix(const char *object_path,
+ const char *prefix)
+{
+ size_t len = strlen(prefix);
+ if (strncmp(object_path, prefix, len) == 0) {
+ return object_path + len;
+ }
+
+ return NULL;
+}
+
+bool
+sbus_opath_is_subtree(const char *object_path)
+{
+ size_t len;
+
+ len = strlen(object_path);
+
+ /* At least slash and asterisk. */
+ if (len < 2) {
+ return false;
+ }
+
+ return object_path[len - 2] == '/' && object_path[len - 1] == '*';
+}
+
+char *
+sbus_opath_subtree_base(TALLOC_CTX *mem_ctx,
+ const char *object_path)
+{
+ char *tree_path;
+ size_t len;
+
+ tree_path = talloc_strdup(mem_ctx, object_path);
+ if (tree_path == NULL) {
+ return NULL;
+ }
+
+ if (!sbus_opath_is_subtree(tree_path)) {
+ return tree_path;
+ }
+
+ /* replace / only if it is not a root path (only slash) */
+ len = strlen(tree_path);
+ tree_path[len - 1] = '\0';
+ tree_path[len - 2] = (len - 2 != 0) ? '\0' : '/';
+
+ return tree_path;
+}
+
+char *
+sbus_opath_subtree_parent(TALLOC_CTX *mem_ctx,
+ const char *object_path)
+{
+ char *subtree;
+ char *slash;
+
+ /* First remove /~* from the end, stop when we have reached the root i.e.
+ * subtree == "/" */
+ subtree = sbus_opath_subtree_base(mem_ctx, object_path);
+ if (subtree == NULL || subtree[1] == '\0') {
+ return NULL;
+ }
+
+ /* Find the first separator and replace the part with asterisk. */
+ slash = strrchr(subtree, '/');
+ if (slash == NULL) {
+ /* We cannot continue up. */
+ talloc_free(subtree);
+ return NULL;
+ }
+
+ if (*(slash + 1) == '\0') {
+ /* This object path is invalid since it cannot end with slash. */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid object path '%s'?\n", object_path);
+ talloc_free(subtree);
+ return NULL;
+ }
+
+ /* Because object path cannot end with / there is enough space for
+ * asterisk and terminating zero. */
+ *(slash + 1) = '*';
+ *(slash + 2) = '\0';
+
+ return subtree;
+}
diff --git a/src/sbus/sbus_opath.h b/src/sbus/sbus_opath.h
new file mode 100644
index 0000000..f5ff15b
--- /dev/null
+++ b/src/sbus/sbus_opath.h
@@ -0,0 +1,181 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_OPATH_H_
+#define _SBUS_OPATH_H_
+
+#include <talloc.h>
+
+#include "util/util.h"
+
+/* @see sbus_opath_compose */
+char *
+_sbus_opath_compose(TALLOC_CTX *mem_ctx,
+ const char *base,
+ const char *part, ...);
+
+/**
+ * Compose an object path from given components. Each component is properly
+ * escaped so it does not contain any invalid character and a valid object
+ * path is returned.
+ *
+ * @param mem_ctx Memory context.
+ * @param base Base object path to begin with.
+ * @param ... Following components as string.
+ *
+ * @return Constructed object path.
+ *
+ * @example
+ * sbus_opath_compose(mem_ctx, "/org", "freedesktop", "sssd")
+ * -> "/org/freedesktop/sssd"
+ */
+#define sbus_opath_compose(mem_ctx, base, ...) \
+ _sbus_opath_compose(mem_ctx, base, ##__VA_ARGS__, NULL)
+
+
+/**
+ * Decompose an object path, unescaping its values if needed. Components
+ * that follow @prefix are returned.
+ *
+ * @param mem_ctx Memory context.
+ * @param object_path Input object path.
+ * @param prefix Beginning of object path that is not returned.
+ * @param _components Output components.
+ * @param _num_components Output number of returned components.
+ *
+ * @return EOK on success, other error code on failure.
+ */
+errno_t
+sbus_opath_decompose(TALLOC_CTX *mem_ctx,
+ const char *object_path,
+ const char *prefix,
+ char ***_components,
+ size_t *_num_components);
+
+/**
+ * Decompose an object path, unescaping its values if needed. This function
+ * returns an error if the object path after @prefix does not contain exactly
+ * @expected number of component.
+ *
+ * @param mem_ctx Memory context.
+ * @param object_path Input object path.
+ * @param prefix Beginning of object path that is not returned.
+ * @param expected Expected number of components.
+ * @param _components Output components.
+ *
+ * @return EOK on success, other error code on failure.
+ */
+errno_t
+sbus_opath_decompose_expected(TALLOC_CTX *mem_ctx,
+ const char *object_path,
+ const char *prefix,
+ size_t expected,
+ char ***_components);
+
+/**
+ * Decompose the object path expecting only a single component after the
+ * @prefix path and return this component.
+ *
+ * @param mem_ctx Memory context.
+ * @param object_path Input object path.
+ * @param prefix Beginning of object path that is not returned.
+ *
+ * @return Unescaped component or NULL on error.
+ */
+char *
+sbus_opath_object_name(TALLOC_CTX *mem_ctx,
+ const char *object_path,
+ const char *prefix);
+
+/**
+ * Escape a single object path component. Use @sbus_opath_compose
+ * if you want to create the whole object path.
+ *
+ * @param mem_ctx Memory context.
+ * @param component Component to escape.
+ *
+ * @return Escaped component or NULL on failure.
+ */
+char *
+sbus_opath_escape(TALLOC_CTX *mem_ctx,
+ const char *component);
+
+/**
+ * Unescape a single object path component. Use @sbus_opath_decompose
+ * if you want to parse the whole object path.
+ *
+ * @param mem_ctx Memory context.
+ * @param component Component to escape.
+ *
+ * @return Escaped component or NULL on failure.
+ */
+char *
+sbus_opath_unescape(TALLOC_CTX *mem_ctx,
+ const char *component);
+
+/**
+ * Remove @prefix from the beginning of @object_path and return the remaining
+ * string. The returned pointer points to the original @object_path.
+ *
+ * @param object_path Object path.
+ * @param prefix Prefix to strip.
+ *
+ * @return Remaining object path or NULL if the prefix is not present.
+ */
+const char *
+sbus_opath_strip_prefix(const char *object_path,
+ const char *prefix);
+
+/**
+ * Return true if given object path represents a subtree path (ending with
+ * asterisk). That is an object path representing all object under this path.
+ *
+ * @param object_path Object path.
+ *
+ * @return True if it is a subtree path, false otherwise.
+ */
+bool
+sbus_opath_is_subtree(const char *object_path);
+
+/**
+ * Return subtree base path (removing the ending slash and asterisk).
+ *
+ * @param mem_ctx Memory context.
+ * @param object_path Subtree object path.
+ *
+ * @return Base path or NULL on error.
+ */
+char *
+sbus_opath_subtree_base(TALLOC_CTX *mem_ctx,
+ const char *object_path);
+
+/**
+ * Travers up on the subtree object path, returning the parent subtree.
+ *
+ * @param mem_ctx Memory context.
+ * @param object_path Subtree object path.
+ *
+ * @return Parent subtree path or NULL on error.
+ */
+char *
+sbus_opath_subtree_parent(TALLOC_CTX *mem_ctx,
+ const char *object_path);
+
+#endif /* _SBUS_OPATH_H_ */
diff --git a/src/sbus/sbus_private.h b/src/sbus/sbus_private.h
new file mode 100644
index 0000000..eef397b
--- /dev/null
+++ b/src/sbus/sbus_private.h
@@ -0,0 +1,671 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_PRIVATE_H_
+#define _SBUS_PRIVATE_H_
+
+#include <time.h>
+#include <dhash.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus.h"
+#include "sbus/sbus_message.h"
+#include "sbus/sbus_sync_private.h"
+#include "sbus/sbus_interface_declarations.h"
+
+/**
+ * First, declare all structures, since they are often cross referenced.
+ */
+
+struct sbus_connection_destructor;
+struct sbus_connection_access;
+struct sbus_connection;
+struct sbus_reconnect;
+struct sbus_server;
+struct sbus_router;
+struct sbus_watch;
+struct sbus_sender;
+struct sbus_listener;
+struct sbus_interface;
+struct sbus_active_requests;
+struct sbus_server_on_connection;
+
+enum sbus_connection_type {
+ /**
+ * Client connection is owned by sbus and not by D-Bus, therefore it
+ * needs to be explicitly closed before removing the last reference.
+ *
+ * This is used for connections created by sbus server.
+ */
+ SBUS_CONNECTION_CLIENT,
+
+ /**
+ * Address and system bus connections are owned by libdbus thus we can not
+ * close these connections explicitly. These connections may be recycled
+ * when we connect to the same address more than once.
+ */
+ SBUS_CONNECTION_ADDRESS,
+ SBUS_CONNECTION_SYSBUS
+};
+
+struct sbus_connection {
+ struct tevent_context *ev;
+ DBusConnection *connection;
+ enum sbus_connection_type type;
+ const char *address;
+
+ /* D-Bus connection well-known name as request by the application. */
+ const char *wellknown_name;
+
+ /* D-Bus connection unique name as assigned by bus. */
+ const char *unique_name;
+
+ /**
+ * True if the connection is being disconnected or freed. No further
+ * manipulation with the connection is allowed.
+ */
+ bool disconnecting;
+
+ /* Make all the structures pointers so we can have them encapsulated. */
+ struct sbus_connection_access *access;
+ struct sbus_connection_destructor *destructor;
+ struct sbus_active_requests *requests;
+ struct sbus_reconnect *reconnect;
+ struct sbus_router *router;
+ struct sbus_watch *watch;
+
+ /**
+ * Connection private data.
+ */
+ void *data;
+
+ /**
+ * Table of <dbus-sender, sbus-sender> pair. Contains resolved uids
+ * of remote D-Bus clients.
+ */
+ hash_table_t *senders;
+
+ /* Pointer to a caller's last activity variable. The time is updated
+ * each time the bus is active (when a method arrives). */
+ time_t *last_activity;
+};
+
+struct sbus_server {
+ struct tevent_context *ev;
+ DBusServer *server;
+ const char *symlink;
+ struct sbus_watch *watch_ctx;
+ struct sbus_router *router;
+ dbus_int32_t data_slot;
+ time_t *last_activity;
+ hash_table_t *names;
+ hash_table_t *match_rules;
+ uint32_t max_connections;
+ uid_t uid;
+ gid_t gid;
+
+ struct sbus_server_on_connection *on_connection;
+ bool disconnecting;
+
+ /* Last generated unique name information. */
+ struct {
+ uint32_t major;
+ uint32_t minor;
+ } name;
+};
+
+/* Setup server interface implementing org.freedesktop.DBus. */
+errno_t
+sbus_server_setup_interface(struct sbus_server *server);
+
+/* Add signal match. */
+errno_t
+sbus_server_add_match(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *rule);
+
+/* Remove signal match. */
+errno_t
+sbus_server_remove_match(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *rule);
+
+/* Send message to all connections whose rule matches the message. */
+errno_t
+sbus_server_matchmaker(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *avoid_name,
+ DBusMessage *message);
+
+/* Send NameAcquired signal to given connection. */
+void
+sbus_server_name_acquired(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *name);
+
+/* Send NameLost signal to given connection. */
+void
+sbus_server_name_lost(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *name);
+
+/* Initialize new sbus connection. */
+struct sbus_connection *
+sbus_connection_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ DBusConnection *dbus_conn,
+ const char *address,
+ const char *dbus_name,
+ enum sbus_connection_type type,
+ time_t *last_activity_time);
+
+/* Replace current D-Bus connection context with a new one. */
+errno_t sbus_connection_replace(struct sbus_connection *sbus_conn,
+ DBusConnection *dbus_conn);
+
+/* Integrate an sbus connection with tevent loop. */
+errno_t sbus_connection_tevent_enable(struct sbus_connection *conn);
+void sbus_connection_tevent_disable(struct sbus_connection *conn);
+
+/* Mark that this connection is currently active (new method call arrived). */
+void sbus_connection_mark_active(struct sbus_connection *conn);
+
+/* Set connection well known name. */
+errno_t sbus_connection_set_name(struct sbus_connection *conn,
+ const char *name);
+
+/* Free connection next time the event loop is processed. We do it
+ * asynchronously to avoid a potential use after free from tevent
+ * callbacks that may be already scheduled. */
+void sbus_connection_free(struct sbus_connection *conn);
+
+/* Try to reconnect to D-Bus if connection was dropped. It will notify
+ * the user with a result through a reconnection callback. */
+void sbus_reconnect(struct sbus_connection *conn);
+
+/* Initialize reconnection structure. */
+struct sbus_reconnect *sbus_reconnect_init(TALLOC_CTX *mem_ctx);
+
+/* Disable any further reconnection attempts. */
+void sbus_reconnect_disable(struct sbus_connection *conn);
+
+/* Return true if we are already trying to reconnect. */
+bool sbus_reconnect_in_progress(struct sbus_connection *conn);
+
+/* Return true if we are allowed to reconnect. */
+bool sbus_reconnect_enabled(struct sbus_connection *conn);
+
+/* Call Hello and RequestName methods. */
+struct tevent_req *
+sbus_connect_init_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ const char *name);
+
+errno_t sbus_connect_init_recv(struct tevent_req *req);
+
+/* Setup D-Bus event watchers to integrate D-Bus with event loop. */
+errno_t sbus_watch_server(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ DBusServer *server,
+ struct sbus_watch **_watch);
+
+errno_t sbus_watch_connection(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ DBusConnection *conn,
+ struct sbus_watch **_watch);
+
+/* Perform an access check on the given request using registered
+ * access check function of connection.*/
+errno_t sbus_check_access(struct sbus_connection *conn,
+ struct sbus_request *sbus_req);
+
+/* Setup D-Bus tevent dispatcher. */
+void sbus_dispatcher_setup(struct sbus_connection *conn);
+void sbus_dispatcher_disable(struct sbus_connection *conn);
+void sbus_dispatch_now(struct sbus_connection *conn);
+
+/* Send a new D-Bus message. */
+struct tevent_req *
+sbus_message_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ DBusMessage *msg,
+ int timeout_ms);
+
+errno_t
+sbus_message_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply);
+
+/* Send reply to a D-Bus message. The @reply is unreferenced when sent. */
+void sbus_reply(struct sbus_connection *conn,
+ DBusMessage *reply);
+
+/* Replz to a D-Bus message with error. */
+void sbus_reply_error(struct sbus_connection *conn,
+ DBusMessage *reply_to,
+ const char *error_name,
+ const char *error_message);
+
+/* Emit a new signal. */
+void sbus_emit_signal(struct sbus_connection *conn,
+ DBusMessage *msg);
+
+struct sbus_interface_list {
+ struct sbus_interface *interface;
+
+ struct sbus_interface_list *next;
+ struct sbus_interface_list *prev;
+};
+
+struct sbus_listener_list {
+ struct sbus_listener *listener;
+
+ struct sbus_listener_list *next;
+ struct sbus_listener_list *prev;
+};
+
+struct sbus_router {
+ struct sbus_connection *conn;
+
+ /**
+ * Table of <object-path, interface> pair. Contains description of
+ * sbus interfaces that are implemented and supported on given path.
+ */
+ hash_table_t *paths;
+
+ /**
+ * Table of <object-path, node> pair. A node contains factory function
+ * given for selected object paths. This function is used to create
+ * list of object paths nodes that are available under requested path
+ * during introspection.
+ */
+ hash_table_t *nodes;
+
+ /**
+ * Table of <interface.signal, listener> pair. Contains description of
+ * sbus signal listeners.
+ */
+ hash_table_t *listeners;
+};
+
+/* Initialize router structure. */
+struct sbus_router *
+sbus_router_init(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn);
+
+/* Re-register paths and listeners on dropped connection. */
+errno_t
+sbus_router_reset(struct sbus_connection *conn);
+
+/* Initialize object paths hash table. */
+hash_table_t *
+sbus_router_paths_init(TALLOC_CTX *mem_ctx);
+
+/* Register an interface with an object path. */
+errno_t
+sbus_router_paths_add(hash_table_t *table,
+ const char *path,
+ struct sbus_interface *iface);
+
+/* Lookup interface for given object path. */
+struct sbus_interface *
+sbus_router_paths_lookup(hash_table_t *table,
+ const char *path,
+ const char *iface_name);
+
+/* Return list of all interfaces registered with given object path. */
+errno_t
+sbus_router_paths_supported(TALLOC_CTX *mem_ctx,
+ hash_table_t *table,
+ const char *path,
+ struct sbus_interface_list **_list);
+
+/* Return all registered paths converted to node names. */
+const char **
+sbus_router_paths_nodes(TALLOC_CTX *mem_ctx,
+ hash_table_t *table);
+
+/* Check if given object path is registered. */
+bool
+sbus_router_paths_exist(hash_table_t *table,
+ const char *object_path);
+
+/* Initialize signal listeners hash table. */
+hash_table_t *
+sbus_router_listeners_init(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn);
+
+/* Add new signal listener. */
+errno_t
+sbus_router_listeners_add(hash_table_t *table,
+ const char *interface,
+ const char *signal_name,
+ struct sbus_listener *listener,
+ bool *_signal_known);
+
+/* Find all listeners for given signal. */
+struct sbus_listener_list *
+sbus_router_listeners_lookup(hash_table_t *table,
+ const char *interface,
+ const char *signal_name);
+
+/* Initialize nodes hash table. */
+hash_table_t *
+sbus_router_nodes_init(TALLOC_CTX *mem_ctx);
+
+/* Add new node factory. */
+errno_t
+sbus_router_nodes_add(hash_table_t *table,
+ struct sbus_node *node);
+
+/* Lookup node factory for given object path. */
+struct sbus_node *
+sbus_router_nodes_lookup(hash_table_t *table,
+ const char *path);
+
+/* Register new interface on path with the router. */
+errno_t
+sbus_router_add_path(struct sbus_router *router,
+ const char *path,
+ struct sbus_interface *iface);
+
+/* Register new interface on path with the router from map. */
+errno_t
+sbus_router_add_path_map(struct sbus_router *router,
+ struct sbus_path *map);
+
+/* Return D-Bus match rule for given signal. */
+char *
+sbus_router_signal_rule(TALLOC_CTX *mem_ctx,
+ const char *interface,
+ const char *signal_name);
+
+/* Parse interface.signal into interface and signal parts. */
+errno_t
+sbus_router_signal_parse(TALLOC_CTX *mem_ctx,
+ const char *qualified_signal,
+ char **_interface,
+ char **_signal_name);
+
+/* Process incoming messages. */
+DBusHandlerResult
+sbus_router_filter(struct sbus_connection *conn,
+ struct sbus_router *router,
+ DBusMessage *message);
+
+/* Handler for incoming D-Bus messages that are recieve by connection. */
+DBusHandlerResult
+sbus_connection_filter(DBusConnection *dbus_conn,
+ DBusMessage *message,
+ void *handler_data);
+
+/* Server filter function. Routes messages between connections. */
+DBusHandlerResult
+sbus_server_filter(DBusConnection *dbus_conn,
+ DBusMessage *message,
+ void *handler_data);
+
+/* Spy that ensures that the request list item is invalidated when the
+ * request or connection is freed. */
+struct sbus_request_spy;
+
+struct sbus_request_list {
+ struct tevent_req *req;
+ struct sbus_connection *conn;
+
+ bool is_invalid;
+ bool is_dbus;
+
+ struct {
+ struct sbus_request_spy *req;
+ struct sbus_request_spy *conn;
+ } spy;
+
+ struct sbus_request_list *prev;
+ struct sbus_request_list *next;
+};
+
+struct sbus_active_requests {
+ hash_table_t *incoming;
+ hash_table_t *outgoing;
+};
+
+/* Initialize active requests structure. */
+struct sbus_active_requests *
+sbus_active_requests_init(TALLOC_CTX *mem_ctx);
+
+/* Initialize request table. */
+hash_table_t *
+sbus_requests_init(TALLOC_CTX *mem_ctx);
+
+/* Add new active request into the table. */
+errno_t
+sbus_requests_add(hash_table_t *table,
+ const char *key,
+ struct sbus_connection *conn,
+ struct tevent_req *req,
+ bool is_dbus,
+ bool *_key_exists);
+
+/* Lookup active requests list. */
+struct sbus_request_list *
+sbus_requests_lookup(hash_table_t *table,
+ const char *key);
+
+/* Delete active requests list. */
+void
+sbus_requests_delete(struct sbus_request_list *list);
+
+/* Finish a request. */
+void
+sbus_requests_finish(struct sbus_request_list *item,
+ errno_t error);
+
+/* Terminate all requests. */
+void
+sbus_requests_terminate_all(hash_table_t *table,
+ errno_t error);
+
+/* Create new sbus request. */
+struct sbus_request *
+sbus_request_create(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *destination,
+ const char *interface,
+ const char *member,
+ const char *path);
+
+/* Run an incoming request handler. */
+struct tevent_req *
+sbus_incoming_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_connection *conn,
+ struct sbus_request *request,
+ const struct sbus_invoker *invoker,
+ const struct sbus_handler *handler,
+ const char *sender_name,
+ DBusMessageIter *read_iter,
+ DBusMessage *msg);
+
+errno_t
+sbus_incoming_request_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply);
+
+/* Issue a new outgoing request. */
+struct tevent_req *
+sbus_outgoing_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_connection *conn,
+ const char *key,
+ DBusMessage *msg);
+
+errno_t
+sbus_outgoing_request_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply);
+
+/* Initialize senders hash table. */
+hash_table_t *
+sbus_senders_init(TALLOC_CTX *mem_ctx);
+
+/* Remove sender from the table. */
+void
+sbus_senders_delete(hash_table_t *table,
+ const char *name);
+
+/* Create new sbus sender. */
+struct sbus_sender *
+sbus_sender_create(TALLOC_CTX *mem_ctx,
+ const char *name,
+ int64_t uid);
+
+/* Copy sbus sender structure. */
+struct sbus_sender *
+sbus_sender_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_sender *input);
+
+/* Resolve sender of the incoming message. */
+struct tevent_req *
+sbus_sender_resolve_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *destination,
+ const char *object_path,
+ const char *interface,
+ const char *member,
+ const char *name);
+
+errno_t
+sbus_sender_resolve_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sbus_sender **_sender);
+
+/* Generic receiver of sbus invoker. */
+errno_t
+sbus_invoker_recv(struct tevent_req *req);
+
+/* Build key of given request. */
+errno_t
+sbus_request_key(TALLOC_CTX *mem_ctx,
+ sbus_invoker_keygen keygen,
+ struct sbus_request *sbus_req,
+ void *input,
+ const char **_key);
+
+/**
+ * Create copy of provided interface. It expects that the interface was
+ * not created manually but through sbus API, therefore many of its fields
+ * must point to a static memory and same pointers are used for copy.
+ */
+struct sbus_interface *
+sbus_interface_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_interface *input);
+
+/* Find given method in interface. */
+const struct sbus_method *
+sbus_interface_find_method(struct sbus_interface *iface,
+ const char *method_name);
+
+/* Find given property in interface. */
+const struct sbus_property *
+sbus_interface_find_property(struct sbus_interface *iface,
+ enum sbus_property_access access,
+ const char *property_name);
+
+/**
+ * Create copy of provided signal listener. It expects that the listener was
+ * not created manually but through sbus API, therefore many of its fields
+ * must point to a static memory and same pointers are used for copy.
+ */
+struct sbus_listener *
+sbus_listener_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_listener *input);
+
+/**
+ * Create copy of provided node. It expects that the listener was
+ * not created manually but through sbus API, therefore many of its fields
+ * must point to a static memory and same pointers are used for copy.
+ */
+struct sbus_node *
+sbus_node_copy(TALLOC_CTX *mem_ctx,
+ struct sbus_node *input);
+
+/* Find given annotation. */
+const char *
+sbus_annotation_find(const struct sbus_annotation *annotations,
+ const char *name);
+
+/* Find given annotation and return its value as boolean. */
+bool
+sbus_annotation_find_as_bool(const struct sbus_annotation *annotations,
+ const char *name);
+
+/* Print a warning if specific annotations exist. */
+void
+sbus_annotation_warn(const struct sbus_interface *iface,
+ const struct sbus_method *method);
+
+/* Register D-Bus introspection interface. */
+errno_t
+sbus_register_introspection(struct sbus_router *router);
+
+/* Register D-Bus properties interface. */
+errno_t
+sbus_register_properties(struct sbus_router *router);
+
+/* Register listeners for org.freedesktop.DBus signals. */
+errno_t
+sbus_register_standard_signals(struct sbus_connection *conn);
+
+/* Send a D-Bus method call. Used in generated callers. */
+struct tevent_req *
+sbus_call_method_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_keygen keygen,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ void *input);
+
+errno_t
+sbus_call_method_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply);
+
+/* Send a D-Bus signal call. Used in generated callers. */
+void
+sbus_call_signal_send(struct sbus_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *path,
+ const char *iface,
+ const char *signal_name,
+ void *input);
+
+#endif /* _SBUS_PRIVATE_H_ */
diff --git a/src/sbus/sbus_request.h b/src/sbus/sbus_request.h
new file mode 100644
index 0000000..94c0f1a
--- /dev/null
+++ b/src/sbus/sbus_request.h
@@ -0,0 +1,169 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_REQUEST_H_
+#define _SBUS_REQUEST_H_
+
+#include <stdint.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util_errors.h"
+#include "sbus/sbus_opath.h"
+
+struct sbus_connection;
+
+/**
+ * There are several cases when the sender id cannot be resolved but the
+ * messages it self are valid. We use special uid values to distinguish
+ * between those cases.
+ */
+
+/**
+ * Sender is a message bus: org.freedesktop.dbus
+ */
+#define SBUS_SENDER_DBUS -1
+
+/**
+ * This is a hello message that is used to estahblished a communication
+ * channel. It is not possible to resolve the sender id at this point.
+ */
+#define SBUS_SENDER_HELLO -2
+
+/**
+ * The message is a signal and the sender has already quit before we
+ * managed to process it. Therefore it is not possible to resolve the
+ * send id.
+ */
+#define SBUS_SENDER_SIGNAL -3
+
+/**
+ * Identity of remote client who initiated this request.
+ */
+struct sbus_sender {
+ /**
+ * D-Bus name.
+ */
+ const char *name;
+
+ /**
+ * Unix user id.
+ */
+ int64_t uid;
+};
+
+enum sbus_request_type {
+ SBUS_REQUEST_METHOD,
+ SBUS_REQUEST_SIGNAL,
+ SBUS_REQUEST_PROPERTY_GET,
+ SBUS_REQUEST_PROPERTY_SET
+};
+
+/**
+ * An sbus request data passed to the method, signal or property handler.
+ */
+struct sbus_request {
+ struct sbus_connection *conn;
+
+ enum sbus_request_type type;
+
+ /**
+ * Identity of remote client who initiated this request.
+ */
+ const struct sbus_sender *sender;
+
+ /**
+ * Request destination name.
+ */
+ const char *destination;
+
+ /**
+ * An sbus interface name.
+ */
+ const char *interface;
+
+ /**
+ * An sbus member name, depending on type
+ * it is method, signal or property name.
+ */
+ union {
+ const char *method;
+ const char *signal_name;
+ const char *property;
+ const char *member;
+ };
+
+ /**
+ * Object path of an sbus object.
+ */
+ const char *path;
+};
+
+/**
+ * Await a finish of an outgoing sbus request.
+ *
+ * Sometimes you want to proceed with an operation only if a specific request
+ * is not in progress. This function will create a new tevent request that
+ * will either finish successfully (EOK) if outgoing request described with
+ * @key is not in progress or it will await its finish and return its result.
+ *
+ * @param mem_ctx Memory context.
+ * @param conn An sbus connection.
+ * @param type Type of the sbus request.
+ * @param object_path Object path on which the request is executed.
+ * @param interface Interface of the request.
+ * @param member Either method or property name, depends on @type.
+ * @param additional_key Additional key that identifies the request.
+ *
+ * @return Tevent request or NULL on error.
+ */
+struct tevent_req *
+sbus_request_await_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *object_path,
+ const char *interface,
+ const char *member,
+ const char *additional_key);
+
+/**
+ * Receive result of @sbus_request_await_send.
+ *
+ * @return EOK on success, other errno code on failure.
+ */
+errno_t sbus_request_await_recv(struct tevent_req *req);
+
+/**
+ * This is a tevent req callback for messages where the caller is not
+ * interested in the reply. Usage:
+ *
+ * tevent_req_set_callback(subreq, sbus_unwanted_reply, NULL);
+ */
+void sbus_unwanted_reply(struct tevent_req *subreq);
+
+/**
+ * It is not possible to send NULL over D-Bus as a string, but if the code
+ * allows it, we can treat an empty string as NULL.
+ */
+#define SBUS_REQ_STRING_IS_EMPTY(str) ((str) == NULL || (str)[0] == '\0')
+#define SBUS_REQ_STRING_DEFAULT(str, def) (SBUS_REQ_STRING_IS_EMPTY(str) ? (def) : (str))
+#define SBUS_REQ_STRING(str) (SBUS_REQ_STRING_IS_EMPTY(str) ? NULL : (str))
+
+#endif /* _SBUS_REQUEST_H_ */
diff --git a/src/sbus/sbus_sync.h b/src/sbus/sbus_sync.h
new file mode 100644
index 0000000..fd14b01
--- /dev/null
+++ b/src/sbus/sbus_sync.h
@@ -0,0 +1,94 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_SYNC_H_
+#define _SBUS_SYNC_H_
+
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_typeof.h"
+#include "sbus/sbus_declarations.h"
+#include "sbus/sbus_interface.h"
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_errors.h"
+
+struct sbus_sync_connection;
+
+/**
+ * Connect to D-Bus system bus, naming this end-point @dbus_name.
+ *
+ * @param mem_ctx Memory context.
+ * @param dbus_name Name of this end-point.
+ *
+ * @return New synchronous sbus connection or NULL on error.
+ */
+struct sbus_sync_connection *
+sbus_sync_connect_system(TALLOC_CTX *mem_ctx,
+ const char *dbus_name);
+
+/**
+ * Connect to a private D-Bus bus at @address.
+ *
+ * @param mem_ctx Memory context.
+ * @param address Remote end-point address.
+ * @param dbus_name Name of this end-point.
+ *
+ * @return New synchronous sbus connection or NULL on error.
+ */
+struct sbus_sync_connection *
+sbus_sync_connect_private(TALLOC_CTX *mem_ctx,
+ const char *address,
+ const char *dbus_name);
+
+/**
+ * Send a D-Bus message over a synchronous sbus connection.
+ *
+ * This call will block until a reply is received.
+ *
+ * @param mem_ctx Memory context with which the reply will be bound.
+ * If NULL, the reply is not bound with talloc context.
+ * @param conn Synchronous sbus connection.
+ * @param msg Message to be sent.
+ * @param timeout_ms Timeout is miliseconds.
+ * @param _reply Output reply. If NULL no reply is expected.
+ *
+ * @return EOK on success, other errno code on error.
+ *
+ * @see SBUS_MESSAGE_TIMEOUT
+ */
+errno_t
+sbus_sync_message_send(TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ DBusMessage *msg,
+ int timeout_ms,
+ DBusMessage **_reply);
+
+/**
+ * Emit signal on synchronous sbus connection.
+ *
+ * @param conn Synchronous sbus connection.
+ * @param msg Message to be sent.
+ */
+void sbus_sync_emit_signal(struct sbus_sync_connection *conn,
+ DBusMessage *msg);
+
+#endif /* _SBUS_SYNC_H_ */
diff --git a/src/sbus/sbus_sync_private.h b/src/sbus/sbus_sync_private.h
new file mode 100644
index 0000000..b43475d
--- /dev/null
+++ b/src/sbus/sbus_sync_private.h
@@ -0,0 +1,129 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_SYNC_PRIVATE_H_
+#define _SBUS_SYNC_PRIVATE_H_
+
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_sync.h"
+
+/* Generic type of an invoker arguments writer. */
+typedef errno_t (*sbus_invoker_writer_fn)(DBusMessageIter *, void *);
+
+/* Generic type of an invoker arguments reader. */
+typedef errno_t (*sbus_invoker_reader_fn)(TALLOC_CTX *, DBusMessageIter *, void *);
+
+/* Generic type for iterator readers. */
+typedef errno_t (*sbus_value_reader_fn)(DBusMessageIter *, void *);
+typedef errno_t (*sbus_value_reader_talloc_fn)(TALLOC_CTX *, DBusMessageIter *, void *);
+
+errno_t
+sbus_write_input(DBusMessage *msg,
+ sbus_invoker_writer_fn writer,
+ void *input);
+
+errno_t
+sbus_read_output(TALLOC_CTX *mem_ctx,
+ DBusMessage *msg,
+ sbus_invoker_reader_fn reader,
+ void *output);
+
+DBusMessage *
+sbus_create_method_call(TALLOC_CTX *mem_ctx,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ void *input);
+
+DBusMessage *
+sbus_create_signal_call(TALLOC_CTX *mem_ctx,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *path,
+ const char *iface,
+ const char *signal_name,
+ void *input);
+
+/* Create Property.Set method call. Used in generated callers. */
+DBusMessage *
+sbus_create_set_call(TALLOC_CTX *mem_ctx,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ const char *type,
+ void *input);
+
+/* Parse reply of an Properties.Get message. */
+errno_t
+sbus_parse_get_message(TALLOC_CTX *mem_ctx,
+ sbus_value_reader_fn reader,
+ sbus_value_reader_talloc_fn reader_talloc,
+ DBusMessage *msg,
+ void *_value_ptr);
+
+struct sbus_parse_getall_table {
+ /* Property name. */
+ const char *name;
+
+ /* Read to read its value. */
+ sbus_value_reader_fn reader;
+ sbus_value_reader_talloc_fn reader_talloc;
+
+ /* Destination where to store the value. */
+ void *destination;
+ bool *is_set;
+};
+
+/* Parse reply of an Properties.GetAll message. */
+errno_t
+sbus_parse_getall_message(TALLOC_CTX *mem_ctx,
+ struct sbus_parse_getall_table *table,
+ DBusMessage *msg);
+
+errno_t
+sbus_sync_call_method(TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ void *input,
+ DBusMessage **_reply);
+
+void
+sbus_sync_call_signal(struct sbus_sync_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *path,
+ const char *iface,
+ const char *signal_name,
+ void *input);
+
+#endif /* _SBUS_SYNC_PRIVATE_H_ */
diff --git a/src/sbus/sbus_typeof.h b/src/sbus/sbus_typeof.h
new file mode 100644
index 0000000..2ab35bc
--- /dev/null
+++ b/src/sbus/sbus_typeof.h
@@ -0,0 +1,53 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#ifndef _SBUS_TYPEOF_H_
+#define _SBUS_TYPEOF_H_
+
+/**
+ * Provide a compile-time type safety for sbus function and its private data.
+ *
+ * We use GCC __typeof__ extension to achieve this. We retrieve the private
+ * data type and create the expected handler function type with it. If the
+ * method accepts parsed D-Bus arguments, they are appended with variadic
+ * parameters. We check that the handler type matches the expected type
+ * and return the sbus_handler structure value.
+ *
+ * We also use __attribute__((unused)) to suppress compiler warning about
+ * unused __fn.
+ *
+ * We do not perform this check on platforms where this extension is not
+ * available and just create a generic handler. This does not matter since
+ * we test compilation with GCC anyway.
+ */
+#if (__GNUC__ >= 3)
+
+#define SBUS_CHECK_FUNCTION(handler, return_type, ...) ({ \
+ __attribute__((unused)) return_type (*__fn)(__VA_ARGS__) = (handler); \
+})
+
+#define SBUS_TYPEOF(data) __typeof__(data)
+
+#else
+#define SBUS_CHECK_FUNCTION(handler, return_type, ...)
+#define SBUS_TYPEOF(data) (void*)
+#endif
+
+#endif /* _SBUS_TYPEOF_H_ */
diff --git a/src/sbus/server/sbus_server.c b/src/sbus/server/sbus_server.c
new file mode 100644
index 0000000..9c9ddc8
--- /dev/null
+++ b/src/sbus/server/sbus_server.c
@@ -0,0 +1,778 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "sbus/sbus_private.h"
+
+struct sbus_server_on_connection {
+ const char *name;
+ sbus_server_on_connection_cb callback;
+ sbus_server_on_connection_data data;
+};
+
+static const char *
+sbus_server_get_filename(const char *address)
+{
+ const char *filename;
+
+ filename = strchr(address, '/');
+ if (filename == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected dbus address [%s].\n", address);
+ return NULL;
+ }
+
+ return filename;
+}
+
+static const char *
+sbus_server_get_socket_address(TALLOC_CTX *mem_ctx,
+ const char *address,
+ bool use_symlink)
+{
+ unsigned long pid;
+
+ if (!use_symlink) {
+ return talloc_strdup(mem_ctx, address);
+ }
+
+ pid = getpid();
+ return talloc_asprintf(mem_ctx, "%s.%lu", address, pid);
+}
+
+static errno_t
+sbus_server_get_socket(TALLOC_CTX *mem_ctx,
+ const char *address,
+ bool use_symlink,
+ const char **_socket_address,
+ const char **_filename,
+ const char **_symlink)
+{
+ const char *symlink = NULL;
+ const char *socket_address;
+ const char *filename;
+
+ /* Get D-Bus socket address. */
+ socket_address = sbus_server_get_socket_address(mem_ctx, address,
+ use_symlink);
+ if (socket_address == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ /* Get system files names. */
+ filename = sbus_server_get_filename(socket_address);
+ if (filename == NULL) {
+ return EINVAL;
+ }
+
+ if (use_symlink) {
+ symlink = sbus_server_get_filename(address);
+ if (symlink == NULL) {
+ return EINVAL;
+ }
+ }
+
+ if (_socket_address != NULL) {
+ *_socket_address = socket_address;
+ }
+
+ if (_filename != NULL) {
+ *_filename = filename;
+ }
+
+ if (_symlink != NULL) {
+ *_symlink = symlink;
+ }
+
+ return EOK;
+}
+
+static DBusServer *
+sbus_server_socket_listen(const char *socket_address)
+{
+ DBusServer *server;
+ DBusError error;
+ char *server_address;
+
+ dbus_error_init(&error);
+
+ server = dbus_server_listen(socket_address, &error);
+ if (server == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to start a D-Bus server at "
+ "%s [%s]: %s\n", socket_address, error.name, error.message);
+ } else {
+ server_address = dbus_server_get_address(server);
+ DEBUG(SSSDBG_TRACE_FUNC, "D-BUS Server listening on %s\n", server_address);
+ free(server_address);
+ }
+
+ dbus_error_free(&error);
+
+ return server;
+}
+
+static errno_t
+sbus_server_symlink_create(const char *filename,
+ const char *symlink_filename)
+{
+ errno_t ret;
+
+ if (symlink_filename == NULL) {
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Symlinking the dbus path %s to a link %s\n",
+ filename, symlink_filename);
+ errno = 0;
+ ret = symlink(filename, symlink_filename);
+ if (ret != 0 && errno == EEXIST) {
+ /* Perhaps cruft after a previous server? */
+ errno = 0;
+ ret = unlink(symlink_filename);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot remove old symlink '%s': [%d][%s].\n",
+ symlink_filename, ret, strerror(ret));
+ return EIO;
+ }
+ errno = 0;
+ ret = symlink(filename, symlink_filename);
+ }
+
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "symlink() failed on file '%s': [%d][%s].\n",
+ filename, ret, strerror(ret));
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static errno_t
+sbus_server_symlink_read(const char *name, char *buf, size_t buf_len)
+{
+ ssize_t num_read = 0;
+ errno_t ret;
+
+ errno = 0;
+ num_read = readlink(name, buf, buf_len - 1);
+ if (num_read < 0) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to read link target [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ buf[num_read] = '\0';
+
+ return EOK;
+}
+
+static errno_t
+sbus_server_symlink_pidpath(const char *name, char *buf, size_t buf_len)
+{
+ int ret;
+
+ ret = snprintf(buf, buf_len, "%s.%lu", name, (unsigned long)getpid());
+ if (ret < 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "snprintf failed\n");
+ return EIO;
+ } else if (ret >= PATH_MAX) {
+ DEBUG(SSSDBG_OP_FAILURE, "path too long?!?!\n");
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static void
+sbus_server_symlink_remove(const char *name)
+{
+ char target[PATH_MAX];
+ char pidpath[PATH_MAX];
+ errno_t ret;
+
+ ret = sbus_server_symlink_read(name, target, PATH_MAX);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "The symlink points to [%s]\n", target);
+
+ ret = sbus_server_symlink_pidpath(name, pidpath, PATH_MAX);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "The path including our pid is [%s]\n", pidpath);
+
+ /* We can only remove the symlink if it points to
+ * a socket with the same PID. */
+
+ if (strcmp(pidpath, target) != 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Will not remove symlink, seems to be "
+ "owned by another process\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = unlink(name);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "unlink failed to remove [%s] [%d]: %s\n",
+ name, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to remove symlink [%s]\n", name);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Symlink removed [%s]\n", name);
+}
+
+static errno_t
+sbus_server_check_file(const char *filename, uid_t uid, gid_t gid)
+{
+ struct stat stat_buf;
+ errno_t ret;
+
+ /* Both check_file and chmod can handle both the symlink and the socket */
+ ret = check_file(filename, getuid(), getgid(), S_IFSOCK, S_IFMT,
+ &stat_buf, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "check_file failed for [%s].\n", filename);
+ return ret;
+ }
+
+ if ((stat_buf.st_mode & ~S_IFMT) != (S_IRUSR | S_IWUSR)) {
+ ret = chmod(filename, (S_IRUSR | S_IWUSR));
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "chmod failed for [%s] [%d]: %s\n",
+ filename, ret, sss_strerror(ret));
+ return ret;
+ }
+ }
+
+ if (stat_buf.st_uid != uid || stat_buf.st_gid != gid) {
+ ret = chown(filename, uid, gid);
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "chown failed for [%s] [%d]: %s\n",
+ filename, ret, sss_strerror(ret));
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static DBusServer *
+sbus_server_setup_dbus(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *address,
+ bool use_symlink,
+ uid_t uid,
+ gid_t gid,
+ const char **_symlink)
+{
+ TALLOC_CTX *tmp_ctx;
+ DBusServer *dbus_server = NULL;
+ bool symlink_created = false;
+ const char *symlink = NULL;
+ const char *socket_address;
+ const char *filename;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return NULL;
+ }
+
+ /* Get socket address. */
+ ret = sbus_server_get_socket(tmp_ctx, address, use_symlink,
+ &socket_address, &filename, &symlink);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* Start listening on this socket. This will also create the socket. */
+ dbus_server = sbus_server_socket_listen(socket_address);
+ if (dbus_server == NULL) {
+ ret = EIO;
+ goto done;
+ }
+
+ /* Create symlink if requested. */
+ if (use_symlink) {
+ ret = sbus_server_symlink_create(filename, symlink);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not create symlink [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = EIO;
+ goto done;
+ }
+
+ symlink_created = true;
+ }
+
+ /* Check file permissions and setup proper owner. */
+ ret = sbus_server_check_file(filename, uid, gid);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (use_symlink) {
+ *_symlink = talloc_strdup(mem_ctx, symlink);
+ if (*_symlink == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ *_symlink = NULL;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK && dbus_server != NULL) {
+ dbus_server_disconnect(dbus_server);
+ dbus_server_unref(dbus_server);
+
+ if (symlink_created) {
+ sbus_server_symlink_remove(symlink);
+ }
+
+ return NULL;
+ }
+
+ return dbus_server;
+}
+
+static bool
+sbus_server_filter_add(struct sbus_server *server,
+ DBusConnection *dbus_conn)
+{
+ dbus_bool_t dbret;
+
+ /* Add a connection filter that is used to process input messages. */
+ dbret = dbus_connection_add_filter(dbus_conn, sbus_server_filter,
+ server, NULL);
+ if (dbret == false) {
+ return false;
+ }
+
+ return true;
+}
+
+static dbus_bool_t
+sbus_server_check_connection_uid(DBusConnection *dbus_conn,
+ unsigned long uid,
+ void *data)
+{
+ struct sbus_server *sbus_server;
+
+ sbus_server = talloc_get_type(data, struct sbus_server);
+
+ if (uid == 0 || uid == sbus_server->uid) {
+ return true;
+ }
+
+ return false;
+}
+
+static void
+sbus_server_new_connection(DBusServer *dbus_server,
+ DBusConnection *dbus_conn,
+ void *data)
+{
+ struct sbus_server *sbus_server;
+ struct sbus_connection *sbus_conn;
+ dbus_bool_t dbret;
+ errno_t ret;
+ bool bret;
+
+ sbus_server = talloc_get_type(data, struct sbus_server);
+
+ DEBUG(SSSDBG_FUNC_DATA, "Adding connection %p.\n", dbus_conn);
+
+ /* Allow access from uid that is associated with this sbus server. */
+ dbus_connection_set_unix_user_function(dbus_conn,
+ sbus_server_check_connection_uid,
+ sbus_server, NULL);
+
+ /* First, add a message filter that will take care of routing messages
+ * between connections. */
+ bret = sbus_server_filter_add(sbus_server, dbus_conn);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add server filter!\n");
+ return;
+ }
+
+ /**
+ * @dbus_conn is unreferenced in libdbus by the caller of this new
+ * connection function thus we must not unreference it here. Its
+ * reference counter is increased in @sbus_connection_init.
+ */
+
+ sbus_conn = sbus_connection_init(sbus_server, sbus_server->ev, dbus_conn,
+ NULL, NULL, SBUS_CONNECTION_CLIENT,
+ NULL);
+ if (sbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Closing connection, unable to setup\n");
+ dbus_connection_close(dbus_conn);
+ return;
+ }
+
+ dbret = dbus_connection_set_data(dbus_conn, sbus_server->data_slot,
+ sbus_conn, NULL);
+ if (!dbret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Closing connection, unable to set data\n");
+ talloc_free(sbus_conn);
+ return;
+ }
+
+ if (sbus_server->on_connection->callback != NULL) {
+ ret = sbus_server->on_connection->callback(sbus_conn,
+ sbus_server->on_connection->data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Closing connection, new connection "
+ "callback failed [%d]: %s\n", ret, sss_strerror(ret));
+ talloc_free(sbus_conn);
+ return;
+ }
+ }
+}
+
+static errno_t
+sbus_server_tevent_enable(struct sbus_server *server)
+{
+ errno_t ret;
+
+ ret = sbus_watch_server(server, server->ev, server->server,
+ &server->watch_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup D-Bus watch [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ /* Set function that is called each time a new client is connected. */
+ dbus_server_set_new_connection_function(server->server,
+ sbus_server_new_connection,
+ server, NULL);
+
+ return EOK;
+}
+
+static void
+sbus_server_tevent_disable(struct sbus_server *server)
+{
+ dbus_server_set_new_connection_function(server->server, NULL, NULL, NULL);
+ talloc_zfree(server->watch_ctx);
+}
+
+static void
+sbus_server_name_owner_changed(struct sbus_server *server,
+ const char *name,
+ const char *new_owner,
+ const char *old_owner)
+{
+ DBusMessage *message;
+
+ /* We can't really send signals when the server is being destroyed. */
+ if (server == NULL || server->disconnecting) {
+ return;
+ }
+
+ message = sbus_signal_create(NULL, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged",
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_STRING, &old_owner);
+ if (message == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return;
+ }
+
+ dbus_message_set_sender(message, DBUS_SERVICE_DBUS);
+
+ /* Send the signal. */
+ sbus_server_matchmaker(server, NULL, name, message);
+}
+
+void
+sbus_server_name_acquired(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *name)
+{
+ DBusMessage *message;
+
+ message = sbus_signal_create(NULL, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameAcquired", DBUS_TYPE_STRING, &name);
+ if (message == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return;
+ }
+
+ dbus_message_set_sender(message, DBUS_SERVICE_DBUS);
+ dbus_message_set_destination(message, conn->unique_name);
+ dbus_connection_send(conn->connection, message, NULL);
+
+ sbus_server_name_owner_changed(server, name, name, "");
+}
+
+void
+sbus_server_name_lost(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *name)
+{
+ DBusMessage *message;
+
+ if (name[0] == ':') {
+ /* The connection is being terminated. Do not send the signal. */
+ return;
+ }
+
+ message = sbus_signal_create(NULL, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameLost", DBUS_TYPE_STRING, &name);
+ if (message == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return;
+ }
+
+ dbus_message_set_sender(message, DBUS_SERVICE_DBUS);
+ dbus_message_set_destination(message, conn->unique_name);
+ dbus_connection_send(conn->connection, message, NULL);
+
+ sbus_server_name_owner_changed(server, name, "", name);
+}
+
+static void
+sbus_server_name_remove_from_table_cb(hash_entry_t *item,
+ hash_destroy_enum type,
+ void *pvt)
+{
+ struct sbus_server *server;
+ const char *name;
+
+ /* We can't really send signals when the server is being destroyed. */
+ if (type == HASH_TABLE_DESTROY) {
+ return;
+ }
+
+ server = talloc_get_type(pvt, struct sbus_server);
+ name = item->key.str;
+
+ sbus_server_name_owner_changed(server, name, "", name);
+}
+
+static int sbus_server_destructor(struct sbus_server *server)
+{
+ if (server->server == NULL) {
+ return 0;
+ }
+
+ server->disconnecting = true;
+
+ /* Remove tevent integration first. */
+ sbus_server_tevent_disable(server);
+
+ if (server->data_slot != -1) {
+ dbus_connection_free_data_slot(&server->data_slot);
+ }
+
+ /* Release server. */
+ dbus_server_disconnect(server->server);
+ dbus_server_unref(server->server);
+
+ if (server->symlink != NULL) {
+ sbus_server_symlink_remove(server->symlink);
+ }
+
+ return 0;
+}
+
+struct sbus_server *
+sbus_server_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *address,
+ bool use_symlink,
+ uint32_t max_connections,
+ uid_t uid,
+ gid_t gid,
+ sbus_server_on_connection_cb on_conn_cb,
+ sbus_server_on_connection_data on_conn_data)
+{
+ DBusServer *dbus_server;
+ struct sbus_server *sbus_server;
+ const char *symlink;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ sbus_server = talloc_zero(mem_ctx, struct sbus_server);
+ if (sbus_server == NULL) {
+ return NULL;
+ }
+
+ sbus_server->data_slot = -1;
+ talloc_set_destructor(sbus_server, sbus_server_destructor);
+
+ dbus_server = sbus_server_setup_dbus(sbus_server, ev, address,
+ use_symlink, uid, gid, &symlink);
+ if (dbus_server == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup a D-Bus server!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ sbus_server->ev = ev;
+ sbus_server->server = dbus_server;
+ sbus_server->symlink = talloc_steal(sbus_server, symlink);
+ sbus_server->max_connections = max_connections;
+ sbus_server->name.major = 1;
+ sbus_server->name.minor = 0;
+ sbus_server->uid = uid;
+ sbus_server->gid = gid;
+
+ sbus_server->on_connection = talloc_zero(sbus_server,
+ struct sbus_server_on_connection);
+ if (sbus_server->on_connection == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (on_conn_cb != NULL) {
+ _sbus_server_set_on_connection(sbus_server, "on-connection", on_conn_cb,
+ on_conn_data);
+ }
+
+ sbus_server->names = sss_ptr_hash_create(sbus_server,
+ sbus_server_name_remove_from_table_cb, sbus_server);
+ if (sbus_server->names == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ sbus_server->match_rules = sss_ptr_hash_create(sbus_server, NULL, NULL);
+ if (sbus_server->match_rules == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ sbus_server->router = sbus_router_init(sbus_server, NULL);
+ if (sbus_server->router == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_server_setup_interface(sbus_server);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup bus interface [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ dbret = dbus_connection_allocate_data_slot(&sbus_server->data_slot);
+ if (!dbret) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_server_tevent_enable(sbus_server);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to integrate with tevent [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(sbus_server);
+ return NULL;
+ }
+
+ return sbus_server;
+}
+
+struct sbus_connection *
+sbus_server_find_connection(struct sbus_server *server, const char *name)
+{
+ return sss_ptr_hash_lookup(server->names, name, struct sbus_connection);
+}
+
+void
+_sbus_server_set_on_connection(struct sbus_server *server,
+ const char *name,
+ sbus_server_on_connection_cb on_connection_cb,
+ sbus_server_on_connection_data data)
+{
+ if (server == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: server is NULL\n");
+ return;
+ }
+
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name is NULL\n");
+ return;
+ }
+
+ if (on_connection_cb == NULL) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Unsetting on connectoin callback\n");
+ server->on_connection->callback = NULL;
+ server->on_connection->data = NULL;
+ server->on_connection->name = NULL;
+ return;
+ }
+
+ if (server->on_connection->callback != NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: on connection callback is "
+ "already set to %s\n", server->on_connection->name);
+ return;
+ }
+
+ server->on_connection->callback = on_connection_cb;
+ server->on_connection->data = data;
+ server->on_connection->name = name;
+}
diff --git a/src/sbus/server/sbus_server_handler.c b/src/sbus/server/sbus_server_handler.c
new file mode 100644
index 0000000..d4e4547
--- /dev/null
+++ b/src/sbus/server/sbus_server_handler.c
@@ -0,0 +1,190 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <string.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "sbus/sbus_private.h"
+
+static DBusHandlerResult
+sbus_server_resend_message(struct sbus_server *server,
+ struct sbus_connection *conn,
+ DBusMessage *message,
+ const char *destination)
+{
+ struct sbus_connection *destconn;
+
+ destconn = sbus_server_find_connection(server, destination);
+ if (destconn == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "Trying to send a message to an unknown "
+ "destination: %s\n", destination);
+ sbus_reply_error(conn, message, DBUS_ERROR_SERVICE_UNKNOWN, destination);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ /* Message is unreferenced by libdbus. */
+ dbus_connection_send(destconn->connection, message, NULL);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+DBusHandlerResult
+sbus_server_route_signal(struct sbus_server *server,
+ struct sbus_connection *conn,
+ DBusMessage *message,
+ const char *destination)
+{
+ errno_t ret;
+
+ /* If a destination is set (unusual but possible) we simply send the
+ * signal to its desired destination. */
+ if (destination != NULL) {
+ return sbus_server_resend_message(server, conn, message, destination);
+ }
+
+ /* Otherwise we need to send it to all connections that listen to it. */
+ ret = sbus_server_matchmaker(server, conn, NULL, message);
+ if (ret == EOK) {
+ return DBUS_HANDLER_RESULT_HANDLED;
+ } else if (ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send signal [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult
+sbus_server_route_message(struct sbus_server *server,
+ struct sbus_connection *conn,
+ DBusMessage *message,
+ const char *destination)
+{
+ if (strcmp(destination, DBUS_SERVICE_DBUS) == 0) {
+ /* This message is addressed to D-Bus service. We must reply to it. */
+ return sbus_router_filter(conn, server->router, message);
+ }
+
+ return sbus_server_resend_message(server, conn, message, destination);
+}
+
+static bool
+sbus_server_check_access(struct sbus_connection *conn,
+ DBusMessage *message)
+{
+ const char *destination;
+ const char *interface;
+ const char *member;
+ int type;
+
+ /* Connection must first obtain its unique name through Hello method. */
+ if (conn->unique_name != NULL) {
+ return true;
+ }
+
+ destination = dbus_message_get_destination(message);
+ interface = dbus_message_get_interface(message);
+ member = dbus_message_get_member(message);
+ type = dbus_message_get_type(message);
+
+ if (type != DBUS_MESSAGE_TYPE_METHOD_CALL) {
+ return false;
+ }
+
+ if (strcmp(destination, DBUS_SERVICE_DBUS) != 0) {
+ return false;
+ }
+
+ if (strcmp(interface, DBUS_INTERFACE_DBUS) != 0) {
+ return false;
+ }
+
+ if (strcmp(member, "Hello") != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+DBusHandlerResult
+sbus_server_filter(DBusConnection *dbus_conn,
+ DBusMessage *message,
+ void *handler_data)
+{
+ struct sbus_server *server;
+ struct sbus_connection *conn;
+ const char *destination;
+ const char *sender;
+ dbus_bool_t dbret;
+ int type;
+
+ server = talloc_get_type(handler_data, struct sbus_server);
+
+ /* We can't really send signals when the server is being destroyed. */
+ if (server == NULL || server->disconnecting) {
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ conn = dbus_connection_get_data(dbus_conn, server->data_slot);
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown connection!\n");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!sbus_server_check_access(conn, message)) {
+ sbus_reply_error(conn, message, DBUS_ERROR_ACCESS_DENIED,
+ "Connection did not call org.freedesktop.DBus.Hello");
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ /* We always require a sender but it may not be assigned yet. We prefer
+ * well known name if set. */
+ sender = sbus_connection_get_name(conn);
+ dbret = dbus_message_set_sender(message, sender);
+ if (!dbret) {
+ sbus_reply_error(conn, message, DBUS_ERROR_FAILED,
+ "Unable to set sender");
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ /* Set sender may reallocate internal fields so this needs to be read
+ * after we call dbus_message_set_sender(). */
+ destination = dbus_message_get_destination(message);
+ type = dbus_message_get_type(message);
+
+ if (type == DBUS_MESSAGE_TYPE_SIGNAL) {
+ return sbus_server_route_signal(server, conn, message, destination);
+ }
+
+ /* We do not allow method calls without destination. */
+ if (destination == NULL) {
+ sbus_reply_error(conn, message, DBUS_ERROR_FAILED,
+ "Non-signal multicast calls are not supported");
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return sbus_server_route_message(server, conn, message, destination);
+}
diff --git a/src/sbus/server/sbus_server_interface.c b/src/sbus/server/sbus_server_interface.c
new file mode 100644
index 0000000..9c0ba0a
--- /dev/null
+++ b/src/sbus/server/sbus_server_interface.c
@@ -0,0 +1,429 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <dhash.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "sbus/sbus_private.h"
+#include "sbus/interface_dbus/sbus_dbus_server.h"
+
+static errno_t
+sbus_server_bus_hello(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char **_out)
+{
+ struct sbus_connection *conn;
+ uint32_t attempts;
+ errno_t ret;
+ char *name;
+
+ /* Generation of unique names is inspired by libdbus source:
+ * create_unique_client_name() from bus/driver.c */
+
+ conn = sbus_req->conn;
+ if (conn->unique_name != NULL) {
+ return EEXIST;
+ }
+
+ for (attempts = 0; attempts < server->max_connections; attempts++) {
+ server->name.minor++;
+ if (server->name.minor == 0) {
+ /* Overflow of minor version. Increase major version. */
+ server->name.major++;
+ server->name.minor = 1;
+ if (server->name.major == 0) {
+ /* Overflow of major version. D-Bus would die here,
+ * we will just start over. */
+ server->name.major = 1;
+ server->name.minor = 0;
+ continue;
+ }
+ }
+
+ name = talloc_asprintf(NULL, ":%u.%u",
+ server->name.major, server->name.minor);
+ if (name == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_ptr_hash_add(server->names, name, conn,
+ struct sbus_connection);
+ if (ret == EEXIST) {
+ talloc_free(name);
+ continue;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Assigning unique name %s to connection %p\n",
+ name, conn);
+
+ conn->unique_name = talloc_steal(conn, name);
+ sbus_server_name_acquired(server, conn, name);
+ *_out = name;
+
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE, "Maximum number [%u] of active connections "
+ "has been reached.\n", server->max_connections);
+
+ return ERR_SBUS_CONNECTION_LIMIT;
+}
+
+static errno_t
+sbus_server_bus_request_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *name,
+ uint32_t flags,
+ uint32_t *_result)
+{
+ struct sbus_connection *conn;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Requesting name: %s\n", name);
+
+ if (name[0] == ':') {
+ DEBUG(SSSDBG_OP_FAILURE, "Can not assign unique name: %s\n", name);
+ return EINVAL;
+ }
+
+ conn = sss_ptr_hash_lookup(server->names, name, struct sbus_connection);
+ if (conn == NULL) {
+ /* We want to remember only the first well known name. */
+ if (sbus_req->conn->wellknown_name == NULL) {
+ ret = sbus_connection_set_name(sbus_req->conn, name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set well known name "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ }
+ }
+
+ ret = sss_ptr_hash_add(server->names, name, sbus_req->conn,
+ struct sbus_connection);
+ if (ret == EOK) {
+ sbus_server_name_acquired(server, sbus_req->conn, name);
+ *_result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+ }
+
+ return ret;
+ }
+
+ if (conn == sbus_req->conn) {
+ *_result = DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
+ return EOK;
+ }
+
+ *_result = DBUS_REQUEST_NAME_REPLY_EXISTS;
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_release_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *name,
+ uint32_t *_result)
+{
+ struct sbus_connection *conn;
+
+ if (name[0] == ':') {
+ DEBUG(SSSDBG_OP_FAILURE, "Can not release unique name: %s\n", name);
+ return EINVAL;
+ }
+
+ conn = sss_ptr_hash_lookup(server->names, name, struct sbus_connection);
+ if (conn == NULL) {
+ *_result = DBUS_RELEASE_NAME_REPLY_NON_EXISTENT;
+ return EOK;
+ }
+
+ if (conn != sbus_req->conn) {
+ *_result = DBUS_RELEASE_NAME_REPLY_NOT_OWNER;
+ return EOK;
+ }
+
+ sss_ptr_hash_delete(server->names, name, false);
+ sbus_server_name_lost(server, conn, name);
+ *_result = DBUS_RELEASE_NAME_REPLY_RELEASED;
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_name_has_owner(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *name,
+ bool *_result)
+{
+ struct sbus_connection *conn;
+
+ conn = sss_ptr_hash_lookup(server->names, name, struct sbus_connection);
+ if (conn == NULL) {
+ *_result = false;
+ return EOK;
+ }
+
+ *_result = true;
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_list_names(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char ***_names)
+{
+ hash_key_t *keys;
+ const char **names;
+ unsigned long count;
+ unsigned long i;
+ int hret;
+
+ hret = hash_keys(server->names, &count, &keys);
+ if (hret != HASH_SUCCESS) {
+ return ENOMEM;
+ }
+
+ names = talloc_zero_array(mem_ctx, const char *, count + 2);
+ if (names == NULL) {
+ talloc_free(keys);
+ return ENOMEM;
+ }
+
+ names[0] = DBUS_SERVICE_DBUS;
+ for (i = 1; i < count + 1; i++) {
+ names[i] = keys[i - 1].str;
+ }
+
+ *_names = names;
+
+ talloc_free(keys);
+
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_list_activatable_names(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char ***_names)
+{
+ /* We do not support activatable services. */
+ *_names = NULL;
+
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_get_name_owner(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *name,
+ const char **_unique_name)
+{
+ struct sbus_connection *conn;
+
+ /* The bus service owns itself. */
+ if (strcmp(name, DBUS_SERVICE_DBUS) == 0) {
+ *_unique_name = DBUS_SERVICE_DBUS;
+ return EOK;
+ }
+
+ conn = sss_ptr_hash_lookup(server->names, name, struct sbus_connection);
+ if (conn == NULL) {
+ return ERR_SBUS_UNKNOWN_OWNER;
+ }
+
+ *_unique_name = conn->unique_name;
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_list_queued_owners(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *name,
+ const char ***_names)
+{
+ /* We do not support queued name requests. */
+ *_names = NULL;
+
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_get_connection_unix_user(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *name,
+ uint32_t *_uid)
+{
+ struct sbus_connection *conn;
+ unsigned long uid;
+ dbus_bool_t dbret;
+
+ if (strcmp(name, DBUS_SERVICE_DBUS) == 0) {
+ *_uid = server->uid;
+ return EOK;
+ }
+
+ conn = sss_ptr_hash_lookup(server->names, name, struct sbus_connection);
+ if (conn == NULL) {
+ return ERR_SBUS_UNKNOWN_OWNER;
+ }
+
+ dbret = dbus_connection_get_unix_user(conn->connection, &uid);
+ if (!dbret) {
+ return EIO;
+ }
+
+ *_uid = (uint32_t)uid;
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_get_connection_unix_process_id(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *name,
+ uint32_t *_pid)
+{
+ struct sbus_connection *conn;
+ unsigned long pid;
+ dbus_bool_t dbret;
+
+ if (strcmp(name, DBUS_SERVICE_DBUS) == 0) {
+ *_pid = getpid();
+ return EOK;
+ }
+
+ conn = sss_ptr_hash_lookup(server->names, name, struct sbus_connection);
+ if (conn == NULL) {
+ return ERR_SBUS_UNKNOWN_OWNER;
+ }
+
+ dbret = dbus_connection_get_unix_process_id(conn->connection, &pid);
+ if (!dbret) {
+ return EIO;
+ }
+
+ *_pid = (uint32_t)pid;
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_start_service_by_name(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *name,
+ uint32_t flags,
+ uint32_t *_result)
+{
+ struct sbus_connection *conn;
+
+ if (strcmp(name, DBUS_SERVICE_DBUS) == 0) {
+ *_result = DBUS_START_REPLY_ALREADY_RUNNING;
+ return EOK;
+ }
+
+ conn = sss_ptr_hash_lookup(server->names, name, struct sbus_connection);
+ if (conn == NULL) {
+ return ERR_SBUS_UNKNOWN_OWNER;
+ }
+
+ *_result = DBUS_START_REPLY_ALREADY_RUNNING;
+ return EOK;
+}
+
+static errno_t
+sbus_server_bus_add_match(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *rule)
+{
+ return sbus_server_add_match(server, sbus_req->conn, rule);
+}
+
+static errno_t
+sbus_server_bus_remove_match(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct sbus_server *server,
+ const char *rule)
+{
+ return sbus_server_remove_match(server, sbus_req->conn, rule);
+}
+
+errno_t
+sbus_server_setup_interface(struct sbus_server *server)
+{
+ errno_t ret;
+
+ SBUS_INTERFACE(bus,
+ org_freedesktop_DBus,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, Hello, sbus_server_bus_hello, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, RequestName, sbus_server_bus_request_name, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, ReleaseName, sbus_server_bus_release_name, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, NameHasOwner, sbus_server_bus_name_has_owner, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, ListNames, sbus_server_bus_list_names, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, ListActivatableNames, sbus_server_bus_list_activatable_names, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, GetNameOwner, sbus_server_bus_get_name_owner, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, ListQueuedOwners, sbus_server_bus_list_queued_owners, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, GetConnectionUnixUser, sbus_server_bus_get_connection_unix_user, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, GetConnectionUnixProcessID, sbus_server_bus_get_connection_unix_process_id, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, StartServiceByName, sbus_server_bus_start_service_by_name, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, AddMatch, sbus_server_bus_add_match, server),
+ SBUS_SYNC(METHOD, org_freedesktop_DBus, RemoveMatch, sbus_server_bus_remove_match, server)
+ ),
+ SBUS_SIGNALS(
+ SBUS_EMITS(org_freedesktop_DBus, NameOwnerChanged),
+ SBUS_EMITS(org_freedesktop_DBus, NameAcquired),
+ SBUS_EMITS(org_freedesktop_DBus, NameLost)
+ ),
+ SBUS_WITHOUT_PROPERTIES
+ );
+
+ /* Here we register interfaces on some object paths. */
+ struct sbus_path paths[] = {
+ {DBUS_PATH_DBUS, &bus},
+ {NULL, NULL}
+ };
+
+ ret = sbus_router_add_path_map(server->router, paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add paths [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
diff --git a/src/sbus/server/sbus_server_match.c b/src/sbus/server/sbus_server_match.c
new file mode 100644
index 0000000..4467c3b
--- /dev/null
+++ b/src/sbus/server/sbus_server_match.c
@@ -0,0 +1,450 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <string.h>
+#include <tevent.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "util/sss_ptr_list.h"
+#include "sbus/sbus_private.h"
+
+struct sbus_rule {
+ const char *type;
+ const char *interface;
+ const char *member;
+};
+
+static struct sbus_connection *
+sbus_match_find(struct sss_ptr_list *list,
+ struct sbus_connection *conn)
+{
+ struct sbus_connection *match_conn;
+
+ SSS_PTR_LIST_FOR_EACH(list, match_conn, struct sbus_connection) {
+ if (match_conn == conn) {
+ return match_conn;
+ }
+ }
+
+ return NULL;
+}
+
+static char *
+sbus_match_rule_key(TALLOC_CTX *mem_ctx,
+ const char *interface,
+ const char *member)
+{
+ if (interface == NULL) {
+ return NULL;
+ }
+
+ if (member == NULL) {
+ return talloc_strdup(mem_ctx, interface);
+ }
+
+ return talloc_asprintf(mem_ctx, "%s.%s", interface, member);
+}
+
+static struct sss_ptr_list *
+sbus_match_rule_create(struct sbus_server *server,
+ const char *key)
+{
+ struct sss_ptr_list *list;
+ errno_t ret;
+
+ list = sss_ptr_list_create(NULL, false);
+ if (list == NULL) {
+ return NULL;
+ }
+
+ ret = sss_ptr_hash_add(server->match_rules, key, list, struct sss_ptr_list);
+ if (ret != EOK) {
+ talloc_free(list);
+ return NULL;
+ }
+
+ talloc_steal(server->match_rules, list);
+
+ return list;
+}
+
+
+static struct sss_ptr_list *
+sbus_match_rule_get(struct sbus_server *server,
+ const char *interface,
+ const char *member,
+ bool create,
+ bool *_created)
+{
+ struct sss_ptr_list *list;
+ char *key;
+
+ key = sbus_match_rule_key(NULL, interface, member);
+ if (key == NULL) {
+ return NULL;
+ }
+
+ list = sss_ptr_hash_lookup(server->match_rules, key, struct sss_ptr_list);
+ if (!create || list != NULL) {
+ if (_created != NULL) {
+ *_created = false;
+ }
+ goto done;
+ }
+
+ list = sbus_match_rule_create(server, key);
+ if (list != NULL && _created != NULL) {
+ *_created = true;
+ }
+
+done:
+ talloc_free(key);
+ return list;
+}
+
+static errno_t
+sbus_match_rule_add(struct sbus_server *server,
+ struct sbus_connection *conn,
+ struct sbus_rule *rule)
+{
+ struct sbus_connection *match_conn;
+ struct sss_ptr_list *list;
+ bool created = false;
+ errno_t ret;
+
+ DEBUG(SSSDBG_TRACE_ALL, "Adding match rule for %s: %s.%s\n",
+ conn->unique_name, rule->interface, rule->member);
+
+ list = sbus_match_rule_get(server, rule->interface, rule->member,
+ true, &created);
+ if (list == NULL) {
+ return ENOMEM;
+ }
+
+ match_conn = sbus_match_find(list, conn);
+ if (match_conn != NULL) {
+ /* Match was already added. */
+ return EOK;
+ }
+
+ ret = sss_ptr_list_add(list, conn);
+ if (ret != EOK && created) {
+ talloc_free(list);
+ }
+
+ return ret;
+}
+
+static errno_t
+sbus_match_rule_remove(struct sbus_server *server,
+ struct sbus_connection *conn,
+ struct sbus_rule *rule)
+{
+ struct sbus_connection *match_conn;
+ struct sss_ptr_list *list;
+
+ DEBUG(SSSDBG_TRACE_ALL, "Removing match rule for %s: %s.%s\n",
+ conn->unique_name, rule->interface, rule->member);
+
+ list = sbus_match_rule_get(server, rule->interface, rule->member,
+ false, NULL);
+ if (list == NULL) {
+ return EOK;
+ }
+
+ match_conn = sbus_match_find(list, conn);
+ if (match_conn == NULL) {
+ return EOK;
+ }
+
+ sss_ptr_list_remove(list, match_conn);
+
+ if (sss_ptr_list_is_empty(list)) {
+ /* This will remove the list from the hash table. */
+ talloc_free(list);
+ }
+
+ return EOK;
+}
+
+static struct sss_ptr_list *
+sbus_match_rule_find(struct sbus_server *server,
+ const char *interface,
+ const char *member)
+{
+ return sbus_match_rule_get(server, interface, member, false, NULL);
+}
+
+static errno_t
+sbus_match_rule_parse_value(TALLOC_CTX *mem_ctx,
+ const char *item,
+ const char *name,
+ const char **_value)
+{
+ size_t name_len = strlen(name);
+ size_t iter_len;
+ const char *iter;
+ char quote;
+
+ if (strncmp(item, name, name_len) != 0) {
+ return ENOENT;
+ }
+
+ iter = item + name_len;
+
+ if (*iter == '=') {
+ iter++;
+ } else {
+ return ENOENT;
+ }
+
+ if (*iter == '"' || *iter == '\'') {
+ quote = *iter;
+ iter++;
+ } else {
+ return EINVAL;
+ }
+
+ iter_len = strlen(iter);
+ if (iter[iter_len - 1] != quote) {
+ return EINVAL;
+ }
+
+ *_value = talloc_strndup(mem_ctx, iter, iter_len - 1);
+ if (*_value == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static errno_t
+sbus_match_rule_parse_keys(TALLOC_CTX *mem_ctx,
+ char **tokens,
+ struct sbus_rule **_rule)
+{
+ struct sbus_rule *rule;
+ errno_t ret;
+ int i, j;
+
+ rule = talloc_zero(mem_ctx, struct sbus_rule);
+ if (rule == NULL) {
+ return ENOMEM;
+ }
+
+ struct {
+ const char *name;
+ const char **value;
+ } keys[] = {
+ {"type", &rule->type},
+ {"interface", &rule->interface},
+ {"member", &rule->member},
+ /* There are more keys in D-Bus specification, such as sender, path
+ * and destination. But we are not interested in them yet. */
+ {NULL, NULL}
+ };
+
+ for (i = 0; tokens[i] != NULL; i++) {
+ for (j = 0; keys[j].name != NULL; j++) {
+ ret = sbus_match_rule_parse_value(rule, tokens[i],
+ keys[j].name, keys[j].value);
+ if (ret == EOK) {
+ break;
+ } else if (ret == ENOENT) {
+ continue;
+ }
+
+ /* Error. */
+ talloc_free(rule);
+ return ret;
+ }
+ }
+
+ *_rule = rule;
+ return EOK;
+}
+
+static errno_t
+sbus_match_rule_parse_check(struct sbus_rule *rule)
+{
+ if (rule->type == NULL || strcmp(rule->type, "signal") != 0) {
+ return EINVAL;
+ }
+
+ if (rule->interface == NULL || rule->member == NULL) {
+ return EINVAL;
+ }
+
+ return EOK;
+}
+
+static errno_t
+sbus_match_rule_parse(TALLOC_CTX *mem_ctx,
+ const char *dbus_rule,
+ struct sbus_rule **_rule)
+{
+ struct sbus_rule *sbus_rule;
+ char **tokens;
+ errno_t ret;
+ int count;
+
+ ret = split_on_separator(NULL, dbus_rule, ',', true, true, &tokens, &count);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_match_rule_parse_keys(mem_ctx, tokens, &sbus_rule);
+ talloc_free(tokens);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_match_rule_parse_check(sbus_rule);
+ if (ret != EOK) {
+ talloc_free(sbus_rule);
+ goto done;
+ }
+
+ *_rule = sbus_rule;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to parse rule [%s] [%d]: %s\n",
+ dbus_rule, ret, sss_strerror(ret));
+ }
+
+
+ return ret;
+}
+
+errno_t
+sbus_server_add_match(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *dbus_rule)
+{
+ struct sbus_rule *sbus_rule;
+ errno_t ret;
+
+ ret = sbus_match_rule_parse(NULL, dbus_rule, &sbus_rule);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_match_rule_add(server, conn, sbus_rule);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to add rule [%s] [%d]: %s\n",
+ dbus_rule, ret, sss_strerror(ret));
+ }
+
+ talloc_free(sbus_rule);
+ return ret;
+}
+
+errno_t
+sbus_server_remove_match(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *dbus_rule)
+{
+ struct sbus_rule *sbus_rule;
+ errno_t ret;
+
+ ret = sbus_match_rule_parse(NULL, dbus_rule, &sbus_rule);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sbus_match_rule_remove(server, conn, sbus_rule);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to remove rule [%s] [%d]: %s\n",
+ dbus_rule, ret, sss_strerror(ret));
+ }
+
+ talloc_free(sbus_rule);
+ return ret;
+}
+
+static bool
+sbus_server_connection_has_name(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *name)
+{
+ struct sbus_connection *named_conn;
+
+ named_conn = sss_ptr_hash_lookup(server->names, name,
+ struct sbus_connection);
+
+ if (named_conn == NULL || named_conn != conn) {
+ return false;
+ }
+
+ return true;
+}
+
+errno_t
+sbus_server_matchmaker(struct sbus_server *server,
+ struct sbus_connection *conn,
+ const char *avoid_name,
+ DBusMessage *message)
+{
+ struct sss_ptr_list *list;
+ struct sbus_connection *match_conn;
+ bool has_name;
+
+ /* We can't really send signals when the server is being destroyed. */
+ if (server == NULL || server->disconnecting) {
+ return EOK;
+ }
+
+ list = sbus_match_rule_find(server,
+ dbus_message_get_interface(message),
+ dbus_message_get_member(message));
+ if (list == NULL) {
+ /* No connection listens for this signal. */
+ return EOK;
+ }
+
+ SSS_PTR_LIST_FOR_EACH(list, match_conn, struct sbus_connection) {
+ /* Do not send signal back to the sender. */
+ if (match_conn == conn) {
+ continue;
+ }
+
+ /* Sometimes (e.g. when a name is being deleted), we do not want to
+ * send the signal to a specific name. */
+ if (avoid_name != NULL) {
+ has_name = sbus_server_connection_has_name(server, match_conn,
+ avoid_name);
+ if (has_name) {
+ continue;
+ }
+ }
+
+ dbus_connection_send(match_conn->connection, message, NULL);
+ }
+
+ return EOK;
+}
diff --git a/src/sbus/sync/sbus_sync.c b/src/sbus/sync/sbus_sync.c
new file mode 100644
index 0000000..55fcf6e
--- /dev/null
+++ b/src/sbus/sync/sbus_sync.c
@@ -0,0 +1,183 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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/>.
+*/
+
+#include <errno.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/connection/sbus_dbus_private.h"
+#include "sbus/sbus_errors.h"
+#include "sbus/sbus_message.h"
+#include "sbus/sbus_sync.h"
+
+struct sbus_sync_connection {
+ DBusConnection *connection;
+ bool disconnecting;
+};
+
+static int
+sbus_sync_connection_destructor(struct sbus_sync_connection *sbus_conn)
+{
+ sbus_conn->disconnecting = true;
+ dbus_connection_unref(sbus_conn->connection);
+
+ return 0;
+}
+
+static struct sbus_sync_connection *
+sbus_sync_connection_init(TALLOC_CTX *mem_ctx,
+ DBusConnection *dbus_conn)
+{
+ struct sbus_sync_connection *sbus_conn;
+
+ sbus_conn = talloc_zero(mem_ctx, struct sbus_sync_connection);
+ if (sbus_conn == NULL) {
+ return NULL;
+ }
+
+ sbus_conn->connection = dbus_connection_ref(dbus_conn);
+
+ talloc_set_destructor(sbus_conn, sbus_sync_connection_destructor);
+
+ return sbus_conn;
+}
+
+struct sbus_sync_connection *
+sbus_sync_connect_system(TALLOC_CTX *mem_ctx,
+ const char *dbus_name)
+{
+ struct sbus_sync_connection *sbus_conn;
+ DBusConnection *dbus_conn;
+
+ dbus_conn = sbus_dbus_connect_bus(DBUS_BUS_SYSTEM, dbus_name);
+ if (dbus_conn == NULL) {
+ return NULL;
+ }
+
+ sbus_conn = sbus_sync_connection_init(mem_ctx, dbus_conn);
+ dbus_connection_unref(dbus_conn);
+ if (sbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create connection context!\n");
+ return NULL;
+ }
+
+ return sbus_conn;
+}
+
+struct sbus_sync_connection *
+sbus_sync_connect_private(TALLOC_CTX *mem_ctx,
+ const char *address,
+ const char *dbus_name)
+{
+ struct sbus_sync_connection *sbus_conn;
+ DBusConnection *dbus_conn;
+
+ dbus_conn = sbus_dbus_connect_address(address, dbus_name, true);
+ if (dbus_conn == NULL) {
+ return NULL;
+ }
+
+ sbus_conn = sbus_sync_connection_init(mem_ctx, dbus_conn);
+ dbus_connection_unref(dbus_conn);
+ if (sbus_conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create connection context!\n");
+ return NULL;
+ }
+
+ return sbus_conn;
+}
+
+errno_t
+sbus_sync_message_send(TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ DBusMessage *msg,
+ int timeout_ms,
+ DBusMessage **_reply)
+{
+ DBusError dbus_error;
+ DBusMessage *reply;
+ errno_t ret;
+
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: message is empty!\n");
+ return EINVAL;
+ }
+
+ if (conn->disconnecting) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Connection is being disconnected\n");
+ return ERR_TERMINATED;
+ }
+
+ if (_reply == NULL) {
+ dbus_connection_send(conn->connection, msg, NULL);
+ dbus_connection_flush(conn->connection);
+ return EOK;
+ }
+
+ dbus_error_init(&dbus_error);
+ reply = dbus_connection_send_with_reply_and_block(conn->connection, msg,
+ timeout_ms, &dbus_error);
+ if (dbus_error_is_set(&dbus_error)) {
+ ret = sbus_error_to_errno(&dbus_error);
+ goto done;
+ } else if (reply == NULL) {
+ ret = ERR_SBUS_NO_REPLY;
+ goto done;
+ }
+
+ ret = sbus_reply_check(reply);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (mem_ctx != NULL) {
+ ret = sbus_message_bound(mem_ctx, reply);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ *_reply = reply;
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Error received [%d]: %s!\n",
+ ret, sss_strerror(ret));
+ }
+
+ dbus_error_free(&dbus_error);
+
+ return ret;
+}
+
+void sbus_sync_emit_signal(struct sbus_sync_connection *conn,
+ DBusMessage *msg)
+{
+ errno_t ret;
+
+ ret = sbus_sync_message_send(NULL, conn, msg, SBUS_MESSAGE_TIMEOUT, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to emit signal [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+}
diff --git a/src/sbus/sync/sbus_sync_call.c b/src/sbus/sync/sbus_sync_call.c
new file mode 100644
index 0000000..a4f8a5c
--- /dev/null
+++ b/src/sbus/sync/sbus_sync_call.c
@@ -0,0 +1,101 @@
+/*
+ Authors:
+ Pavel Březina <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/>.
+*/
+
+#include <errno.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_sync.h"
+#include "sbus/sbus_sync_private.h"
+#include "sbus/sbus_message.h"
+
+errno_t
+sbus_sync_call_method(TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ void *input,
+ DBusMessage **_reply)
+{
+ TALLOC_CTX *tmp_ctx;
+ DBusMessage *reply;
+ DBusMessage *msg;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ msg = sbus_create_method_call(tmp_ctx, raw_message, writer, bus, path,
+ iface, method, input);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_sync_message_send(tmp_ctx, conn, msg, SBUS_MESSAGE_TIMEOUT,
+ &reply);
+ if (ret != EOK) {
+ 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;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+void
+sbus_sync_call_signal(struct sbus_sync_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *path,
+ const char *iface,
+ const char *signal_name,
+ void *input)
+{
+ DBusMessage *msg;
+
+ msg = sbus_create_signal_call(NULL, raw_message, writer, path, iface,
+ signal_name, input);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create signal message!\n");
+ return;
+ }
+
+ sbus_sync_emit_signal(conn, msg);
+}