diff options
Diffstat (limited to 'src/sbus')
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); +} |