From ab74a86a680b6a0fcfb34f1a4e5234300d285f76 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Tue, 20 Aug 2013 11:30:32 +0300 Subject: [PATCH] common/dbus: systemd-bus based murphy-dbus implementation. Added an alternative murphy-dbus implementation based on the low-level D-Bus library (systemd-bus) from systemd. This needs a patched systemd to build and install systemd-bus as a shared library. To get rid of libdbus-dependency it was also necessary to provide an abstraction for message building and parsing and hence break backward compatibility. To ease the transitional pain, we provide currently three version of murphy-dbus: the original one with the leaky message abstraction, as well as a libdbus- and a systmed-bus- based one with the new abstraction. Once all the code has been updated to not use directly libdbus calls for message building and parsing, the original library will be removed. --- configure.ac | 66 +- src/Makefile.am | 233 ++- src/common/dbus-error.h | 82 + src/common/{dbus-glue.c => dbus-libdbus-glue.c} | 0 src/common/dbus-libdbus-transport.c | 1740 +++++++++++++++++ src/common/dbus-libdbus.c | 2054 ++++++++++++++++++++ src/common/dbus-libdbus.h | 362 ++++ src/common/dbus-sdbus-glue.c | 140 ++ src/common/dbus-sdbus-transport.c | 1781 +++++++++++++++++ src/common/dbus-sdbus.c | 1955 +++++++++++++++++++ src/common/dbus-sdbus.h | 398 ++++ src/common/dbus-transport.c | 2 +- src/common/libdbus-glue.c | 374 ++++ src/common/{dbus.c => libdbus.c} | 2 +- src/common/{dbus.h => libdbus.h} | 0 src/common/murphy-dbus-libdbus.pc.in | 11 + src/common/murphy-dbus-sdbus.pc.in | 11 + .../{murphy-dbus.pc.in => murphy-libdbus.pc.in} | 4 +- src/common/tests/Makefile.am | 47 +- src/common/tests/dbus-sdbus-test.c | 563 ++++++ src/common/tests/dbus-test.c | 47 +- src/common/tests/libdbus-test.c | 482 +++++ src/common/tests/libdbus-transport-test.c | 847 ++++++++ src/common/tests/sdbus-test.c | 226 +++ src/plugins/plugin-resource-dbus.c | 2 +- 25 files changed, 11346 insertions(+), 83 deletions(-) create mode 100644 src/common/dbus-error.h rename src/common/{dbus-glue.c => dbus-libdbus-glue.c} (100%) create mode 100644 src/common/dbus-libdbus-transport.c create mode 100644 src/common/dbus-libdbus.c create mode 100644 src/common/dbus-libdbus.h create mode 100644 src/common/dbus-sdbus-glue.c create mode 100644 src/common/dbus-sdbus-transport.c create mode 100644 src/common/dbus-sdbus.c create mode 100644 src/common/dbus-sdbus.h create mode 100644 src/common/libdbus-glue.c rename src/common/{dbus.c => libdbus.c} (99%) rename src/common/{dbus.h => libdbus.h} (100%) create mode 100644 src/common/murphy-dbus-libdbus.pc.in create mode 100644 src/common/murphy-dbus-sdbus.pc.in rename src/common/{murphy-dbus.pc.in => murphy-libdbus.pc.in} (69%) create mode 100644 src/common/tests/dbus-sdbus-test.c create mode 100644 src/common/tests/libdbus-test.c create mode 100644 src/common/tests/libdbus-transport-test.c create mode 100644 src/common/tests/sdbus-test.c diff --git a/configure.ac b/configure.ac index f10716c..15e9cbc 100644 --- a/configure.ac +++ b/configure.ac @@ -124,31 +124,56 @@ AC_ARG_ENABLE(gpl, [ --enable-gpl enable linking against GPL code], [enable_gpl=$enableval], [enable_gpl=no]) -# Check if DBUS was enabled. -AC_ARG_ENABLE(dbus, - [ --enable-dbus enable D-BUS support], - [enable_dbus=$enableval], [enable_dbus=no]) +# Check if original libdbus-based DBUS support was enabled. +AC_ARG_ENABLE(libdbus, + [ --enable-libdbus enable libdbus-based D-BUS support], + [enable_libdbus=$enableval], [enable_libdbus=no]) -if test "$enable_dbus" = "yes"; then +if test "$enable_libdbus" = "yes"; then if test "$enable_gpl" = "no"; then - AC_MSG_ERROR([D-Bus support requires the --enable-gpl option.]) + AC_MSG_ERROR([libdbus D-Bus support requires the --enable-gpl option.]) fi - PKG_CHECK_MODULES(DBUS, dbus-1 >= 0.70) + PKG_CHECK_MODULES(LIBDBUS, dbus-1 >= 0.70) DBUS_SESSION_DIR="`pkg-config --variable session_bus_services_dir dbus-1`" AC_SUBST(DBUS_SESSION_DIR) + + AC_DEFINE([LIBDBUS_ENABLED], 1, [Enable libdbus D-Bus support ?]) else - AC_MSG_NOTICE([D-Bus support is disabled.]) + AC_MSG_NOTICE([libdbus-based D-Bus support is disabled.]) fi -if test "$enable_dbus" = "yes"; then - AC_DEFINE([DBUS_ENABLED], 1, [Enable D-BUS support ?]) +AM_CONDITIONAL(LIBDBUS_ENABLED, [test "$enable_libdbus" = "yes"]) +AC_SUBST(LIBDBUS_ENABLED) +AC_SUBST(LIBDBUS_CFLAGS) +AC_SUBST(LIBDBUS_LIBS) + +# Check if systemd-bus-based D-Bus support was enabled. +AC_ARG_ENABLE(sdbus, + [ --enable-sdbus enable systemd-based D-BUS support], + [enable_sdbus=$enableval], [enable_sdbus=no]) + +if test "$enable_sdbus" = "yes"; then + PKG_CHECK_MODULES(SDBUS, libsystemd-bus) + AC_DEFINE([SDBUS_ENABLED], 1, [Enable systemd-bus support ?]) + + if test -z "$DBUS_SESSION_DIR"; then + # Try to determine the session bus service directory. + DBUS_SESSION_DIR="`pkg-config --variable \ + session_bus_services_dir dbus-1`" + if test "$?" != "0" -o -z "$DBUS_SESSION_DIR"; then + DBUS_SESSION_DIR="/usr/share/dbus-1/services" + fi + AC_SUBST(DBUS_SESSION_DIR) + fi +else + AC_MSG_NOTICE([libsystemd-bus based D-Bus support is disabled.]) fi -AM_CONDITIONAL(DBUS_ENABLED, [test "$enable_dbus" = "yes"]) -AC_SUBST(DBUS_ENABLED) -AC_SUBST(DBUS_CFLAGS) -AC_SUBST(DBUS_LIBS) +AM_CONDITIONAL(SDBUS_ENABLED, [test "$enable_sdbus" = "yes"]) +AC_SUBST(SDBUS_ENABLED) +AC_SUBST(SDBUS_CFLAGS) +AC_SUBST(SDBUS_LIBS) # Check if PulseAudio mainloop support was enabled. AC_ARG_ENABLE(pulse, @@ -562,14 +587,16 @@ AC_CONFIG_FILES([build-aux/shave src/daemon/tests/Makefile src/plugins/tests/Makefile src/common/murphy-common.pc - src/common/murphy-dbus.pc + src/common/murphy-libdbus.pc + src/common/murphy-dbus-libdbus.pc + src/common/murphy-dbus-sdbus.pc src/common/murphy-pulse.pc src/common/murphy-ecore.pc src/common/murphy-glib.pc - src/common/murphy-qt.pc + src/common/murphy-qt.pc src/core/murphy-core.pc - src/core/lua-utils/murphy-lua-utils.pc - src/core/lua-decision/murphy-lua-decision.pc + src/core/lua-utils/murphy-lua-utils.pc + src/core/lua-decision/murphy-lua-decision.pc src/breedline/breedline.pc src/breedline/breedline-murphy.pc src/breedline/breedline-glib.pc @@ -595,7 +622,8 @@ AC_OUTPUT # Display the configuration. echo "----- configuration -----" echo "Extra C warnings flags: $WARNING_CFLAGS" -echo "D-Bus support: $enable_dbus" +echo "D-Bus (libdbus) support: $enable_libdbus" +echo "D-Bus (systemd-bus) support: $enable_sdbus" echo "PulseAudio mainloop support: $enable_pulse" echo "EFL/ecore mainloop support: $enable_ecore" echo "glib mainloop support: $enable_glib" diff --git a/src/Makefile.am b/src/Makefile.am index 5a68ba6..2822b45 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -364,64 +364,212 @@ clean-func-infos:: ################################### # murphy dbus library # +# Right now we have three variants of the murphy D-Bus library. The +# original with leaky libdbus abstraction, a revised one that plugs +# the most gaping abstraction holes, and one which uses systemd-bus, +# the licensing-wise much easier/liberal D-Bus implementation from +# systemd. +# +# All original code uses/used to use the libdbus-based one with leaky +# abstraction. This would make it very difficult to move away from +# the license-troubled libdbus even if/when a viable alternative (eg. +# from the kdbus effort) became available. To get rid of this problem, +# we need to get rid of the direct dependency on libdbus. Once that is +# done, we can (hopefully) switch easily and transparently between +# D-Bus libraries, if needed by touching only one component, the +# murphy-dbus library. +# +# To recap these are the different library variants: +# +# 1) murphy-libdbus +# This is the original murphy-dbus library. It uses libdbus as the +# underlying D-Bus client library implementation. It does not provide +# a proper abstraction on top of libdbus. Messages, message building, +# and parsing used DBusMessage. +# +# 2) murphy-dbus-libdbus +# This is a modified version of murphy-libdbus which provides an +# additional abstraction for messages, message building and parsing. +# +# 3) murphy-dbus-systemdbus +# This library provides an identical API to murphy-dbus-libdbus but +# it uses the systemd dbus library implementation (systemd-bus) as +# the underlying D-Bus client library implementation. +# +# So all current code still using libdbus for message building/parsing +# needs to get rid of it and changed to use murphy-dbus-libdbus. +# -murphy_dbus_headers = \ - common/dbus.h \ - common/dbus-transport.h +# original libdbus-based library +murphy_libdbus_headers = \ + common/libdbus.h -if DBUS_ENABLED -lib_LTLIBRARIES += libmurphy-dbus.la -EXTRA_DIST += common/murphy-dbus.pc -pkgconfig_DATA += common/murphy-dbus.pc +if LIBDBUS_ENABLED +lib_LTLIBRARIES += libmurphy-libdbus.la +EXTRA_DIST += common/murphy-libdbus.pc +pkgconfig_DATA += common/murphy-libdbus.pc -libmurphy_dbus_ladir = \ +libmurphy_libdbus_ladir = \ $(includedir)/murphy/common -libmurphy_dbus_la_HEADERS = $(murphy_dbus_headers) +libmurphy_libdbus_la_HEADERS = $(murphy_libdbus_headers) + +libmurphy_libdbus_la_REGULAR_SOURCES = \ + common/libdbus.c \ + common/libdbus-glue.c + +libmurphy_libdbus_la_SOURCES = \ + $(libmurphy_libdbus_la_REGULAR_SOURCES) \ + libdbus-func-info.c + +libmurphy_libdbus_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBDBUS_CFLAGS) + +libmurphy_libdbus_la_LDFLAGS = \ + -Wl,-version-script=linker-script.libdbus \ + -version-info @MURPHY_VERSION_INFO@ + +libmurphy_libdbus_la_LIBADD = \ + -lrt $(LIBDBUS_LIBS) + +libmurphy_libdbus_la_DEPENDENCIES = linker-script.libdbus + +libmurphy_libdbusdir = $(includedir)/murphy/dbus +libmurphy_libdbus_HEADERS = $(libmurphy_libdbus_la_HEADERS) + +# debug file:line-function mapping generation +libdbus-func-info.c: $(libmurphy_libdbus_la_REGULAR_SOURCES) + $(QUIET_GEN)$(top_builddir)/build-aux/gen-debug-table -o $@ $^ + +clean-func-infos:: + -rm libdbus-func-info.c +endif + +# linker script generation +linker-script.libdbus: $(murphy_libdbus_headers) + $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \ + -c "$(libmurphy_libdbus_la_CFLAGS)" -o $@ $^ + +clean-linker-script:: + -rm -f linker-script.libdbus + +generate-linker-scripts: linker-script.libdbus + + +# libdbus-based library with full abstraction +murphy_dbus_libdbus_headers = \ + common/dbus-libdbus.h + +if LIBDBUS_ENABLED +lib_LTLIBRARIES += libmurphy-dbus-libdbus.la +EXTRA_DIST += common/murphy-dbus-libdbus.pc +pkgconfig_DATA += common/murphy-dbus-libdbus.pc + +libmurphy_dbus_libdbus_ladir = \ + $(includedir)/murphy/common + +libmurphy_dbus_libdbus_la_HEADERS = $(murphy_dbus_libdbus_headers) + +libmurphy_dbus_libdbus_la_REGULAR_SOURCES = \ + common/dbus-libdbus.c \ + common/dbus-libdbus-glue.c \ + common/dbus-libdbus-transport.c + +libmurphy_dbus_libdbus_la_SOURCES = \ + $(libmurphy_dbus_libdbus_la_REGULAR_SOURCES) \ + dbus-libdbus-func-info.c + +libmurphy_dbus_libdbus_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBDBUS_CFLAGS) + +libmurphy_dbus_libdbus_la_LDFLAGS = \ + -Wl,-version-script=linker-script.dbus-libdbus \ + -version-info @MURPHY_VERSION_INFO@ + +libmurphy_dbus_libdbus_la_LIBADD = \ + -lrt $(LIBDBUS_LIBS) + +libmurphy_dbus_libdbus_la_DEPENDENCIES = linker-script.dbus-libdbus + +libmurphy_dbus_libdbusdir = $(includedir)/murphy/dbus +libmurphy_dbus_libdbus_HEADERS = $(libmurphy_dbus_libdbus_la_HEADERS) + +# debug file:line-function mapping generation +dbus-libdbus-func-info.c: $(libmurphy_dbus_libdbus_la_REGULAR_SOURCES) + $(QUIET_GEN)$(top_builddir)/build-aux/gen-debug-table -o $@ $^ + +clean-func-infos:: + -rm dbus-libdbus-func-info.c +endif + +# linker script generation +linker-script.dbus-libdbus: $(murphy_dbus_libdbus_headers) + $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \ + -c "$(libmurphy_dbus_libdbus_la_CFLAGS)" -o $@ $^ + +clean-linker-script:: + -rm -f linker-script.dbus-libdbus + +generate-linker-scripts: linker-script.dbus-libdbus + +# systemd-bus based library +murphy_dbus_sdbus_headers = \ + common/dbus-sdbus.h + +if SDBUS_ENABLED +lib_LTLIBRARIES += libmurphy-dbus-sdbus.la +EXTRA_DIST += common/murphy-dbus-sdbus.pc +pkgconfig_DATA += common/murphy-dbus-sdbus.pc + +libmurphy_dbus_sdbus_ladir = \ + $(includedir)/murphy/common +libmurphy_dbus_sdbus_la_HEADERS = $(murphy_dbus_sdbus_headers) -libmurphy_dbus_la_REGULAR_SOURCES = \ - common/dbus.c \ - common/dbus-glue.c \ - common/dbus-transport.c +libmurphy_dbus_sdbus_la_REGULAR_SOURCES = \ + common/dbus-sdbus.c \ + common/dbus-sdbus-glue.c \ + common/dbus-sdbus-transport.c -libmurphy_dbus_la_SOURCES = \ - $(libmurphy_dbus_la_REGULAR_SOURCES) \ - dbus-func-info.c +libmurphy_dbus_sdbus_la_SOURCES = \ + $(libmurphy_dbus_sdbus_la_REGULAR_SOURCES) \ + dbus-sdbus-func-info.c -libmurphy_dbus_la_CFLAGS = \ +libmurphy_dbus_sdbus_la_CFLAGS = \ $(AM_CFLAGS) \ - $(DBUS_CFLAGS) + $(SDBUS_CFLAGS) -libmurphy_dbus_la_LDFLAGS = \ - -Wl,-version-script=linker-script.dbus \ +libmurphy_dbus_sdbus_la_LDFLAGS = \ + -Wl,-version-script=linker-script.dbus-sdbus \ -version-info @MURPHY_VERSION_INFO@ -libmurphy_dbus_la_LIBADD = \ - -lrt $(DBUS_LIBS) +libmurphy_dbus_sdbus_la_LIBADD = \ + -lrt $(SDBUS_LIBS) -libmurphy_dbus_la_DEPENDENCIES = linker-script.dbus +libmurphy_dbus_sdbus_la_DEPENDENCIES = linker-script.dbus-sdbus -libdbusincludedir = $(includedir)/murphy/dbus -libdbusinclude_HEADERS = $(libmurphy_dbus_la_HEADERS) +libmurphy_dbus_sdbusdir = $(includedir)/murphy/dbus +libmurphy_dbus_sdbus_HEADERS = $(libmurphy_dbus_sdbus_la_HEADERS) # debug file:line-function mapping generation -dbus-func-info.c: $(libmurphy_dbus_la_REGULAR_SOURCES) +dbus-sdbus-func-info.c: $(libmurphy_dbus_sdbus_la_REGULAR_SOURCES) $(QUIET_GEN)$(top_builddir)/build-aux/gen-debug-table -o $@ $^ clean-func-infos:: - -rm dbus-func-info.c + -rm dbus-sdbus-func-info.c endif # linker script generation -linker-script.dbus: $(murphy_dbus_headers) +linker-script.dbus-sdbus: $(murphy_dbus_sdbus_headers) $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \ - -c "$(libmurphy_dbus_la_CFLAGS)" -o $@ $^ + -c "$(libmurphy_dbus_sdbus_la_CFLAGS)" -o $@ $^ clean-linker-script:: - -rm -f linker-script.dbus + -rm -f linker-script.sd-bus -generate-linker-scripts: linker-script.dbus +generate-linker-scripts: linker-script.sd-bus ################################### @@ -1005,10 +1153,10 @@ endif endif # dbus plugin -if DBUS_ENABLED +if LIBDBUS_ENABLED DBUS_PLUGIN_SOURCES = plugins/plugin-dbus.c -DBUS_PLUGIN_CFLAGS = $(DBUS_CFLAGS) -DBUS_PLUGIN_LIBS = libmurphy-dbus.la $(DBUS_LIBS) +DBUS_PLUGIN_CFLAGS = $(LIBDBUS_CFLAGS) +DBUS_PLUGIN_LIBS = libmurphy-dbus-libdbus.la $(LIBDBUS_LIBS) if !DISABLED_PLUGIN_DBUS if BUILTIN_PLUGIN_DBUS @@ -1050,13 +1198,14 @@ endif # resource-dbus plugin if BUILD_RESOURCES -if DBUS_ENABLED +if LIBDBUS_ENABLED RESOURCE_DBUS_PLUGIN_SOURCES = plugins/plugin-resource-dbus.c -RESOURCE_DBUS_PLUGIN_CFLAGS = $(DBUS_CFLAGS) -RESOURCE_DBUS_PLUGIN_LIBS = libmurphy-dbus.la \ - libmurphy-core.la \ - libmurphy-common.la \ - $(RESOURCE_LIBRARY) +RESOURCE_DBUS_PLUGIN_CFLAGS = $(LIBDBUS_CFLAGS) +RESOURCE_DBUS_PLUGIN_LIBS = \ + libmurphy-libdbus.la \ + libmurphy-core.la \ + libmurphy-common.la \ + $(RESOURCE_LIBRARY) if !DISABLED_PLUGIN_RESOURCE_DBUS if BUILTIN_PLUGIN_RESOURCE_DBUS @@ -1401,8 +1550,8 @@ murphy_console_LDADD = \ libbreedline.la \ libmurphy-common.la -if DBUS_ENABLED -murphy_console_LDADD += libmurphy-dbus.la +if LIBDBUS_ENABLED +murphy_console_LDADD += libmurphy-dbus-libdbus.la endif murphy_console_LDFLAGS = -rdynamic diff --git a/src/common/dbus-error.h b/src/common/dbus-error.h new file mode 100644 index 0000000..1704cec --- /dev/null +++ b/src/common/dbus-error.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2012, 2013, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MURPHY_DBUS_ERROR_H__ +#define __MURPHY_DBUS_ERROR_H__ + +#define __ERR(error) "org.freedesktop.DBus.Error."#error + +#define MRP_DBUS_ERROR_FAILED __ERR(Failed) +#define MRP_DBUS_ERROR_NO_MEMORY __ERR(NoMemory) +#define MRP_DBUS_ERROR_SERVICE_UNKNOWN __ERR(ServiceUnknown) +#define MRP_DBUS_ERROR_NAME_HAS_NO_OWNER __ERR(NameHasNoOwner) +#define MRP_DBUS_ERROR_NO_REPLY __ERR(NoReply) +#define MRP_DBUS_ERROR_IO_ERROR __ERR(IOError) +#define MRP_DBUS_ERROR_BAD_ADDRESS __ERR(BadAddress) +#define MRP_DBUS_ERROR_NOT_SUPPORTED __ERR(NotSupported) +#define MRP_DBUS_ERROR_LIMITS_EXCEEDED __ERR(LimitsExceeded) +#define MRP_DBUS_ERROR_ACCESS_DENIED __ERR(AccessDenied) +#define MRP_DBUS_ERROR_AUTH_FAILED __ERR(AuthFailed) +#define MRP_DBUS_ERROR_NO_SERVER __ERR(NoServer) +#define MRP_DBUS_ERROR_TIMEOUT __ERR(Timeout) +#define MRP_DBUS_ERROR_NO_NETWORK __ERR(NoNetwork) +#define MRP_DBUS_ERROR_ADDRESS_IN_USE __ERR(AddressInUse) +#define MRP_DBUS_ERROR_DISCONNECTED __ERR(Disconnected) +#define MRP_DBUS_ERROR_INVALID_ARGS __ERR(InvalidArgs) +#define MRP_DBUS_ERROR_FILE_NOT_FOUND __ERR(FileNotFound) +#define MRP_DBUS_ERROR_FILE_EXISTS __ERR(FileExists) +#define MRP_DBUS_ERROR_UNKNOWN_METHOD __ERR(UnknownMethod) +#define MRP_DBUS_ERROR_UNKNOWN_OBJECT __ERR(UnknownObject) +#define MRP_DBUS_ERROR_UNKNOWN_INTERFACE __ERR(UnknownInterface) +#define MRP_DBUS_ERROR_UNKNOWN_PROPERTY __ERR(UnknownProperty) +#define MRP_DBUS_ERROR_PROPERTY_READ_ONLY __ERR(PropertyReadOnly) +#define MRP_DBUS_ERROR_TIMED_OUT __ERR(TimedOut) +#define MRP_DBUS_ERROR_MATCH_RULE_NOT_FOUND __ERR(MatchRuleNotFound) +#define MRP_DBUS_ERROR_MATCH_RULE_INVALID __ERR(MatchRuleInvalid) +#define MRP_DBUS_ERROR_SPAWN_EXEC_FAILED __ERR(Spawn.ExecFailed) +#define MRP_DBUS_ERROR_SPAWN_FORK_FAILED __ERR(Spawn.ForkFailed) +#define MRP_DBUS_ERROR_SPAWN_CHILD_EXITED __ERR(Spawn.ChildExited) +#define MRP_DBUS_ERROR_SPAWN_CHILD_SIGNALED __ERR(Spawn.ChildSignaled) +#define MRP_DBUS_ERROR_SPAWN_FAILED __ERR(Spawn.Failed) +#define MRP_DBUS_ERROR_SPAWN_SETUP_FAILED __ERR(Spawn.FailedToSetup) +#define MRP_DBUS_ERROR_SPAWN_CONFIG_INVALID __ERR(Spawn.ConfigInvalid) +#define MRP_DBUS_ERROR_SPAWN_SERVICE_INVALID __ERR(Spawn.ServiceNotValid) +#define MRP_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND __ERR(Spawn.ServiceNotFound) +#define MRP_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID __ERR(Spawn.PermissionsInvalid) +#define MRP_DBUS_ERROR_SPAWN_FILE_INVALID __ERR(Spawn.FileInvalid) +#define MRP_DBUS_ERROR_SPAWN_NO_MEMORY __ERR(Spawn.NoMemory) +#define MRP_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN __ERR(UnixProcessIdUnknown) +#define MRP_DBUS_ERROR_INVALID_SIGNATURE __ERR(InvalidSignature) +#define MRP_DBUS_ERROR_INVALID_FILE_CONTENT __ERR(InvalidFileContent) +#define MRP_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN __ERR(AdtAuditDataUnknown) +#define MRP_DBUS_ERROR_OBJECT_PATH_IN_USE __ERR(ObjectPathInUse) +#define MRP_DBUS_ERROR_INCONSISTENT_MESSAGE __ERR(InconsistentMessage) +#define MRP_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN __ERR(SELinuxSecurityContextUnknown) + +#endif /* __MURPHY_MRP_DBUS_ERROR_H__ */ diff --git a/src/common/dbus-glue.c b/src/common/dbus-libdbus-glue.c similarity index 100% rename from src/common/dbus-glue.c rename to src/common/dbus-libdbus-glue.c diff --git a/src/common/dbus-libdbus-transport.c b/src/common/dbus-libdbus-transport.c new file mode 100644 index 0000000..5b7c0c0 --- /dev/null +++ b/src/common/dbus-libdbus-transport.c @@ -0,0 +1,1740 @@ +/* + * Copyright (c) 2012, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DBUS "dbus" +#define DBUSL 4 + +#define TRANSPORT_PATH "/murphy/transport" +#define TRANSPORT_INTERFACE "Murphy.Transport" +#define TRANSPORT_MESSAGE "DeliverMessage" +#define TRANSPORT_DATA "DeliverData" +#define TRANSPORT_RAW "DeliverRaw" +#define TRANSPORT_METHOD "DeliverMessage" + +#define ANY_ADDRESS "any" + +typedef struct { + MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */ + mrp_dbus_t *dbus; /* D-BUS connection */ + int bound : 1; /* whether bound to an address */ + int peer_resolved : 1; /* connected and peer name known */ + mrp_dbusaddr_t local; /* address we're bound to */ + mrp_dbusaddr_t remote; /* address we're connected to */ +} dbus_t; + + +static uint32_t nauto; /* for autobinding */ + + +static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data); +static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data); +static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data); + +static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up, + const char *owner, void *user_data); + +static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + mrp_msg_t *msg); +static mrp_msg_t *msg_decode(mrp_dbus_msg_t *msg, const char **sender_id); + +static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + void *data, uint16_t tag); +static void *data_decode(mrp_dbus_msg_t *msg, uint16_t *tag, + const char **sender_id); + +static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + void *data, size_t size); +static void *raw_decode(mrp_dbus_msg_t *msg, size_t *sizep, + const char **sender_id); + + +static socklen_t parse_address(const char *str, mrp_dbusaddr_t *addr, + socklen_t size) +{ + const char *p, *e; + char *q; + size_t l, n; + + if (size < sizeof(*addr)) { + errno = EINVAL; + return FALSE; + } + + if (strncmp(str, DBUS":", DBUSL + 1)) + return 0; + else + str += DBUSL + 1; + + /* + * The format of the address is + * dbus:[bus-address]@address/path + * eg. + * dbus:[session]@:1.33/client1, or + * dbus:[unix:abstract=/tmp/dbus-Xx2Kpi...a572]@:1.33/client1 + */ + + p = str; + q = addr->db_fqa; + l = sizeof(addr->db_fqa); + + /* get bus address */ + if (*p != '[') { + errno = EINVAL; + return 0; + } + else + p++; + + e = strchr(p, ']'); + + if (e == NULL) { + errno = EINVAL; + return 0; + } + + n = e - p; + if (n >= l) { + errno = ENAMETOOLONG; + return 0; + } + + /* save bus address */ + strncpy(q, p, n); + q[n] = '\0'; + addr->db_bus = q; + + q += n + 1; + l -= n + 1; + p = e + 1; + + /* get (local or remote) address on bus */ + if (*p != '@') + addr->db_addr = ANY_ADDRESS; + else { + p++; + e = strchr(p, '/'); + + if (e == NULL) { + errno = EINVAL; + return 0; + } + + n = e - p; + if (n >= l) { + errno = ENAMETOOLONG; + return 0; + } + + /* save address on bus */ + strncpy(q, p, n); + q[n] = '\0'; + addr->db_addr = q; + + q += n + 1; + l -= n + 1; + p = e; + } + + /* get object path */ + if (*p != '/') { + errno = EINVAL; + return 0; + } + + n = snprintf(q, l, "%s%s", TRANSPORT_PATH, p); + if (n >= l) { + errno = ENAMETOOLONG; + return 0; + } + + addr->db_path = q; + addr->db_family = MRP_AF_DBUS; + + return sizeof(*addr); +} + + +static mrp_dbusaddr_t *copy_address(mrp_dbusaddr_t *dst, mrp_dbusaddr_t *src) +{ + char *p, *q; + size_t l, n; + + dst->db_family = src->db_family; + + /* copy bus address */ + p = src->db_bus; + q = dst->db_fqa; + l = sizeof(dst->db_fqa); + + n = strlen(p); + if (l < n + 1) { + errno = EOVERFLOW; + return NULL; + } + + dst->db_bus = q; + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + /* copy address */ + p = src->db_addr; + n = strlen(p); + if (l < n + 1) { + errno = EOVERFLOW; + return NULL; + } + + dst->db_addr = q; + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + /* copy path */ + p = src->db_path; + n = strlen(p); + if (l < n + 1) { + errno = EOVERFLOW; + return NULL; + } + + dst->db_path = q; + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + return dst; +} + + +static inline int check_address(mrp_sockaddr_t *addr, socklen_t addrlen) +{ + mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addr; + + return (a->db_family = MRP_AF_DBUS && addrlen == sizeof(*a)); +} + + +static size_t peer_address(mrp_sockaddr_t *addrp, const char *sender, + const char *path) +{ + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + const char *p; + char *q; + int l, n; + + q = addr->db_fqa; + l = sizeof(addr->db_fqa); + p = ANY_ADDRESS; + n = 3; + + addr->db_bus = q; + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + addr->db_addr = q; + p = sender; + n = strlen(sender); + + if (l < n + 1) + return 0; + + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + addr->db_path = q; + p = path; + n = strlen(p); + + if (l < n + 1) + return 0; + + memcpy(q, p, n + 1); + + return sizeof(addrp); +} + + +static socklen_t dbus_resolve(const char *str, mrp_sockaddr_t *addr, + socklen_t size, const char **typep) +{ + socklen_t len; + + len = parse_address(str, (mrp_dbusaddr_t *)addr, size); + + if (len > 0) { + if (typep != NULL) + *typep = DBUS; + } + + return len; +} + + +static int dbus_open(mrp_transport_t *mt) +{ + MRP_UNUSED(mt); + + return TRUE; +} + + +static int dbus_createfrom(mrp_transport_t *mt, void *conn) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbus_t *dbus = (mrp_dbus_t *)conn; + + t->dbus = mrp_dbus_ref(dbus); + + if (t->dbus != NULL) + return TRUE; + else + return FALSE; +} + + +static int dbus_bind(mrp_transport_t *mt, mrp_sockaddr_t *addrp, + socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbus_t *dbus = NULL; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *); + const char *method; + + if (t->bound) { + errno = EINVAL; + goto fail; + } + + if (!check_address(addrp, addrlen)) { + errno = EINVAL; + goto fail; + } + + if (t->dbus == NULL) { + dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL); + + if (dbus == NULL) { + errno = ECONNRESET; + goto fail; + } + else { + t->dbus = dbus; + + if (addr->db_addr != NULL && strcmp(addr->db_addr, ANY_ADDRESS)) { + if (!mrp_dbus_acquire_name(t->dbus, addr->db_addr, NULL)) { + errno = EADDRINUSE; /* XXX TODO, should check error... */ + goto fail; + } + } + } + } + else { + /* XXX TODO: should check given address against address of the bus */ + } + + copy_address(&t->local, addr); + + switch (t->mode) { + case MRP_TRANSPORT_MODE_DATA: + method = TRANSPORT_DATA; + cb = dbus_data_cb; + break; + case MRP_TRANSPORT_MODE_RAW: + method = TRANSPORT_RAW; + cb = dbus_raw_cb; + break; + case MRP_TRANSPORT_MODE_MSG: + method = TRANSPORT_MESSAGE; + cb = dbus_msg_cb; + break; + default: + errno = EPROTOTYPE; + goto fail; + } + + if (!mrp_dbus_export_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE, + method, cb, t)) { + errno = EIO; + goto fail; + } + else { + t->bound = TRUE; + return TRUE; + } + + fail: + if (dbus != NULL) { + mrp_dbus_unref(dbus); + t->dbus = NULL; + } + + return FALSE; +} + + +static int dbus_autobind(mrp_transport_t *mt, mrp_sockaddr_t *addrp) +{ + mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addrp; + char astr[MRP_SOCKADDR_SIZE]; + mrp_sockaddr_t addr; + socklen_t alen; + + snprintf(astr, sizeof(astr), "dbus:[%s]/auto/%u", a->db_bus, nauto++); + + alen = dbus_resolve(astr, &addr, sizeof(addr), NULL); + + if (alen > 0) + return dbus_bind(mt, &addr, alen); + else + return FALSE; +} + + +static void dbus_close(mrp_transport_t *mt) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr; + const char *method; + int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *); + + if (t->bound) { + switch (t->mode) { + case MRP_TRANSPORT_MODE_DATA: + method = TRANSPORT_DATA; + cb = dbus_data_cb; + break; + case MRP_TRANSPORT_MODE_RAW: + method = TRANSPORT_RAW; + cb = dbus_raw_cb; + break; + default: + case MRP_TRANSPORT_MODE_MSG: + method = TRANSPORT_MESSAGE; + cb = dbus_msg_cb; + } + + addr = (mrp_dbusaddr_t *)&t->local; + mrp_dbus_remove_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE, + method, cb, t); + } + + if (t->connected && t->remote.db_addr != NULL) + mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t); + + mrp_dbus_unref(t->dbus); + t->dbus = NULL; +} + + +static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data) +{ + mrp_transport_t *mt = (mrp_transport_t *)user_data; + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t addr; + socklen_t alen; + const char *sender, *sender_path; + mrp_msg_t *msg; + + MRP_UNUSED(dbus); + + msg = msg_decode(dmsg, &sender_path); + + if (msg != NULL) { + sender = mrp_dbus_msg_sender(dmsg); + + if (mt->connected) { + if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender)) + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvmsg(mt, msg, mt->user_data); + }); + } + else { + peer_address(&addr, sender, sender_path); + alen = sizeof(addr); + + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvmsgfrom(mt, msg, &addr, alen, mt->user_data); + }); + } + + mrp_msg_unref(msg); + + mt->check_destroy(mt); + } + else { + mrp_log_error("Failed to decode message."); + } + + return TRUE; +} + + +static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data) +{ + mrp_transport_t *mt = (mrp_transport_t *)user_data; + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t addr; + socklen_t alen; + const char *sender, *sender_path; + uint16_t tag; + void *decoded; + + MRP_UNUSED(dbus); + + decoded = data_decode(dmsg, &tag, &sender_path); + + if (decoded != NULL) { + sender = mrp_dbus_msg_sender(dmsg); + + if (mt->connected) { + if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender)) + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvdata(mt, decoded, tag, mt->user_data); + }); + } + else { + peer_address(&addr, sender, sender_path); + alen = sizeof(addr); + + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen, + mt->user_data); + }); + } + + mt->check_destroy(mt); + } + else { + mrp_log_error("Failed to decode custom data message."); + } + + return TRUE; +} + + +static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data) +{ + mrp_transport_t *mt = (mrp_transport_t *)user_data; + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t addr; + socklen_t alen; + const char *sender, *sender_path; + void *data; + size_t size; + + MRP_UNUSED(dbus); + + data = raw_decode(dmsg, &size, &sender_path); + + if (data != NULL) { + sender = mrp_dbus_msg_sender(dmsg); + + if (mt->connected) { + if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender)) + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvraw(mt, data, size, mt->user_data); + }); + } + else { + peer_address(&addr, sender, sender_path); + alen = sizeof(addr); + + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvrawfrom(mt, data, size, &addr, alen, + mt->user_data); + }); + } + + mt->check_destroy(mt); + } + else { + mrp_log_error("Failed to decode raw message."); + } + + return TRUE; +} + + +static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up, + const char *owner, void *user_data) +{ + dbus_t *t = (dbus_t *)user_data; + mrp_sockaddr_t addr; + + MRP_UNUSED(dbus); + MRP_UNUSED(name); + + if (up) { + peer_address(&addr, owner, t->remote.db_path); + copy_address(&t->remote, (mrp_dbusaddr_t *)&addr); + t->peer_resolved = TRUE; + } + else { + /* + * XXX TODO: + * It would be really tempting here to call + * mt->evt.closed(mt, ECONNRESET, mt->user_data) + * to notify the user about the fact our peer went down. + * However, that would not be in line with the other + * transports which call the closed event handler only + * upon foricble transport closes upon errors. + * + * The transport interface abstraction (especially the + * available set of events) anyway needs some eyeballing, + * so the right thing to do might be to define a new event + * for disconnection and call the handler for that here... + */ + } + +} + + +static int dbus_connect(mrp_transport_t *mt, mrp_sockaddr_t *addrp, + socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + + if (!check_address(addrp, addrlen)) { + errno = EINVAL; + return FALSE; + } + + if (t->dbus == NULL) { + t->dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL); + + if (t->dbus == NULL) { + errno = ECONNRESET; + return FALSE; + } + } + else { + /* XXX TODO: check given address against address of the bus */ + } + + if (!t->bound) + if (!dbus_autobind(mt, addrp)) + return FALSE; + + if (mrp_dbus_follow_name(t->dbus, addr->db_addr, peer_state_cb, t)) { + copy_address(&t->remote, addr); + + return TRUE; + } + else + return FALSE; +} + + +static int dbus_disconnect(mrp_transport_t *mt) +{ + dbus_t *t = (dbus_t *)mt; + + if (t->connected && t->remote.db_addr != NULL) { + mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t); + mrp_clear(&t->remote); + t->peer_resolved = FALSE; + } + + return TRUE; +} + + +static int dbus_sendmsgto(mrp_transport_t *mt, mrp_msg_t *msg, + mrp_sockaddr_t *addrp, socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + mrp_dbus_msg_t *m; + int success; + + if (check_address(addrp, addrlen)) { + if (t->dbus == NULL && !dbus_autobind(mt, addrp)) + return FALSE; + + m = msg_encode(t->dbus, addr->db_addr, addr->db_path, + TRANSPORT_INTERFACE, TRANSPORT_MESSAGE, + t->local.db_path, msg); + + if (m != NULL) { + if (mrp_dbus_send_msg(t->dbus, m)) + success = TRUE; + else { + errno = ECOMM; + success = FALSE; + } + + mrp_dbus_msg_unref(m); + } + else + success = FALSE; + } + else { + errno = EINVAL; + success = FALSE; + } + + return success; +} + + +static int dbus_sendmsg(mrp_transport_t *mt, mrp_msg_t *msg) +{ + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote; + socklen_t alen = sizeof(t->remote); + + return dbus_sendmsgto(mt, msg, addr, alen); +} + + +static int dbus_sendrawto(mrp_transport_t *mt, void *data, size_t size, + mrp_sockaddr_t *addrp, socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + mrp_dbus_msg_t *m; + int success; + + + MRP_UNUSED(mt); + MRP_UNUSED(data); + MRP_UNUSED(size); + MRP_UNUSED(addr); + MRP_UNUSED(addrlen); + + if (check_address(addrp, addrlen)) { + if (t->dbus == NULL && !dbus_autobind(mt, addrp)) + return FALSE; + + m = raw_encode(t->dbus, addr->db_addr, addr->db_path, + TRANSPORT_INTERFACE, TRANSPORT_RAW, + t->local.db_path, data, size); + + if (m != NULL) { + if (mrp_dbus_send_msg(t->dbus, m)) + success = TRUE; + else { + errno = ECOMM; + success = FALSE; + } + + mrp_dbus_msg_unref(m); + } + else + success = FALSE; + } + else { + errno = EINVAL; + success = FALSE; + } + + return success; +} + + +static int dbus_sendraw(mrp_transport_t *mt, void *data, size_t size) +{ + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote; + socklen_t alen = sizeof(t->remote); + + return dbus_sendrawto(mt, data, size, addr, alen); +} + + +static int dbus_senddatato(mrp_transport_t *mt, void *data, uint16_t tag, + mrp_sockaddr_t *addrp, socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + mrp_dbus_msg_t *m; + int success; + + if (check_address(addrp, addrlen)) { + if (t->dbus == NULL && !dbus_autobind(mt, addrp)) + return FALSE; + + m = data_encode(t->dbus, addr->db_addr, addr->db_path, + TRANSPORT_INTERFACE, TRANSPORT_DATA, + t->local.db_path, data, tag); + + if (m != NULL) { + if (mrp_dbus_send_msg(t->dbus, m)) + success = TRUE; + else { + errno = ECOMM; + success = FALSE; + } + + mrp_dbus_msg_unref(m); + } + else + success = FALSE; + } + else { + errno = EINVAL; + success = FALSE; + } + + return success; +} + + +static int dbus_senddata(mrp_transport_t *mt, void *data, uint16_t tag) +{ + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote; + socklen_t alen = sizeof(t->remote); + + return dbus_senddatato(mt, data, tag, addr, alen); +} + + +static const char *get_array_signature(uint16_t type) +{ +#define MAP(from, to) \ + case MRP_MSG_FIELD_##from: \ + return MRP_DBUS_TYPE_##to##_AS_STRING; + + switch (type) { + MAP(STRING, STRING); + MAP(BOOL , BOOLEAN); + MAP(UINT8 , UINT16); + MAP(SINT8 , INT16); + MAP(UINT16, UINT16); + MAP(SINT16, INT16); + MAP(UINT32, UINT32); + MAP(SINT32, INT32); + MAP(UINT64, UINT64); + MAP(SINT64, INT64); + MAP(DOUBLE, DOUBLE); + MAP(BLOB , BYTE); + default: + return NULL; + } +} + + +static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + mrp_msg_t *msg) +{ + /* + * Notes: There is a type mismatch between our and DBUS types for + * 8-bit integers (D-BUS does not have a signed 8-bit type) + * and boolean types (D-BUS has uint32_t booleans, C99 fails + * to specify the type and gcc uses a signed char). The + * QUIRKY versions of the macros take care of these mismatches. + */ + +#define BASIC_SIMPLE(_i, _mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = &(_val); \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + +#define BASIC_QUIRKY(_i, _mtype, _dtype, _mval, _dval) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + _dval = _mval; \ + vptr = &_dval; \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_SIMPLE(_i, _mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = &_val; \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + _dvar = _mvar; \ + vptr = &_dvar; \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + + mrp_dbus_msg_t *m; + mrp_list_hook_t *p, *n; + mrp_msg_field_t *f; + uint16_t base; + uint32_t asize, i; + const char *sig; + int type, len; + void *vptr; + dbus_bool_t bln; + uint16_t u16, blb; + int16_t s16; + + m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member); + + if (m == NULL) + return NULL; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender_id)) + goto fail; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &msg->nfield)) + goto fail; + + mrp_list_foreach(&msg->fields, p, n) { + f = mrp_list_entry(p, typeof(*f), hook); + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) || + !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type)) + goto fail; + + switch (f->type) { + BASIC_SIMPLE(m, STRING, STRING , f->str); + BASIC_QUIRKY(m, BOOL , BOOLEAN, f->bln, bln); + BASIC_QUIRKY(m, UINT8 , UINT16 , f->u8 , u16); + BASIC_QUIRKY(m, SINT8 , INT16 , f->s8 , s16); + BASIC_SIMPLE(m, UINT16, UINT16 , f->u16); + BASIC_SIMPLE(m, SINT16, INT16 , f->s16); + BASIC_SIMPLE(m, UINT32, UINT32 , f->u32); + BASIC_SIMPLE(m, SINT32, INT32 , f->s32); + BASIC_SIMPLE(m, UINT64, UINT64 , f->u64); + BASIC_SIMPLE(m, SINT64, INT64 , f->s64); + BASIC_SIMPLE(m, DOUBLE, DOUBLE , f->dbl); + + case MRP_MSG_FIELD_BLOB: + vptr = f->blb; + len = (int)f->size[0]; + sig = get_array_signature(f->type); + asize = len; + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, NULL)) + goto fail; + + for (i = 0; i < asize; i++) { + blb = ((uint8_t *)f->blb)[i]; + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &blb)) + goto fail; + } + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + break; + + default: + if (f->type & MRP_MSG_FIELD_ARRAY) { + base = f->type & ~(MRP_MSG_FIELD_ARRAY); + asize = f->size[0]; + sig = get_array_signature(base); + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + for (i = 0; i < asize; i++) { + switch (base) { + ARRAY_SIMPLE(m, STRING, STRING , f->astr[i]); + ARRAY_QUIRKY(m, BOOL , BOOLEAN, f->abln[i], bln); + ARRAY_QUIRKY(m, UINT8 , UINT16 , f->au8[i] , u16); + ARRAY_QUIRKY(m, SINT8 , INT16 , f->as8[i] , s16); + ARRAY_SIMPLE(m, UINT16, UINT16 , f->au16[i]); + ARRAY_SIMPLE(m, SINT16, INT16 , f->as16[i]); + ARRAY_SIMPLE(m, UINT32, UINT32 , f->au32[i]); + ARRAY_SIMPLE(m, SINT32, INT32 , f->as32[i]); + ARRAY_SIMPLE(m, UINT64, UINT64 , f->au64[i]); + ARRAY_SIMPLE(m, DOUBLE, DOUBLE , f->adbl[i]); + + case MRP_MSG_FIELD_BLOB: + goto fail; + + default: + goto fail; + } + } + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + } + else + goto fail; + } + } + + return m; + + fail: + if (m != NULL) + mrp_dbus_msg_unref(m); + + errno = ECOMM; + + return FALSE; + +#undef BASIC_SIMPLE +#undef BASIC_QUIRKY +#undef ARRAY_SIMPLE +#undef ARRAY_QUIRKY +} + + +static mrp_msg_t *msg_decode(mrp_dbus_msg_t *m, const char **sender_id) +{ +#define BASIC_SIMPLE(_i, _mtype, _dtype, _var) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \ + &(_var))) \ + goto fail; \ + \ + if (!mrp_msg_append(msg, tag, type, (_var))) \ + goto fail; \ + break + +#define BASIC_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \ + &(_dvar))) \ + goto fail; \ + \ + _mvar = _dvar; \ + if (!mrp_msg_append(msg, tag, type, (_mvar))) \ + goto fail; \ + break + +#define ARRAY_SIMPLE(_i, _mtype, _dtype, _var) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \ + &(_var))) \ + goto fail; \ + break + +#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \ + &(_dvar))) \ + goto fail; \ + \ + _mvar = _dvar; \ + break + +#define APPEND_ARRAY(_type, _var) \ + case MRP_MSG_FIELD_##_type: \ + if (!mrp_msg_append(msg, tag, \ + MRP_MSG_FIELD_ARRAY | \ + MRP_MSG_FIELD_##_type, \ + n, _var)) \ + goto fail; \ + break + + mrp_msg_t *msg; + mrp_msg_value_t v; + uint16_t u16; + int16_t s16; + uint32_t u32; + uint16_t nfield, tag, type, base, i; + uint32_t n, j; + int asize; + const char *sender, *sig; + + msg = NULL; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield)) + goto fail; + + msg = mrp_msg_create_empty(); + + if (msg == NULL) + goto fail; + + for (i = 0; i < nfield; i++) { + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type)) + goto fail; + + switch (type) { + BASIC_SIMPLE(m, STRING, STRING , v.str); + BASIC_QUIRKY(m, BOOL , BOOLEAN, v.bln, u32); + BASIC_QUIRKY(m, UINT8 , UINT16 , v.u8 , u16); + BASIC_QUIRKY(m, SINT8 , INT16 , v.s8 , s16); + BASIC_SIMPLE(m, UINT16, UINT16 , v.u16); + BASIC_SIMPLE(m, SINT16, INT16 , v.s16); + BASIC_SIMPLE(m, UINT32, UINT32 , v.u32); + BASIC_SIMPLE(m, SINT32, INT32 , v.s32); + BASIC_SIMPLE(m, UINT64, UINT64 , v.u64); + BASIC_SIMPLE(m, SINT64, INT64 , v.s64); + BASIC_SIMPLE(m, DOUBLE, DOUBLE , v.dbl); + + case MRP_MSG_FIELD_BLOB: + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + { + uint8_t blb[n]; + + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL)) + goto fail; + + for (j = 0; j < n; j++) { + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, + blb + j)) + goto fail; + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + + asize = n; + if (!mrp_msg_append(msg, tag, type, asize, blb)) + goto fail; + } + break; + + default: + if (!(type & MRP_MSG_FIELD_ARRAY)) + goto fail; + + base = type & ~(MRP_MSG_FIELD_ARRAY); + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + sig = get_array_signature(base); + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + { + char *astr[n]; + uint32_t dbln[n]; + bool abln[n]; + uint8_t au8 [n]; + int8_t as8 [n]; + uint16_t au16[n]; + int16_t as16[n]; + uint32_t au32[n]; + int32_t as32[n]; + uint64_t au64[n]; + int64_t as64[n]; + double adbl[n]; + + for (j = 0; j < n; j++) { + switch (base) { + ARRAY_SIMPLE(m, STRING, STRING , astr[j]); + ARRAY_QUIRKY(m, BOOL , BOOLEAN, abln[j], dbln[j]); + ARRAY_QUIRKY(m, UINT8 , UINT16 , au8[j] , au16[j]); + ARRAY_QUIRKY(m, SINT8 , INT16 , as8[j] , as16[j]); + ARRAY_SIMPLE(m, UINT16, UINT16 , au16[j]); + ARRAY_SIMPLE(m, SINT16, INT16 , as16[j]); + ARRAY_SIMPLE(m, UINT32, UINT32 , au32[j]); + ARRAY_SIMPLE(m, SINT32, INT32 , as32[j]); + ARRAY_SIMPLE(m, UINT64, UINT64 , au64[j]); + ARRAY_SIMPLE(m, SINT64, INT64 , as64[j]); + ARRAY_SIMPLE(m, DOUBLE, DOUBLE , adbl[j]); + default: + goto fail; + } + } + + switch (base) { + APPEND_ARRAY(STRING, astr); + APPEND_ARRAY(BOOL , abln); + APPEND_ARRAY(UINT8 , au8 ); + APPEND_ARRAY(SINT8 , as8 ); + APPEND_ARRAY(UINT16, au16); + APPEND_ARRAY(SINT16, as16); + APPEND_ARRAY(UINT32, au32); + APPEND_ARRAY(SINT32, as32); + APPEND_ARRAY(UINT64, au64); + APPEND_ARRAY(SINT64, as64); + APPEND_ARRAY(DOUBLE, adbl); + default: + goto fail; + } + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + } + } + + if (sender_id != NULL) + *sender_id = sender; + + return msg; + + fail: + mrp_msg_unref(msg); + errno = EBADMSG; + + return NULL; + +#undef BASIC_SIMPLE +#undef BASIC_QUIRKY +#undef ARRAY_SIMPLE +#undef ARRAY_QUIRKY +#undef APPEND_ARRAY +} + + +static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + void *data, uint16_t tag) +{ +#define BASIC_SIMPLE(_mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = &(_val); \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + +#define BASIC_QUIRKY(_mtype, _dtype, _mval, _dval) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + _dval = _mval; \ + vptr = &_dval; \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_SIMPLE(_mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = &_val; \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_QUIRKY(_mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + _dvar = _mvar; \ + vptr = &_dvar; \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + + mrp_dbus_msg_t *m; + mrp_data_descr_t *descr; + mrp_data_member_t *fields, *f; + int nfield; + uint16_t type, base; + mrp_msg_value_t *v; + void *vptr; + uint32_t n, j; + int i, blblen; + const char *sig; + uint16_t u16; + int16_t s16; + uint32_t bln, asize; + + m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member); + + if (m == NULL) + return NULL; + + descr = mrp_msg_find_type(tag); + + if (descr == NULL) + goto fail; + + fields = descr->fields; + nfield = descr->nfield; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender_id)) + goto fail; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &tag)) + goto fail; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &nfield)) + goto fail; + + for (i = 0, f = fields; i < nfield; i++, f++) { + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) || + !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type)) + goto fail; + + v = (mrp_msg_value_t *)(data + f->offs); + + switch (f->type) { + BASIC_SIMPLE(STRING, STRING , v->str); + BASIC_QUIRKY(BOOL , BOOLEAN, v->bln, bln); + BASIC_QUIRKY(UINT8 , UINT16 , v->u8 , u16); + BASIC_QUIRKY(SINT8 , INT16 , v->s8 , s16); + BASIC_SIMPLE(UINT16, UINT16 , v->u16); + BASIC_SIMPLE(SINT16, INT16 , v->s16); + BASIC_SIMPLE(UINT32, UINT32 , v->u32); + BASIC_SIMPLE(SINT32, INT32 , v->s32); + BASIC_SIMPLE(UINT64, UINT64 , v->u64); + BASIC_SIMPLE(SINT64, INT64 , v->s64); + BASIC_SIMPLE(DOUBLE, DOUBLE , v->dbl); + + case MRP_MSG_FIELD_BLOB: + sig = get_array_signature(f->type); + blblen = mrp_data_get_blob_size(data, descr, i); + asize = blblen; + + if (blblen == -1) + goto fail; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + for (i = 0; i < blblen; i++) + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, + f->blb + i)) + goto fail; + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + break; + + default: + if (!(f->type & MRP_MSG_FIELD_ARRAY)) + goto fail; + + base = f->type & ~(MRP_MSG_FIELD_ARRAY); + n = mrp_data_get_array_size(data, descr, i); + sig = get_array_signature(base); + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + for (j = 0; j < n; j++) { + switch (base) { + ARRAY_SIMPLE(STRING, STRING , v->astr[j]); + ARRAY_QUIRKY(BOOL , BOOLEAN, v->abln[j], bln); + ARRAY_QUIRKY(UINT8 , UINT16 , v->au8[j] , u16); + ARRAY_QUIRKY(SINT8 , INT16 , v->as8[j] , s16); + ARRAY_SIMPLE(UINT16, UINT16 , v->au16[j]); + ARRAY_SIMPLE(SINT16, INT16 , v->as16[j]); + ARRAY_SIMPLE(UINT32, UINT32 , v->au32[j]); + ARRAY_SIMPLE(SINT32, INT32 , v->as32[j]); + ARRAY_SIMPLE(UINT64, UINT64 , v->au64[j]); + ARRAY_SIMPLE(DOUBLE, DOUBLE , v->adbl[j]); + + case MRP_MSG_FIELD_BLOB: + goto fail; + + default: + goto fail; + } + } + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + } + } + + return m; + + fail: + if (m != NULL) + mrp_dbus_msg_unref(m); + + errno = ECOMM; + + return NULL; + +#undef BASIC_SIMPLE +#undef BASIC_QUIRKY +#undef ARRAY_SIMPLE +#undef ARRAY_QUIRKY +} + + +static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield, + uint16_t tag) +{ + mrp_data_member_t *f; + int i; + + for (i = 0, f = fields; i < nfield; i++, f++) + if (f->tag == tag) + return f; + + return NULL; +} + + +static void *data_decode(mrp_dbus_msg_t *m, uint16_t *tagp, + const char **sender_id) +{ +#define HANDLE_SIMPLE(_i, _mtype, _dtype, _var) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \ + &(_var))) \ + goto fail; \ + break + +#define HANDLE_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \ + &(_dvar))) \ + goto fail; \ + \ + _mvar = _dvar; \ + break + + void *data; + mrp_data_descr_t *descr; + mrp_data_member_t *fields, *f; + int nfield; + uint16_t tag, type, base; + mrp_msg_value_t *v; + uint32_t n, j, size; + int i; + const char *sender, *sig; + uint32_t u32; + uint16_t u16; + int16_t s16; + + tag = 0; + data = NULL; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag)) + goto fail; + + descr = mrp_msg_find_type(tag); + + if (descr == NULL) + goto fail; + + *tagp = tag; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield)) + goto fail; + + if (nfield != descr->nfield) + goto fail; + + fields = descr->fields; + data = mrp_allocz(descr->size); + + if (MRP_UNLIKELY(data == NULL)) + goto fail; + + for (i = 0; i < nfield; i++) { + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type)) + goto fail; + + f = member_type(fields, nfield, tag); + + if (MRP_UNLIKELY(f == NULL)) + goto fail; + + v = (mrp_msg_value_t *)(data + f->offs); + + switch (type) { + HANDLE_SIMPLE(&im, STRING, STRING , v->str); + HANDLE_QUIRKY(&im, BOOL , BOOLEAN, v->bln, u32); + HANDLE_QUIRKY(&im, UINT8 , UINT16 , v->u8 , u16); + HANDLE_QUIRKY(&im, SINT8 , INT16 , v->s8 , s16); + HANDLE_SIMPLE(&im, UINT16, UINT16 , v->u16); + HANDLE_SIMPLE(&im, SINT16, INT16 , v->s16); + HANDLE_SIMPLE(&im, UINT32, UINT32 , v->u32); + HANDLE_SIMPLE(&im, SINT32, INT32 , v->s32); + HANDLE_SIMPLE(&im, UINT64, UINT64 , v->u64); + HANDLE_SIMPLE(&im, SINT64, INT64 , v->s64); + HANDLE_SIMPLE(&im, DOUBLE, DOUBLE , v->dbl); + + case MRP_MSG_FIELD_BLOB: + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &size)) + goto fail; + + sig = MRP_DBUS_TYPE_BYTE_AS_STRING; + + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + { + uint8_t blb[size]; + + for (j = 0; j < size; j++) + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, + blb + j)) + goto fail; + + v->blb = mrp_alloc(size); + + if (v->blb == NULL && size != 0) + goto fail; + + memcpy(v->blb, blb, size); + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + break; + + default: + if (!(f->type & MRP_MSG_FIELD_ARRAY)) + goto fail; + + base = type & ~(MRP_MSG_FIELD_ARRAY); + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL)) + goto fail; + + size = n; + + switch (base) { + case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break; + case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break; + case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break; + case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break; + case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break; + case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break; + case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break; + case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break; + case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break; + case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break; + case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break; + default: + goto fail; + } + + v->aany = mrp_allocz(size); + if (v->aany == NULL) + goto fail; + + for (j = 0; j < n; j++) { + uint32_t dbln[n]; + uint16_t au16[n]; + int16_t as16[n]; + + switch (base) { + HANDLE_SIMPLE(&ia, STRING, STRING , v->astr[j]); + HANDLE_QUIRKY(&ia, BOOL , BOOLEAN, v->abln[j], dbln[j]); + HANDLE_QUIRKY(&ia, UINT8 , UINT16 , v->au8[j] , au16[j]); + HANDLE_QUIRKY(&ia, SINT8 , INT16 , v->as8[j] , as16[j]); + HANDLE_SIMPLE(&ia, UINT16, UINT16 , v->au16[j]); + HANDLE_SIMPLE(&ia, SINT16, INT16 , v->as16[j]); + HANDLE_SIMPLE(&ia, UINT32, UINT32 , v->au32[j]); + HANDLE_SIMPLE(&ia, SINT32, INT32 , v->as32[j]); + HANDLE_SIMPLE(&ia, UINT64, UINT64 , v->au64[j]); + HANDLE_SIMPLE(&ia, SINT64, INT64 , v->as64[j]); + HANDLE_SIMPLE(&ia, DOUBLE, DOUBLE , v->adbl[j]); + } + + if (base == MRP_MSG_FIELD_STRING) { + v->astr[j] = mrp_strdup(v->astr[j]); + if (v->astr[j] == NULL) + goto fail; + } + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + } + + if (f->type == MRP_MSG_FIELD_STRING) { + v->str = mrp_strdup(v->str); + if (v->str == NULL) + goto fail; + } + } + + if (sender_id != NULL) + *sender_id = sender; + + return data; + + fail: + mrp_data_free(data, tag); + errno = EBADMSG; + + return NULL; +} + + +static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + void *data, size_t size) +{ + mrp_dbus_msg_t *m; + const char *sig; + uint32_t i, n; + + m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member); + + if (m == NULL) + return NULL; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender_id)) + goto fail; + + sig = MRP_DBUS_TYPE_BYTE_AS_STRING; + n = size; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + for (i = 0; i < n; i++) + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, data + i)) + goto fail; + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + + return m; + + fail: + mrp_dbus_msg_unref(m); + + errno = ECOMM; + + return NULL; +} + + +static void *raw_decode(mrp_dbus_msg_t *m, size_t *sizep, + const char **sender_id) +{ + const char *sender, *sig; + void *data; + uint32_t n, i; + + data = NULL; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + sig = MRP_DBUS_TYPE_BYTE_AS_STRING; + + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + { + uint8_t databuf[n]; + + for (i = 0; i < n; i++) + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, databuf + i)) + goto fail; + + data = mrp_alloc(n); + + if (data == NULL && n != 0) + goto fail; + + memcpy(data, databuf, n); + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + + if (sizep != NULL) + *sizep = (size_t)n; + + if (sender_id != NULL) + *sender_id = sender; + + return data; + + fail: + errno = EBADMSG; + + return NULL; +} + + +MRP_REGISTER_TRANSPORT(dbus, DBUS, dbus_t, dbus_resolve, + dbus_open, dbus_createfrom, dbus_close, NULL, + dbus_bind, NULL, NULL, + dbus_connect, dbus_disconnect, + dbus_sendmsg, dbus_sendmsgto, + dbus_sendraw, dbus_sendrawto, + dbus_senddata, dbus_senddatato, + NULL, NULL); diff --git a/src/common/dbus-libdbus.c b/src/common/dbus-libdbus.c new file mode 100644 index 0000000..59722eb --- /dev/null +++ b/src/common/dbus-libdbus.c @@ -0,0 +1,2054 @@ +/* + * Copyright (c) 2012, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DBUS_ADMIN_SERVICE "org.freedesktop.DBus" +#define DBUS_ADMIN_INTERFACE "org.freedesktop.DBus" +#define DBUS_ADMIN_PATH "/org/freedesktop/DBus" +#define DBUS_NAME_CHANGED "NameOwnerChanged" + + +struct mrp_dbus_s { + char *address; /* bus address */ + DBusConnection *conn; /* actual D-BUS connection */ + mrp_mainloop_t *ml; /* murphy mainloop */ + mrp_htbl_t *methods; /* method handler table */ + mrp_htbl_t *signals; /* signal handler table */ + mrp_list_hook_t name_trackers; /* peer (name) watchers */ + mrp_list_hook_t calls; /* pending calls */ + uint32_t call_id; /* next call id */ + const char *unique_name; /* our unique D-BUS address */ + int priv; /* whether a private connection */ + int signal_filter; /* if signal dispatching is set up */ + int register_fallback; /* if the fallback object is set up */ + mrp_refcnt_t refcnt; /* reference count */ +}; + + +struct mrp_dbus_msg_s { + DBusMessage *msg; /* actual D-BUS message */ + mrp_refcnt_t refcnt; /* reference count */ + mrp_list_hook_t iterators; /* iterator stack */ + mrp_list_hook_t arrays; /* implicitly freed related arrays */ +}; + + +typedef struct { + DBusMessageIter it; /* actual iterator */ + char *peeked; /* peeked contents, or NULL */ + mrp_list_hook_t hook; /* hook to iterator stack */ +} msg_iter_t; + + +typedef struct { + mrp_list_hook_t hook; + char **items; + size_t nitem; +} msg_array_t; + +/* + * Notes: + * + * At the moment we administer DBUS method and signal handlers + * in a very primitive way (subject to be changed later). For + * every bus instance we maintain two hash tables, one for methods + * and another for signals. Each method and signal handler is + * hashed in only by it's method/signal name to a linked list of + * method or signal handlers. + * + * When dispatching a method, we look up the chain with a matching + * method name, or the chain for "" in case a matching chain is + * not found, and invoke the handler which best matches the + * received message (by looking at the path, interface and name). + * Only one such handler is invoked at most. + * + * For signals we look up both the chain with a matching name and + * the chain for "" and invoke all signal handlers that match the + * received message (regardless of their return value). + */ + + +typedef struct { + char *member; /* signal/method name */ + mrp_list_hook_t handlers; /* handlers with matching member */ +} handler_list_t; + +typedef struct { + mrp_list_hook_t hook; + char *sender; + char *path; + char *interface; + char *member; + mrp_dbus_handler_t handler; + void *user_data; +} handler_t; + +#define method_t handler_t +#define signal_t handler_t + + +typedef struct { + mrp_list_hook_t hook; /* hook to name tracker list */ + char *name; /* name to track */ + mrp_dbus_name_cb_t cb; /* status change callback */ + void *user_data; /* opaque callback user data */ + int32_t qid; /* initial query ID */ +} name_tracker_t; + + +typedef struct { + mrp_dbus_t *dbus; /* DBUS connection */ + int32_t id; /* call id */ + mrp_dbus_reply_cb_t cb; /* completion notification callback */ + void *user_data; /* opaque callback data */ + DBusPendingCall *pend; /* pending DBUS call */ + mrp_list_hook_t hook; /* hook to list of pending calls */ +} call_t; + + +typedef struct { + mrp_mainloop_t *ml; /* mainloop for bus connection */ + const char *address; /* address of bus */ +} bus_spec_t; + +static mrp_htbl_t *buses; + + + +static DBusHandlerResult dispatch_signal(DBusConnection *c, + DBusMessage *msg, void *data); +static DBusHandlerResult dispatch_method(DBusConnection *c, + DBusMessage *msg, void *data); +static void purge_name_trackers(mrp_dbus_t *dbus); +static void purge_calls(mrp_dbus_t *dbus); +static void handler_list_free_cb(void *key, void *entry); +static void handler_free(handler_t *h); +static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, + void *data); +static void call_free(call_t *call); +static void free_msg_array(msg_array_t *a); + + + +static int purge_filters(void *key, void *entry, void *user_data) +{ + mrp_dbus_t *dbus = (mrp_dbus_t *)user_data; + handler_list_t *l = (handler_list_t *)entry; + mrp_list_hook_t *p, *n; + handler_t *h; + + MRP_UNUSED(key); + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + mrp_dbus_remove_filter(dbus, + h->sender, h->path, h->interface, + h->member, NULL); + } + + return MRP_HTBL_ITER_MORE; +} + + +void dbus_disconnect(mrp_dbus_t *dbus) +{ + if (dbus) { + mrp_htbl_remove(buses, dbus->conn, FALSE); + + if (dbus->signals) { + mrp_htbl_foreach(dbus->signals, purge_filters, dbus); + mrp_htbl_destroy(dbus->signals, TRUE); + } + if (dbus->methods) + mrp_htbl_destroy(dbus->methods, TRUE); + + if (dbus->conn != NULL) { + if (dbus->signal_filter) + dbus_connection_remove_filter(dbus->conn, dispatch_signal, + dbus); + if (dbus->register_fallback) + dbus_connection_unregister_object_path(dbus->conn, "/"); + if (dbus->priv) + dbus_connection_close(dbus->conn); + dbus_connection_unref(dbus->conn); + } + + purge_name_trackers(dbus); + purge_calls(dbus); + + mrp_free(dbus->address); + dbus->conn = NULL; + dbus->ml = NULL; + + mrp_free(dbus); + } +} + + +static int bus_cmp(const void *key1, const void *key2) +{ + return key2 - key1; +} + + +static uint32_t bus_hash(const void *key) +{ + uint32_t h; + + h = (ptrdiff_t)key; + h >>= 2 * sizeof(key); + + return h; +} + + +static int find_bus_by_spec(void *key, void *object, void *user_data) +{ + mrp_dbus_t *dbus = (mrp_dbus_t *)object; + bus_spec_t *spec = (bus_spec_t *)user_data; + + MRP_UNUSED(key); + + if (dbus->ml == spec->ml && !strcmp(dbus->address, spec->address)) + return TRUE; + else + return FALSE; +} + + +static mrp_dbus_t *dbus_get(mrp_mainloop_t *ml, const char *address) +{ + mrp_htbl_config_t hcfg; + bus_spec_t spec; + + if (buses == NULL) { + mrp_clear(&hcfg); + + hcfg.comp = bus_cmp; + hcfg.hash = bus_hash; + hcfg.free = NULL; + + buses = mrp_htbl_create(&hcfg); + + return NULL; + } + else { + spec.ml = ml; + spec.address = address; + + return mrp_htbl_find(buses, find_bus_by_spec, &spec); + } +} + + +mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address, + mrp_dbus_err_t *errp) +{ + static struct DBusObjectPathVTable vtable = { + .message_function = dispatch_method + }; + + mrp_htbl_config_t hcfg; + mrp_dbus_t *dbus; + + if ((dbus = dbus_get(ml, address)) != NULL) + return mrp_dbus_ref(dbus); + + if ((dbus = mrp_allocz(sizeof(*dbus))) == NULL) + return NULL; + + mrp_list_init(&dbus->calls); + mrp_list_init(&dbus->name_trackers); + mrp_refcnt_init(&dbus->refcnt); + + dbus->ml = ml; + + + mrp_dbus_error_init(errp); + + /* + * connect to the bus + */ + + if (!strcmp(address, "system")) + dbus->conn = dbus_bus_get(DBUS_BUS_SYSTEM, errp); + else if (!strcmp(address, "session")) + dbus->conn = dbus_bus_get(DBUS_BUS_SESSION, errp); + else { + dbus->conn = dbus_connection_open_private(address, errp); + dbus->priv = TRUE; + + if (dbus->conn == NULL || !dbus_bus_register(dbus->conn, errp)) + goto fail; + } + + if (dbus->conn == NULL) + goto fail; + + dbus->address = mrp_strdup(address); + dbus->unique_name = dbus_bus_get_unique_name(dbus->conn); + + /* + * set up with mainloop + */ + + if (!mrp_dbus_setup_connection(ml, dbus->conn)) + goto fail; + + /* + * set up our message dispatchers and take our name on the bus + */ + + if (!dbus_connection_add_filter(dbus->conn, dispatch_signal, dbus, NULL)) { + dbus_set_error(errp, DBUS_ERROR_FAILED, + "Failed to set up signal dispatching."); + goto fail; + } + dbus->signal_filter = TRUE; + + if (!dbus_connection_register_fallback(dbus->conn, "/", &vtable, dbus)) { + dbus_set_error(errp, DBUS_ERROR_FAILED, + "Failed to set up method dispatching."); + goto fail; + } + dbus->register_fallback = TRUE; + + mrp_clear(&hcfg); + hcfg.comp = mrp_string_comp; + hcfg.hash = mrp_string_hash; + hcfg.free = handler_list_free_cb; + + if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) { + dbus_set_error(errp, DBUS_ERROR_FAILED, + "Failed to create DBUS method table."); + goto fail; + } + + if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) { + dbus_set_error(errp, DBUS_ERROR_FAILED, + "Failed to create DBUS signal table."); + goto fail; + } + + + /* + * install handler for NameOwnerChanged for tracking clients/peers + */ + + if (!mrp_dbus_add_signal_handler(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH, + DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED, + name_owner_change_cb, NULL)) { + dbus_set_error(errp, DBUS_ERROR_FAILED, + "Failed to install NameOwnerChanged handler."); + goto fail; + } + + /* install a 'safe' filter to avoid receiving all name change signals */ + mrp_dbus_install_filter(dbus, + DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH, + DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED, + DBUS_ADMIN_SERVICE, NULL); + + mrp_list_init(&dbus->name_trackers); + dbus->call_id = 1; + + if (mrp_htbl_insert(buses, dbus->conn, dbus)) + return dbus; + + fail: + dbus_disconnect(dbus); + return NULL; +} + + +mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus) +{ + return mrp_ref_obj(dbus, refcnt); +} + + +int mrp_dbus_unref(mrp_dbus_t *dbus) +{ + if (mrp_unref_obj(dbus, refcnt)) { + dbus_disconnect(dbus); + + return TRUE; + } + else + return FALSE; +} + + +int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_err_t *error) +{ + int flags, status; + + mrp_dbus_error_init(error); + + flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE; + status = dbus_bus_request_name(dbus->conn, name, flags, error); + + if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + return TRUE; + else { + if (status == DBUS_REQUEST_NAME_REPLY_EXISTS) { + if (error) + dbus_error_free(error); + dbus_set_error(error, DBUS_ERROR_FAILED, "name already taken"); + } + return FALSE; + } +} + + +int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_err_t *error) +{ + mrp_dbus_error_init(error); + + if (dbus_bus_release_name(dbus->conn, name, error) != -1) + return TRUE; + else + return FALSE; +} + + +const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus) +{ + return dbus->unique_name; +} + + +static void name_owner_query_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data) +{ + name_tracker_t *t = (name_tracker_t *)data; + DBusMessage *msg = m->msg; + const char *owner; + int state; + + if (t->cb != NULL) { /* tracker still active */ + t->qid = 0; + state = dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &owner, + DBUS_TYPE_INVALID)) + owner = ""; + + t->cb(dbus, t->name, state, owner, t->user_data); + } + else /* already requested to delete */ + mrp_free(t); +} + + +static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data) +{ + const char *name, *prev, *next; + mrp_list_hook_t *p, *n; + name_tracker_t *t; + DBusMessage *msg; + + MRP_UNUSED(data); + + msg = m->msg; + + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) + return FALSE; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &prev, + DBUS_TYPE_STRING, &next, + DBUS_TYPE_INVALID)) + return FALSE; + +#if 0 + /* + * Notes: XXX TODO + * In principle t->cb could call mrp_dbus_forget for some other D-BUS + * address than name. If that happened to be n (== p->hook.next) this + * would result in a crash or memory corruption in the next iteration + * of this loop (when handling n). We can easily get around this + * problem by + * + * 1. administering in mrp_dbus_t that we're handing a NameOwnerChange + * 2. checking for this in mrp_dbus_forget_name and if it is the case + * only marking the affected entry for deletion + * 3. removing entries marked for deletion in this loop (or just + * ignoring them and making another pass in the end removing any + * such entry). + */ +#endif + + mrp_list_foreach(&dbus->name_trackers, p, n) { + t = mrp_list_entry(p, name_tracker_t, hook); + + if (!strcmp(name, t->name)) + t->cb(dbus, name, next && *next, next, t->user_data); + } + + return TRUE; +} + + +int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_name_cb_t cb, void *user_data) +{ + name_tracker_t *t; + + if ((t = mrp_allocz(sizeof(*t))) != NULL) { + if ((t->name = mrp_strdup(name)) != NULL) { + t->cb = cb; + t->user_data = user_data; + + if (mrp_dbus_install_filter(dbus, + DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH, + DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED, + name, NULL)) { + mrp_list_append(&dbus->name_trackers, &t->hook); + + t->qid = mrp_dbus_call(dbus, + DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH, + DBUS_ADMIN_SERVICE, "GetNameOwner", 5000, + name_owner_query_cb, t, + DBUS_TYPE_STRING, t->name, + DBUS_TYPE_INVALID); + return TRUE; + } + else { + mrp_free(t->name); + mrp_free(t); + } + } + } + + return FALSE; +} + + +int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_name_cb_t cb, void *user_data) +{ + mrp_list_hook_t *p, *n; + name_tracker_t *t; + + mrp_dbus_remove_filter(dbus, + DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH, + DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED, + name, NULL); + + mrp_list_foreach(&dbus->name_trackers, p, n) { + t = mrp_list_entry(p, name_tracker_t, hook); + + if (t->cb == cb && t->user_data == user_data && !strcmp(t->name,name)) { + mrp_list_delete(&t->hook); + mrp_free(t->name); + + if (!t->qid) + mrp_free(t); + else { + t->cb = NULL; + t->user_data = NULL; + t->name = NULL; + } + + return TRUE; + } + } + + return FALSE; +} + + +static void purge_name_trackers(mrp_dbus_t *dbus) +{ + mrp_list_hook_t *p, *n; + name_tracker_t *t; + + mrp_list_foreach(&dbus->name_trackers, p, n) { + t = mrp_list_entry(p, name_tracker_t, hook); + + mrp_list_delete(p); + mrp_dbus_remove_filter(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH, + DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED, + t->name, NULL); + mrp_free(t->name); + mrp_free(t); + } +} + + +static handler_t *handler_alloc(const char *sender, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data) +{ + handler_t *h; + + if ((h = mrp_allocz(sizeof(*h))) != NULL) { + h->sender = mrp_strdup(sender); + h->path = mrp_strdup(path); + h->interface = mrp_strdup(interface); + h->member = mrp_strdup(member); + + if ((path && !h->path) || !h->interface || !h->member) { + handler_free(h); + return NULL; + } + + h->handler = handler; + h->user_data = user_data; + + return h; + } + + return NULL; +} + + +static void handler_free(handler_t *h) +{ + if (h != NULL) { + mrp_free(h->sender); + mrp_free(h->path); + mrp_free(h->interface); + mrp_free(h->member); + + mrp_free(h); + } +} + + +static handler_list_t *handler_list_alloc(const char *member) +{ + handler_list_t *l; + + if ((l = mrp_allocz(sizeof(*l))) != NULL) { + if ((l->member = mrp_strdup(member)) != NULL) + mrp_list_init(&l->handlers); + else { + mrp_free(l); + l = NULL; + } + } + + return l; +} + + +static inline void handler_list_free(handler_list_t *l) +{ + mrp_list_hook_t *p, *n; + handler_t *h; + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + mrp_list_delete(p); + handler_free(h); + } + + mrp_free(l->member); + mrp_free(l); +} + + +static void handler_list_free_cb(void *key, void *entry) +{ + MRP_UNUSED(key); + + handler_list_free((handler_list_t *)entry); +} + + +static inline int handler_specificity(handler_t *h) +{ + int score = 0; + + if (h->path && *h->path) + score |= 0x4; + if (h->interface && *h->interface) + score |= 0x2; + if (h->member && *h->member) + score |= 0x1; + + return score; +} + + +static void handler_list_insert(handler_list_t *l, handler_t *handler) +{ + mrp_list_hook_t *p, *n; + handler_t *h; + int score; + + score = handler_specificity(handler); + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + + if (score >= handler_specificity(h)) { + mrp_list_append(h->hook.prev, &handler->hook); /* add before h */ + return; + } + } + + mrp_list_append(&l->handlers, &handler->hook); +} + + +static handler_t *handler_list_lookup(handler_list_t *l, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, + void *user_data) +{ + mrp_list_hook_t *p, *n; + handler_t *h; + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + + if (h->handler == handler && user_data == h->user_data && + path && !strcmp(path, h->path) && + interface && !strcmp(interface, h->interface) && + member && !strcmp(member, h->member)) + return h; + } + + return NULL; +} + + +static handler_t *handler_list_find(handler_list_t *l, const char *path, + const char *interface, const char *member) +{ +#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field)) + mrp_list_hook_t *p, *n; + handler_t *h; + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + + if (MATCHES(h, path) && MATCHES(h, interface) && MATCHES(h, member)) + return h; + } + + return NULL; +#undef MATCHES +} + + +int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data) +{ + handler_list_t *methods; + handler_t *m; + + if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) { + if ((methods = handler_list_alloc(member)) == NULL) + return FALSE; + + mrp_htbl_insert(dbus->methods, methods->member, methods); + } + + m = handler_alloc(NULL, path, interface, member, handler, user_data); + if (m != NULL) { + handler_list_insert(methods, m); + return TRUE; + } + else + return FALSE; +} + + +int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data) +{ + handler_list_t *methods; + handler_t *m; + + if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) + return FALSE; + + m = handler_list_lookup(methods, path, interface, member, + handler, user_data); + if (m != NULL) { + mrp_list_delete(&m->hook); + handler_free(m); + + return TRUE; + } + else + return FALSE; +} + + +int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, mrp_dbus_handler_t handler, + void *user_data) +{ + handler_list_t *signals; + handler_t *s; + + if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) { + if ((signals = handler_list_alloc(member)) == NULL) + return FALSE; + + if (!mrp_htbl_insert(dbus->signals, signals->member, signals)) { + handler_list_free(signals); + return FALSE; + } + } + + s = handler_alloc(sender, path, interface, member, handler, user_data); + if (s != NULL) { + handler_list_insert(signals, s); + return TRUE; + } + else { + handler_free(s); + if (mrp_list_empty(&signals->handlers)) + mrp_htbl_remove(dbus->signals, signals->member, TRUE); + return FALSE; + } +} + + + +int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, mrp_dbus_handler_t handler, + void *user_data) +{ + handler_list_t *signals; + handler_t *s; + + MRP_UNUSED(sender); + + if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) + return FALSE; + + s = handler_list_lookup(signals, path, interface, member, + handler, user_data); + if (s != NULL) { + mrp_list_delete(&s->hook); + handler_free(s); + + if (mrp_list_empty(&signals->handlers)) + mrp_htbl_remove(dbus->signals, (void *)member, TRUE); + + return TRUE; + } + else + return FALSE; +} + + + +int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus, + mrp_dbus_handler_t handler, void *user_data, + const char *sender, const char *path, + const char *interface, const char *member, ...) +{ + va_list ap; + int success; + + + if (mrp_dbus_add_signal_handler(dbus, sender, path, interface, member, + handler, user_data)) { + va_start(ap, member); + success = mrp_dbus_install_filterv(dbus, + sender, path, interface, member, ap); + va_end(ap); + + if (success) + return TRUE; + else + mrp_dbus_del_signal_handler(dbus, sender, path, interface, member, + handler, user_data); + } + + return FALSE; +} + + +int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus, + mrp_dbus_handler_t handler, void *user_data, + const char *sender, const char *path, + const char *interface, const char *member, ...) +{ + va_list ap; + int status; + + status = mrp_dbus_del_signal_handler(dbus, sender, path, interface, member, + handler, user_data); + va_start(ap, member); + status &= mrp_dbus_remove_filterv(dbus, + sender, path, interface, member, ap); + va_end(ap); + + return status; +} + + +int mrp_dbus_install_filterv(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, va_list args) +{ +#define ADD_TAG(tag, value) do { \ + if (value != NULL) { \ + l = snprintf(p, n, "%s%s='%s'", p == filter ? "" : ",", \ + tag, value); \ + if (l >= n) \ + return FALSE; \ + n -= l; \ + p += l; \ + } \ + } while (0) + + va_list ap; + DBusError error; + char filter[1024], *p, argn[16], *val; + int n, l, i; + + p = filter; + n = sizeof(filter); + + ADD_TAG("type" , "signal"); + ADD_TAG("sender" , sender); + ADD_TAG("path" , path); + ADD_TAG("interface", interface); + ADD_TAG("member" , member); + + va_copy(ap, args); + i = 0; + while ((val = va_arg(ap, char *)) != NULL) { + snprintf(argn, sizeof(argn), "arg%d", i); + ADD_TAG(argn, val); + i++; + } + va_end(ap); + + dbus_error_init(&error); + dbus_bus_add_match(dbus->conn, filter, &error); + + if (dbus_error_is_set(&error)) { + mrp_log_error("Failed to install filter '%s' (error: %s).", filter, + mrp_dbus_errmsg(&error)); + dbus_error_free(&error); + + return FALSE; + } + else + return TRUE; + +} + + +int mrp_dbus_install_filter(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, ...) +{ + va_list ap; + int status; + + va_start(ap, member); + status = mrp_dbus_install_filterv(dbus, + sender, path, interface, member, ap); + va_end(ap); + + return status; +} + + +int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, va_list args) +{ + va_list ap; + char filter[1024], *p, argn[16], *val; + int n, l, i; + + p = filter; + n = sizeof(filter); + + ADD_TAG("type" , "signal"); + ADD_TAG("sender" , sender); + ADD_TAG("path" , path); + ADD_TAG("interface", interface); + ADD_TAG("member" , member); + + va_copy(ap, args); + i = 0; + while ((val = va_arg(ap, char *)) != NULL) { + snprintf(argn, sizeof(argn), "arg%d", i); + ADD_TAG(argn, val); + i++; + } + va_end(ap); + + dbus_bus_remove_match(dbus->conn, filter, NULL); + return TRUE; +#undef ADD_TAG +} + + +int mrp_dbus_remove_filter(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, ...) +{ + va_list ap; + int status; + + va_start(ap, member); + status = mrp_dbus_remove_filterv(dbus, sender, path, interface, member, ap); + va_end(ap); + + return status; +} + + +static inline mrp_dbus_msg_t *create_message(DBusMessage *msg) +{ + mrp_dbus_msg_t *m; + + if (msg != NULL) { + if ((m = mrp_allocz(sizeof(*m))) != NULL) { + mrp_refcnt_init(&m->refcnt); + mrp_list_init(&m->iterators); + mrp_list_init(&m->arrays); + m->msg = dbus_message_ref(msg); + } + } + else + m = NULL; + + return m; +} + + +mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m) +{ + return mrp_ref_obj(m, refcnt); +} + + +static void free_message(mrp_dbus_msg_t *m) +{ + mrp_list_hook_t *p, *n; + msg_iter_t *it; + msg_array_t *a; + + mrp_list_foreach(&m->iterators, p, n) { + it = mrp_list_entry(p, typeof(*it), hook); + + mrp_list_delete(&it->hook); + mrp_free(it->peeked); + mrp_free(it); + } + + mrp_list_foreach(&m->arrays, p, n) { + a = mrp_list_entry(p, typeof(*a), hook); + + free_msg_array(a); + } + + mrp_free(m); +} + + +int mrp_dbus_msg_unref(mrp_dbus_msg_t *m) +{ + DBusMessage *msg; + + if (mrp_unref_obj(m, refcnt)) { + msg = m->msg; + + free_message(m); + + if (msg != NULL) + dbus_message_unref(msg); + + return TRUE; + } + else + return FALSE; +} + + +static DBusHandlerResult dispatch_method(DBusConnection *c, + DBusMessage *msg, void *data) +{ +#define SAFESTR(str) (str ? str : "") + const char *path = dbus_message_get_path(msg); + const char *interface = dbus_message_get_interface(msg); + const char *member = dbus_message_get_member(msg); + + mrp_dbus_t *dbus = (mrp_dbus_t *)data; + mrp_dbus_msg_t *m = NULL; + int r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + handler_list_t *l; + handler_t *h; + + MRP_UNUSED(c); + + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL || !member) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + mrp_debug("path='%s', interface='%s', member='%s')...", + SAFESTR(path), SAFESTR(interface), SAFESTR(member)); + + if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) { + retry: + if ((h = handler_list_find(l, path, interface, member)) != NULL) { + if (m == NULL) + m = create_message(msg); + + if (m != NULL && h->handler(dbus, m, h->user_data)) + r = DBUS_HANDLER_RESULT_HANDLED; + + goto out; + } + } + else { + if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL) + goto retry; + } + + out: + mrp_dbus_msg_unref(m); + + if (r == DBUS_HANDLER_RESULT_NOT_YET_HANDLED) + mrp_debug("Unhandled method path=%s, %s.%s.", SAFESTR(path), + SAFESTR(interface), SAFESTR(member)); + + return r; +} + + +static DBusHandlerResult dispatch_signal(DBusConnection *c, + DBusMessage *msg, void *data) +{ +#define MATCHES(h, field) (!*field || !h->field || !*h->field || \ + !strcmp(field, h->field)) + + const char *path = dbus_message_get_path(msg); + const char *interface = dbus_message_get_interface(msg); + const char *member = dbus_message_get_member(msg); + + mrp_dbus_t *dbus = (mrp_dbus_t *)data; + mrp_dbus_msg_t *m = NULL; + mrp_list_hook_t *p, *n; + handler_list_t *l; + handler_t *h; + int retried = FALSE; + int handled = FALSE; + + MRP_UNUSED(c); + + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL || !member) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + mrp_debug("%s(path='%s', interface='%s', member='%s')...", + __FUNCTION__, + SAFESTR(path), SAFESTR(interface), SAFESTR(member)); + + if ((l = mrp_htbl_lookup(dbus->signals, (void *)member)) != NULL) { + retry: + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + + if (MATCHES(h,path) && MATCHES(h,interface) && MATCHES(h,member)) { + if (m == NULL) + m = create_message(msg); + + if (m == NULL) + goto out; + + h->handler(dbus, m, h->user_data); + handled = TRUE; + } + } + } + + if (!retried) { + if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) { + retried = TRUE; + goto retry; + } + } + + if (!handled) + mrp_debug("Unhandled signal path=%s, %s.%s.", SAFESTR(path), + SAFESTR(interface), SAFESTR(member)); + + out: + mrp_dbus_msg_unref(m); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +#undef MATCHES +#undef SAFESTR +} + + +static int append_args_inttype(DBusMessage *msg, int type, va_list ap) +{ + void *vptr; + void **aptr; + int atype, alen; + int r = TRUE; + + while (type != MRP_DBUS_TYPE_INVALID && r) { + switch (type) { + case MRP_DBUS_TYPE_BYTE: + case MRP_DBUS_TYPE_BOOLEAN: + case MRP_DBUS_TYPE_INT16: + case MRP_DBUS_TYPE_UINT16: + case MRP_DBUS_TYPE_INT32: + case MRP_DBUS_TYPE_UINT32: + case MRP_DBUS_TYPE_INT64: + case MRP_DBUS_TYPE_UINT64: + case MRP_DBUS_TYPE_DOUBLE: + case MRP_DBUS_TYPE_UNIX_FD: + vptr = va_arg(ap, void *); + r = dbus_message_append_args(msg, type, vptr, DBUS_TYPE_INVALID); + break; + + case MRP_DBUS_TYPE_STRING: + case MRP_DBUS_TYPE_OBJECT_PATH: + case MRP_DBUS_TYPE_SIGNATURE: + vptr = va_arg(ap, void *); + r = dbus_message_append_args(msg, type, &vptr, DBUS_TYPE_INVALID); + break; + + case MRP_DBUS_TYPE_ARRAY: + atype = va_arg(ap, int); + aptr = va_arg(ap, void **); + alen = va_arg(ap, int); + r = dbus_message_append_args(msg, DBUS_TYPE_ARRAY, + atype, &aptr, alen, DBUS_TYPE_INVALID); + break; + + default: + return FALSE; + } + + type = va_arg(ap, int); + } + + return r; +} + + +static void call_reply_cb(DBusPendingCall *pend, void *user_data) +{ + call_t *call = (call_t *)user_data; + DBusMessage *reply; + mrp_dbus_msg_t *m; + + reply = dbus_pending_call_steal_reply(pend); + m = create_message(reply); + + call->pend = NULL; + mrp_list_delete(&call->hook); + + call->cb(call->dbus, m, call->user_data); + + mrp_dbus_msg_unref(m); + dbus_message_unref(reply); + dbus_pending_call_unref(pend); + + call_free(call); +} + + +int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, const char *path, + const char *interface, const char *member, int timeout, + mrp_dbus_reply_cb_t cb, void *user_data, int type, ...) +{ + va_list ap; + int32_t id; + call_t *call; + DBusMessage *msg; + DBusPendingCall *pend; + int success; + + call = NULL; + pend = NULL; + + msg = dbus_message_new_method_call(dest, path, interface, member); + + if (msg == NULL) + return 0; + + if (cb != NULL) { + if ((call = mrp_allocz(sizeof(*call))) != NULL) { + mrp_list_init(&call->hook); + + call->dbus = dbus; + call->id = dbus->call_id++; + call->cb = cb; + call->user_data = user_data; + + id = call->id; + } + else + goto fail; + } + else + id = dbus->call_id++; + + if (type == DBUS_TYPE_INVALID) + success = TRUE; + else { + va_start(ap, type); + success = append_args_inttype(msg, type, ap); + va_end(ap); + } + + if (!success) + goto fail; + + if (cb == NULL) { + dbus_message_set_no_reply(msg, TRUE); + if (!dbus_connection_send(dbus->conn, msg, NULL)) + goto fail; + } + else { + if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout)) + goto fail; + + if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL)) + goto fail; + } + + if (cb != NULL) { + mrp_list_append(&dbus->calls, &call->hook); + call->pend = pend; + } + + dbus_message_unref(msg); + + return id; + + fail: + if (pend != NULL) + dbus_pending_call_unref(pend); + + if(msg != NULL) + dbus_message_unref(msg); + + call_free(call); + + return 0; +} + + +int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path, + const char *interface, const char *member, int timeout, + mrp_dbus_reply_cb_t cb, void *user_data, + mrp_dbus_msg_t *m) +{ + int32_t id; + call_t *call; + DBusPendingCall *pend; + DBusMessage *msg; + int method; + + call = NULL; + pend = NULL; + msg = m->msg; + + if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_SIGNAL) { + if (cb != NULL) + goto fail; + else + method = FALSE; + } + else + method = TRUE; + + if (cb != NULL) { + if ((call = mrp_allocz(sizeof(*call))) != NULL) { + mrp_list_init(&call->hook); + + call->dbus = dbus; + call->id = dbus->call_id++; + call->cb = cb; + call->user_data = user_data; + + id = call->id; + } + else + goto fail; + } + else + id = dbus->call_id++; + + if (!dbus_message_set_destination(msg, dest)) + goto fail; + if (!dbus_message_set_path(msg, path)) + goto fail; + if (!dbus_message_set_interface(msg, interface)) + goto fail; + if (!dbus_message_set_member(msg, member)) + goto fail; + + if (cb == NULL) { + if (method) + dbus_message_set_no_reply(msg, TRUE); + if (!dbus_connection_send(dbus->conn, msg, NULL)) + goto fail; + } + else { + if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout)) + goto fail; + + if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL)) + goto fail; + } + + if (cb != NULL) { + mrp_list_append(&dbus->calls, &call->hook); + call->pend = pend; + } + + return id; + + fail: + if (pend != NULL) + dbus_pending_call_unref(pend); + + call_free(call); + + return 0; +} + + +int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *m) +{ + return dbus_connection_send(dbus->conn, m->msg, NULL); +} + + +int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id) +{ + mrp_list_hook_t *p, *n; + call_t *call; + + mrp_list_foreach(&dbus->calls, p, n) { + call = mrp_list_entry(p, call_t, hook); + + if (call->id == id) { + mrp_list_delete(p); + + dbus_pending_call_cancel(call->pend); + dbus_pending_call_unref(call->pend); + call->pend = NULL; + + call_free(call); + return TRUE; + } + } + + return FALSE; +} + + +int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, int type, ...) +{ + va_list ap; + DBusMessage *msg, *rpl; + int success; + + msg = m->msg; + rpl = dbus_message_new_method_return(msg); + + if (rpl == NULL) + return FALSE; + + if (type == DBUS_TYPE_INVALID) + success = TRUE; + else { + va_start(ap, type); + success = append_args_inttype(rpl, type, ap); + va_end(ap); + } + + if (!success) + goto fail; + + if (!dbus_connection_send(dbus->conn, rpl, NULL)) + goto fail; + + dbus_message_unref(rpl); + + return TRUE; + + fail: + if(rpl != NULL) + dbus_message_unref(rpl); + + return FALSE; +} + + +int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, + const char *errname, const char *errmsg, int type, ...) +{ + va_list ap; + DBusMessage *msg, *rpl; + int success; + + msg = m->msg; + rpl = dbus_message_new_error(msg, errname, errmsg); + + if (rpl == NULL) + return FALSE; + + if (type == DBUS_TYPE_INVALID) + success = TRUE; + else { + va_start(ap, type); + success = append_args_inttype(rpl, type, ap); + va_end(ap); + } + + if (!success) + goto fail; + + if (!dbus_connection_send(dbus->conn, rpl, NULL)) + goto fail; + + dbus_message_unref(rpl); + + return TRUE; + + fail: + if(rpl != NULL) + dbus_message_unref(rpl); + + return FALSE; +} + + +static void call_free(call_t *call) +{ + if (call != NULL) + mrp_free(call); +} + + +static void purge_calls(mrp_dbus_t *dbus) +{ + mrp_list_hook_t *p, *n; + call_t *call; + + mrp_list_foreach(&dbus->calls, p, n) { + call = mrp_list_entry(p, call_t, hook); + + mrp_list_delete(&call->hook); + + if (call->pend != NULL) + dbus_pending_call_unref(call->pend); + + mrp_free(call); + } +} + + +int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path, + const char *interface, const char *member, int type, ...) +{ + va_list ap; + DBusMessage *msg; + int success; + + msg = dbus_message_new_signal(path, interface, member); + + if (msg == NULL) + return 0; + + if (type == DBUS_TYPE_INVALID) + success = TRUE; + else { + va_start(ap, type); + success = append_args_inttype(msg, type, ap); + va_end(ap); + } + + if (!success) + goto fail; + + if (dest && *dest && !dbus_message_set_destination(msg, dest)) + goto fail; + + if (!dbus_connection_send(dbus->conn, msg, NULL)) + goto fail; + + dbus_message_unref(msg); + + return TRUE; + + fail: + /* + * XXX TODO: Hmm... IIRC, libdbus unrefs messages upon failure. If it + * was really so, this would corrupt/crash. Check this from + * libdbus code. + */ + if(msg != NULL) + dbus_message_unref(msg); + + return 0; +} + + +mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *bus, + const char *destination, + const char *path, + const char *interface, + const char *member) +{ + mrp_dbus_msg_t *m; + DBusMessage *msg; + + MRP_UNUSED(bus); + + msg = dbus_message_new_method_call(destination, path, interface, member); + + if (msg != NULL) { + m = create_message(msg); + dbus_message_unref(msg); + } + else + m = NULL; + + return m; +} + + +mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *bus, + mrp_dbus_msg_t *m) +{ + mrp_dbus_msg_t *mr; + DBusMessage *msg; + + MRP_UNUSED(bus); + + msg = dbus_message_new_method_return(m->msg); + + if (msg != NULL) { + mr = create_message(msg); + dbus_message_unref(msg); + } + else + mr = NULL; + + return mr; +} + + +mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *bus, mrp_dbus_msg_t *m, + mrp_dbus_err_t *err) +{ + mrp_dbus_msg_t *me; + DBusMessage *msg; + + MRP_UNUSED(bus); + + msg = dbus_message_new_error(m->msg, err->name, err->message); + + if (msg != NULL) { + me = create_message(msg); + dbus_message_unref(msg); + } + else + me = NULL; + + return me; +} + + +mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *bus, + const char *destination, + const char *path, + const char *interface, + const char *member) +{ + mrp_dbus_msg_t *m; + DBusMessage *msg; + + MRP_UNUSED(bus); + + msg = dbus_message_new_signal(path, interface, member); + + if (msg != NULL) { + if (!destination || dbus_message_set_destination(msg, destination)) { + m = create_message(msg); + dbus_message_unref(msg); + } + else { + dbus_message_unref(msg); + m = NULL; + } + } + else + m = NULL; + + return m; +} + + +mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *m) +{ + return (mrp_dbus_msg_type_t)dbus_message_get_type(m->msg); +} + +#define WRAP_GETTER(type, what) \ + type mrp_dbus_msg_##what(mrp_dbus_msg_t *m) \ + { \ + return dbus_message_get_##what(m->msg); \ + } \ + struct __mrp_dbus_allow_trailing_semicolon + +WRAP_GETTER(const char *, path); +WRAP_GETTER(const char *, interface); +WRAP_GETTER(const char *, member); +WRAP_GETTER(const char *, destination); +WRAP_GETTER(const char *, sender); + +#undef WRAP_GETTER + + +static msg_iter_t *message_iterator(mrp_dbus_msg_t *m, int append) +{ + msg_iter_t *it; + + if (mrp_list_empty(&m->iterators)) { + if ((it = mrp_allocz(sizeof(*it))) != NULL) { + mrp_list_init(&it->hook); + mrp_list_append(&m->iterators, &it->hook); + + if (append) + dbus_message_iter_init_append(m->msg, &it->it); + else + dbus_message_iter_init(m->msg, &it->it); + } + } + else + it = mrp_list_entry(&m->iterators.next, typeof(*it), hook); + + return it; +} + + +msg_iter_t *current_iterator(mrp_dbus_msg_t *m) +{ + msg_iter_t *it; + + if (!mrp_list_empty(&m->iterators)) + it = mrp_list_entry(m->iterators.prev, typeof(*it), hook); + else + it = NULL; + + return it; +} + + +int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type, + const char *contents) +{ + msg_iter_t *it, *parent; + + if ((parent = current_iterator(m)) == NULL && + (parent = message_iterator(m, TRUE)) == NULL) + return FALSE; + + if ((it = mrp_allocz(sizeof(*it))) != NULL) { + mrp_list_init(&it->hook); + + if (dbus_message_iter_open_container(&parent->it, type, contents, + &it->it)) { + mrp_list_append(&m->iterators, &it->hook); + + return TRUE; + } + + mrp_free(it); + } + + return FALSE; +} + + +int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m) +{ + msg_iter_t *it, *parent; + int r; + + it = current_iterator(m); + + if (it == NULL || it == message_iterator(m, FALSE)) + return FALSE; + + mrp_list_delete(&it->hook); + + if ((parent = current_iterator(m)) != NULL) + r = dbus_message_iter_close_container(&parent->it, &it->it); + else + r = FALSE; + + mrp_free(it); + + return r; +} + + +int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep) +{ + msg_iter_t *it; + + if (!dbus_type_is_basic(type)) + return FALSE; + + if ((it = current_iterator(m)) != NULL || + (it = message_iterator(m, TRUE)) != NULL) { + if (type != MRP_DBUS_TYPE_STRING && + type != MRP_DBUS_TYPE_OBJECT_PATH && + type != MRP_DBUS_TYPE_SIGNATURE) + return dbus_message_iter_append_basic(&it->it, type, valuep); + else + return dbus_message_iter_append_basic(&it->it, type, &valuep); + } + else + return FALSE; +} + + +int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *m, char type, + const char *contents) +{ + msg_iter_t *it, *parent; + char *signature; + + if ((parent = current_iterator(m)) == NULL && + (parent = message_iterator(m, FALSE)) == NULL) + return FALSE; + + if (dbus_message_iter_get_arg_type(&parent->it) != type) + return FALSE; + + if ((it = mrp_allocz(sizeof(*it))) != NULL) { + mrp_list_init(&it->hook); + mrp_list_append(&m->iterators, &it->hook); + + dbus_message_iter_recurse(&parent->it, &it->it); + + if (contents != NULL) { + /* XXX TODO: proper signature checking */ + signature = dbus_message_iter_get_signature(&it->it); + if (strcmp(contents, signature)) + mrp_log_error("*** %s(): signature mismath ('%s' != '%s')", + __FUNCTION__, contents, signature); + mrp_free(signature); + } + + dbus_message_iter_next(&parent->it); + + return TRUE; + } + + return FALSE; +} + + +int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m) +{ + msg_iter_t *it; + + if ((it = current_iterator(m)) == NULL || it == message_iterator(m, FALSE)) + return FALSE; + + mrp_list_delete(&it->hook); + + mrp_free(it->peeked); + mrp_free(it); + + return TRUE; +} + + +int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep) +{ + msg_iter_t *it; + + if (!dbus_type_is_basic(type)) + return FALSE; + + if ((it = current_iterator(m)) != NULL || + (it = message_iterator(m, FALSE)) != NULL) { + if (dbus_message_iter_get_arg_type(&it->it) == type) { + dbus_message_iter_get_basic(&it->it, valuep); + dbus_message_iter_next(&it->it); + + return TRUE; + } + } + + return FALSE; +} + + +static void free_msg_array(msg_array_t *a) +{ + if (a == NULL) + return; + + mrp_list_delete(&a->hook); + mrp_free(a->items); + mrp_free(a); +} + + +int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type, + void **itemsp, size_t *nitemp) +{ + msg_iter_t *it; + msg_array_t *a; + DBusMessageIter sub; + void *items; + int nitem, atype; + + if (!dbus_type_is_basic(type)) + return FALSE; + + if ((it = current_iterator(m)) == NULL && + (it = message_iterator(m, FALSE)) == NULL) + return FALSE; + + if (dbus_message_iter_get_arg_type(&it->it) != DBUS_TYPE_ARRAY) + return FALSE; + + dbus_message_iter_recurse(&it->it, &sub); + atype = dbus_message_iter_get_arg_type(&sub); + + if (atype == MRP_DBUS_TYPE_INVALID) { + items = NULL; + nitem = 0; + + goto out; + } + + if (atype != type) + return FALSE; + + /* for fixed types, just use the libdbus function */ + if (type != MRP_DBUS_TYPE_STRING && type != MRP_DBUS_TYPE_OBJECT_PATH) { + nitem = -1; + items = NULL; + dbus_message_iter_get_fixed_array(&sub, (void *)&items, &nitem); + + if (nitem == -1) + return FALSE; + } + /* for string-like types, collect items into an implicitly freed array */ + else { + a = mrp_allocz(sizeof(*a)); + + if (a == NULL) + return FALSE; + + mrp_list_init(&a->hook); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + if (mrp_reallocz(a->items, a->nitem, a->nitem + 1) != NULL) { + dbus_message_iter_get_basic(&sub, a->items + a->nitem); + a->nitem++; + dbus_message_iter_next(&sub); + } + else { + free_msg_array(a); + return FALSE; + } + } + + mrp_list_append(&m->arrays, &a->hook); + + items = a->items; + nitem = a->nitem; + } + + out: + dbus_message_iter_next(&it->it); + + *itemsp = items; + *nitemp = (size_t)nitem; + + return TRUE; +} + + +mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents) +{ + msg_iter_t *it; + DBusMessageIter sub; + char type; + + if ((it = current_iterator(m)) != NULL || + (it = message_iterator(m, FALSE)) != NULL) { + type = dbus_message_iter_get_arg_type(&it->it); + + if (dbus_type_is_container(type)) { + mrp_free(it->peeked); + + if (contents != NULL) { + dbus_message_iter_recurse(&it->it, &sub); + it->peeked = dbus_message_iter_get_signature(&sub); + *contents = it->peeked; + } + } + + return type; + } + + return MRP_DBUS_TYPE_INVALID; +} diff --git a/src/common/dbus-libdbus.h b/src/common/dbus-libdbus.h new file mode 100644 index 0000000..8c729d9 --- /dev/null +++ b/src/common/dbus-libdbus.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2012, 2013, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MURPHY_DBUS_LIBDBUS_H__ +#define __MURPHY_DBUS_LIBDBUS_H__ + +#include +#include +#include + +/** Type for a D-Bus (connection). */ +struct mrp_dbus_s; +typedef struct mrp_dbus_s mrp_dbus_t; + +/** Type for a D-Bus message. */ +struct mrp_dbus_msg_s; +typedef struct mrp_dbus_msg_s mrp_dbus_msg_t; + +/** Type for a D-Bus error. */ +typedef DBusError mrp_dbus_err_t; + +/** D-BUS method or signal callback type. */ +typedef int (*mrp_dbus_handler_t)(mrp_dbus_t *, mrp_dbus_msg_t *, void *); + +/** Create a new connection to the given bus. */ +mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address, + mrp_dbus_err_t *errp); +#define mrp_dbus_get mrp_dbus_connect + +/** Set up a DBusConnection with a mainloop. */ +int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn); + +/** Increase the reference count of the given DBus (connection). */ +mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus); + +/** Decrease the reference count of the given DBus (connection). */ +int mrp_dbus_unref(mrp_dbus_t *dbus); + +/** Acquire the given name on the given bus (connection). */ +int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_err_t *error); + +/** Release the given name on the given bus (connection). */ +int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_err_t *error); + +/** Type for a name tracking callback. */ +typedef void (*mrp_dbus_name_cb_t)(mrp_dbus_t *, const char *, int, + const char *, void *); +/** Start tracking the given name. */ +int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_name_cb_t cb, void *user_data); +/** Stop tracking the given name. */ +int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_name_cb_t cb, void *user_data); + +/** Export a method to the bus. */ +int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data); + +/** Remove an exported method. */ +int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data); + +/** Install a filter and add a handler for the given signal on the bus. */ +MRP_NULLTERM int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus, + mrp_dbus_handler_t handler, + void *user_data, + const char *sender, + const char *path, + const char *interface, + const char *member, ...); + +/** Remove the signal handler and filter for the given signal on the bus. */ +MRP_NULLTERM int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus, + mrp_dbus_handler_t handler, + void *user_data, + const char *sender, + const char *path, + const char *interface, + const char *member, ...); + +/** Install a filter for the given message on the bus. */ +MRP_NULLTERM int mrp_dbus_install_filter(mrp_dbus_t *dbus, + const char *sender, + const char *path, + const char *interface, + const char *member, ...); + +/** Install a filter for the given message on the bus. */ +int mrp_dbus_install_filterv(mrp_dbus_t *dbus, + const char *sender, + const char *path, + const char *interface, + const char *member, + va_list ap); + +/** Remove a filter for the given message on the bus. */ +MRP_NULLTERM int mrp_dbus_remove_filter(mrp_dbus_t *dbus, + const char *sender, + const char *path, + const char *interface, + const char *member, ...); + +/** Remove a filter for the given message on the bus. */ +int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, + const char *sender, + const char *path, + const char *interface, + const char *member, + va_list ap); + +/** Add a signal handler for the gvien signal on the bus. */ +int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, mrp_dbus_handler_t handler, + void *user_data); + +/** Remove the given signal handler for the given signal on the bus. */ +int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, mrp_dbus_handler_t handler, + void *user_data); + +/** Type of a method call reply callback. */ +typedef void (*mrp_dbus_reply_cb_t)(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply, + void *user_data); + +/** Call the given method on the bus. */ +int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, + const char *path, const char *interface, + const char *member, int timeout, + mrp_dbus_reply_cb_t cb, void *user_data, + int dbus_type, ...); + +/** Cancel an ongoing method call on the bus. */ +int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id); + +/** Send a reply to the given method call on the bus. */ +int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, int type, ...); + +/** Send an error reply to the given method call on the bus. */ +int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, + const char *errname, const char *errmsg, + int type, ...); + +/** Emit the given signal on the bus. */ +int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path, + const char *interface, const char *member, int type, ...); + +/** Send the given method call message on the bus. */ +int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path, + const char *interface, const char *member, int timeout, + mrp_dbus_reply_cb_t cb, void *user_data, + mrp_dbus_msg_t *msg); + +/** Send the given message on the bus. */ +int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg); + +/** Get our unique name on the bus. */ +const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus); + +/** Initialize the given error. */ +static inline mrp_dbus_err_t *mrp_dbus_error_init(mrp_dbus_err_t *err) +{ + if (err != NULL) + dbus_error_init(err); + + return err; +} + +/** Set the given error buffer up with the error name and message. */ +static inline mrp_dbus_err_t *mrp_dbus_error_set(mrp_dbus_err_t *err, + const char *name, + const char *message) +{ + dbus_set_error_const(err, name, message); + + return err; +} + + +/** Get the error message from the given bus error message. */ +static inline const char *mrp_dbus_errmsg(mrp_dbus_err_t *err) +{ + if (err && dbus_error_is_set(err)) + return err->message; + else + return "unknown DBUS error"; +} + + +/** Increase the reference count of a message. */ +mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m); + +/** Decrease the reference count of a message, freeing it if necessary. */ +int mrp_dbus_msg_unref(mrp_dbus_msg_t *m); + + +/** Create a new method call message. */ +mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *bus, + const char *destination, + const char *path, + const char *interface, + const char *member); + +/** Create a new method return message. */ +mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *bus, + mrp_dbus_msg_t *msg); + +/** Create a new error reply message. */ +mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *bus, mrp_dbus_msg_t *msg, + mrp_dbus_err_t *err); + +/** Create a new signal message. */ +mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *bus, + const char *destination, + const char *path, + const char *interface, + const char *member); + +/** Bus message types. */ +typedef enum { +# define TYPE(type) MRP_DBUS_MESSAGE_TYPE_##type = DBUS_MESSAGE_TYPE_##type + TYPE(INVALID), + TYPE(METHOD_CALL), + TYPE(METHOD_RETURN), + TYPE(ERROR), + TYPE(SIGNAL) +# undef TYPE +} mrp_dbus_msg_type_t; + +/** Get the type of the given message. */ +mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *msg); + +/** Message type checking convenience functions. */ +#define TYPE_CHECK_FUNCTION(type, TYPE) \ + static inline int mrp_dbus_msg_is_##type(mrp_dbus_msg_t *msg) \ + { \ + return mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_##TYPE; \ + } \ + struct __mrp_dbus_allow_traling_semicolon + +TYPE_CHECK_FUNCTION(method_call , METHOD_CALL); +TYPE_CHECK_FUNCTION(method_return, METHOD_RETURN); +TYPE_CHECK_FUNCTION(error , ERROR); +TYPE_CHECK_FUNCTION(signal , SIGNAL); + +/** Message argument types. */ +typedef enum { +#define TYPE(t) MRP_DBUS_TYPE_##t = DBUS_TYPE_##t + TYPE(INVALID), + TYPE(BYTE), + TYPE(BOOLEAN), + TYPE(INT16), + TYPE(UINT16), + TYPE(INT32), + TYPE(UINT32), + TYPE(INT64), + TYPE(UINT64), + TYPE(DOUBLE), + TYPE(STRING), + TYPE(OBJECT_PATH), + TYPE(SIGNATURE), + TYPE(UNIX_FD), + TYPE(ARRAY), + TYPE(VARIANT), + TYPE(STRUCT), + TYPE(DICT_ENTRY), +#undef TYPE +} mrp_dbus_type_t; + +/** Message argument types as strings. */ +#define MRP_DBUS_TYPE_BYTE_AS_STRING DBUS_TYPE_BYTE_AS_STRING +#define MRP_DBUS_TYPE_BOOLEAN_AS_STRING DBUS_TYPE_BOOLEAN_AS_STRING +#define MRP_DBUS_TYPE_INT16_AS_STRING DBUS_TYPE_INT16_AS_STRING +#define MRP_DBUS_TYPE_UINT16_AS_STRING DBUS_TYPE_UINT16_AS_STRING +#define MRP_DBUS_TYPE_INT32_AS_STRING DBUS_TYPE_INT32_AS_STRING +#define MRP_DBUS_TYPE_UINT32_AS_STRING DBUS_TYPE_UINT32_AS_STRING +#define MRP_DBUS_TYPE_INT64_AS_STRING DBUS_TYPE_INT64_AS_STRING +#define MRP_DBUS_TYPE_UINT64_AS_STRING DBUS_TYPE_UINT64_AS_STRING +#define MRP_DBUS_TYPE_DOUBLE_AS_STRING DBUS_TYPE_DOUBLE_AS_STRING +#define MRP_DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING +#define MRP_DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING +#define MRP_DBUS_TYPE_SIGNATURE_AS_STRING DBUS_TYPE_SIGNATURE_AS_STRING +#define MRP_DBUS_TYPE_UNIX_FD_AS_STRING DBUS_TYPE_UNIX_FD_AS_STRING +#define MRP_DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_ARRAY_AS_STRING +#define MRP_DBUS_TYPE_VARIANT_AS_STRING DBUS_TYPE_VARIANT_AS_STRING +#define MRP_DBUS_TYPE_STRUCT_AS_STRING DBUS_TYPE_STRUCT_AS_STRING +#define MRP_DBUS_TYPE_DICT_ENTRY_AS_STRING DBUS_TYPE_DICT_ENTRY_AS_STRING + +/** Get the path of the given message. */ +const char *mrp_dbus_msg_path(mrp_dbus_msg_t *msg); + +/** Get the interface of the given message. */ +const char *mrp_dbus_msg_interface(mrp_dbus_msg_t *msg); + +/** Get the member of the given message. */ +const char *mrp_dbus_msg_member(mrp_dbus_msg_t *msg); + +/** Get the destination of the given message. */ +const char *mrp_dbus_msg_destination(mrp_dbus_msg_t *msg); + +/** Get the sender of the given message. */ +const char *mrp_dbus_msg_sender(mrp_dbus_msg_t *msg); + +/** Open a new container of the given type and cotained types. */ +int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type, + const char *contents); + +/** Close the current container. */ +int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m); + +/** Append an argument of a basic type to the given message. */ +int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep); + +/** Get the type of the current message argument. */ +mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents); + +/** Open the current container (of the given type and contents) for reading. */ +int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *msg, char type, + const char *contents); + +/** Exit from the container being currently read. */ +int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m); + +/** Read the next argument (of basic type) from the given message. */ +int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep); + +/** Read the next array of one of the basic types. */ +int mrp_dbus_msg_read_array(mrp_dbus_msg_t *msg, char type, + void **itemsp, size_t *nitemp); + +#endif /* __MURPHY_DBUS_LIBDBUS_H__ */ diff --git a/src/common/dbus-sdbus-glue.c b/src/common/dbus-sdbus-glue.c new file mode 100644 index 0000000..a2b98c6 --- /dev/null +++ b/src/common/dbus-sdbus-glue.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2012, 2013, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include + +#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000)) +#define MSEC_TO_USEC(msec) ((uint64_t)(msec) * 1000) + +typedef struct { + sd_bus *bus; + mrp_mainloop_t *ml; + mrp_subloop_t *sl; + int events; +} bus_glue_t; + + +static int bus_prepare(void *user_data) +{ + MRP_UNUSED(user_data); + + return FALSE; +} + + +static int bus_query(void *user_data, struct pollfd *fds, int nfd, int *timeout) +{ + bus_glue_t *b = (bus_glue_t *)user_data; + uint64_t usec; + + if (nfd > 0) { + fds[0].fd = sd_bus_get_fd(b->bus); + fds[0].events = sd_bus_get_events(b->bus) | POLLIN | POLLHUP; + fds[0].revents = 0; + + if (sd_bus_get_timeout(b->bus, &usec) < 0) + *timeout = -1; + else + *timeout = USEC_TO_MSEC(usec); + + mrp_debug("fd: %d, events: 0x%x, timeout: %u", fds[0].fd, + fds[0].events, *timeout); + } + + return 1; +} + + +static int bus_check(void *user_data, struct pollfd *fds, int nfd) +{ + bus_glue_t *b = (bus_glue_t *)user_data; + + if (nfd > 0) { + b->events = fds[0].revents; + + if (b->events != 0) + return TRUE; + } + else + b->events = 0; + + return FALSE; +} + + +static void bus_dispatch(void *user_data) +{ + bus_glue_t *b = (bus_glue_t *)user_data; + + mrp_debug("dispatching events 0x%x to sd_bus %p", b->events, b->bus); + + if (b->events & MRP_IO_EVENT_HUP) + mrp_debug("sd_bus peer has closed the connection"); + + while (sd_bus_process(b->bus, NULL) > 0) + sd_bus_flush(b->bus); + + mrp_debug("done dispatching"); +} + + +int mrp_dbus_setup_with_mainloop(mrp_mainloop_t *ml, sd_bus *bus) +{ + static mrp_subloop_ops_t bus_ops = { + .prepare = bus_prepare, + .query = bus_query, + .check = bus_check, + .dispatch = bus_dispatch + }; + + bus_glue_t *b; + + + if ((b = mrp_allocz(sizeof(*b))) != NULL) { + /* XXX TODO: Hmm... is this really needed ? */ + while (sd_bus_process(bus, NULL) > 0) + sd_bus_flush(bus); + + b->bus = bus; + b->ml = ml; + b->sl = mrp_add_subloop(ml, &bus_ops, b); + + if (b->sl != NULL) + return TRUE; + else + mrp_free(b); + } + + return FALSE; +} diff --git a/src/common/dbus-sdbus-transport.c b/src/common/dbus-sdbus-transport.c new file mode 100644 index 0000000..baa6847 --- /dev/null +++ b/src/common/dbus-sdbus-transport.c @@ -0,0 +1,1781 @@ +/* + * Copyright (c) 2012, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DBUS "dbus" +#define DBUSL 4 + +#define TRANSPORT_PATH "/murphy/transport" +#define TRANSPORT_INTERFACE "Murphy.Transport" +#define TRANSPORT_MESSAGE "DeliverMessage" +#define TRANSPORT_DATA "DeliverData" +#define TRANSPORT_RAW "DeliverRaw" +#define TRANSPORT_METHOD "DeliverMessage" + +#define ANY_ADDRESS "any" + +typedef struct { + MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */ + mrp_dbus_t *dbus; /* D-BUS connection */ + int bound : 1; /* whether bound to an address */ + int peer_resolved : 1; /* connected and peer name known */ + mrp_dbusaddr_t local; /* address we're bound to */ + mrp_dbusaddr_t remote; /* address we're connected to */ +} dbus_t; + + +static uint32_t nauto; /* for autobinding */ + + +static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data); +static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data); +static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data); + +static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up, + const char *owner, void *user_data); + +static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + mrp_msg_t *msg); +static mrp_msg_t *msg_decode(mrp_dbus_msg_t *msg, const char **sender_id); + +static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + void *data, uint16_t tag); +static void *data_decode(mrp_dbus_msg_t *msg, uint16_t *tag, + const char **sender_id); + +static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + void *data, size_t size); +static void *raw_decode(mrp_dbus_msg_t *msg, size_t *sizep, + const char **sender_id); + + +static socklen_t parse_address(const char *str, mrp_dbusaddr_t *addr, + socklen_t size) +{ + const char *p, *e; + char *q; + size_t l, n; + + if (size < sizeof(*addr)) { + errno = EINVAL; + return FALSE; + } + + if (strncmp(str, DBUS":", DBUSL + 1)) + return 0; + else + str += DBUSL + 1; + + /* + * The format of the address is + * dbus:[bus-address]@address/path + * eg. + * dbus:[session]@:1.33/client1, or + * dbus:[unix:abstract=/tmp/dbus-Xx2Kpi...a572]@:1.33/client1 + */ + + p = str; + q = addr->db_fqa; + l = sizeof(addr->db_fqa); + + /* get bus address */ + if (*p != '[') { + errno = EINVAL; + return 0; + } + else + p++; + + e = strchr(p, ']'); + + if (e == NULL) { + errno = EINVAL; + return 0; + } + + n = e - p; + if (n >= l) { + errno = ENAMETOOLONG; + return 0; + } + + /* save bus address */ + strncpy(q, p, n); + q[n] = '\0'; + addr->db_bus = q; + + q += n + 1; + l -= n + 1; + p = e + 1; + + /* get (local or remote) address on bus */ + if (*p != '@') + addr->db_addr = ANY_ADDRESS; + else { + p++; + e = strchr(p, '/'); + + if (e == NULL) { + errno = EINVAL; + return 0; + } + + n = e - p; + if (n >= l) { + errno = ENAMETOOLONG; + return 0; + } + + /* save address on bus */ + strncpy(q, p, n); + q[n] = '\0'; + addr->db_addr = q; + + q += n + 1; + l -= n + 1; + p = e; + } + + /* get object path */ + if (*p != '/') { + errno = EINVAL; + return 0; + } + + n = snprintf(q, l, "%s%s", TRANSPORT_PATH, p); + if (n >= l) { + errno = ENAMETOOLONG; + return 0; + } + + addr->db_path = q; + addr->db_family = MRP_AF_DBUS; + + return sizeof(*addr); +} + + +static mrp_dbusaddr_t *copy_address(mrp_dbusaddr_t *dst, mrp_dbusaddr_t *src) +{ + char *p, *q; + size_t l, n; + + dst->db_family = src->db_family; + + /* copy bus address */ + p = src->db_bus; + q = dst->db_fqa; + l = sizeof(dst->db_fqa); + + n = strlen(p); + if (l < n + 1) { + errno = EOVERFLOW; + return NULL; + } + + dst->db_bus = q; + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + /* copy address */ + p = src->db_addr; + n = strlen(p); + if (l < n + 1) { + errno = EOVERFLOW; + return NULL; + } + + dst->db_addr = q; + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + /* copy path */ + p = src->db_path; + n = strlen(p); + if (l < n + 1) { + errno = EOVERFLOW; + return NULL; + } + + dst->db_path = q; + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + return dst; +} + + +static inline int check_address(mrp_sockaddr_t *addr, socklen_t addrlen) +{ + mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addr; + + return (a->db_family = MRP_AF_DBUS && addrlen == sizeof(*a)); +} + + +static size_t peer_address(mrp_sockaddr_t *addrp, const char *sender, + const char *path) +{ + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + const char *p; + char *q; + int l, n; + + q = addr->db_fqa; + l = sizeof(addr->db_fqa); + p = ANY_ADDRESS; + n = 3; + + addr->db_bus = q; + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + addr->db_addr = q; + p = sender; + n = strlen(sender); + + if (l < n + 1) + return 0; + + memcpy(q, p, n + 1); + q += n + 1; + l -= n + 1; + + addr->db_path = q; + p = path; + n = strlen(p); + + if (l < n + 1) + return 0; + + memcpy(q, p, n + 1); + + return sizeof(addrp); +} + + +static socklen_t dbus_resolve(const char *str, mrp_sockaddr_t *addr, + socklen_t size, const char **typep) +{ + socklen_t len; + + len = parse_address(str, (mrp_dbusaddr_t *)addr, size); + + if (len > 0) { + if (typep != NULL) + *typep = DBUS; + } + + return len; +} + + +static int dbus_open(mrp_transport_t *mt) +{ + MRP_UNUSED(mt); + + return TRUE; +} + + +static int dbus_createfrom(mrp_transport_t *mt, void *conn) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbus_t *dbus = (mrp_dbus_t *)conn; + + t->dbus = mrp_dbus_ref(dbus); + + if (t->dbus != NULL) + return TRUE; + else + return FALSE; +} + + +static int dbus_bind(mrp_transport_t *mt, mrp_sockaddr_t *addrp, + socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbus_t *dbus = NULL; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *); + const char *method; + + if (t->bound) { + errno = EINVAL; + goto fail; + } + + if (!check_address(addrp, addrlen)) { + errno = EINVAL; + goto fail; + } + + if (t->dbus == NULL) { + dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL); + + if (dbus == NULL) { + errno = ECONNRESET; + goto fail; + } + else { + t->dbus = dbus; + + if (addr->db_addr != NULL && strcmp(addr->db_addr, ANY_ADDRESS)) { + if (!mrp_dbus_acquire_name(t->dbus, addr->db_addr, NULL)) { + errno = EADDRINUSE; /* XXX TODO, should check error... */ + goto fail; + } + } + } + } + else { + /* XXX TODO: should check given address against address of the bus */ + } + + copy_address(&t->local, addr); + + switch (t->mode) { + case MRP_TRANSPORT_MODE_DATA: + method = TRANSPORT_DATA; + cb = dbus_data_cb; + break; + case MRP_TRANSPORT_MODE_RAW: + method = TRANSPORT_RAW; + cb = dbus_raw_cb; + break; + case MRP_TRANSPORT_MODE_MSG: + method = TRANSPORT_MESSAGE; + cb = dbus_msg_cb; + break; + default: + errno = EPROTOTYPE; + goto fail; + } + + if (!mrp_dbus_export_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE, + method, cb, t)) { + errno = EIO; + goto fail; + } + else { + t->bound = TRUE; + return TRUE; + } + + fail: + if (dbus != NULL) { + mrp_dbus_unref(dbus); + t->dbus = NULL; + } + + return FALSE; +} + + +static int dbus_autobind(mrp_transport_t *mt, mrp_sockaddr_t *addrp) +{ + mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addrp; + char astr[MRP_SOCKADDR_SIZE]; + mrp_sockaddr_t addr; + socklen_t alen; + + snprintf(astr, sizeof(astr), "dbus:[%s]/auto/%u", a->db_bus, nauto++); + + alen = dbus_resolve(astr, &addr, sizeof(addr), NULL); + + if (alen > 0) + return dbus_bind(mt, &addr, alen); + else + return FALSE; +} + + +static void dbus_close(mrp_transport_t *mt) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr; + const char *method; + int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *); + + if (t->bound) { + switch (t->mode) { + case MRP_TRANSPORT_MODE_DATA: + method = TRANSPORT_DATA; + cb = dbus_data_cb; + break; + case MRP_TRANSPORT_MODE_RAW: + method = TRANSPORT_RAW; + cb = dbus_raw_cb; + break; + default: + case MRP_TRANSPORT_MODE_MSG: + method = TRANSPORT_MESSAGE; + cb = dbus_msg_cb; + } + + addr = (mrp_dbusaddr_t *)&t->local; + mrp_dbus_remove_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE, + method, cb, t); + } + + if (t->connected && t->remote.db_addr != NULL) + mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t); + + mrp_dbus_unref(t->dbus); + t->dbus = NULL; +} + + +static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data) +{ + mrp_transport_t *mt = (mrp_transport_t *)user_data; + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t addr; + socklen_t alen; + const char *sender, *sender_path; + mrp_msg_t *msg; + + MRP_UNUSED(dbus); + + msg = msg_decode(dmsg, &sender_path); + + if (msg != NULL) { + sender = mrp_dbus_msg_sender(dmsg); + + if (mt->connected) { + if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender)) + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvmsg(mt, msg, mt->user_data); + }); + } + else { + peer_address(&addr, sender, sender_path); + alen = sizeof(addr); + + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvmsgfrom(mt, msg, &addr, alen, mt->user_data); + }); + } + + mrp_msg_unref(msg); + + mt->check_destroy(mt); + } + else { + mrp_log_error("Failed to decode message."); + } + + return TRUE; +} + + +static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data) +{ + mrp_transport_t *mt = (mrp_transport_t *)user_data; + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t addr; + socklen_t alen; + const char *sender, *sender_path; + uint16_t tag; + void *decoded; + + MRP_UNUSED(dbus); + + decoded = data_decode(dmsg, &tag, &sender_path); + + if (decoded != NULL) { + sender = mrp_dbus_msg_sender(dmsg); + + if (mt->connected) { + if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender)) + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvdata(mt, decoded, tag, mt->user_data); + }); + } + else { + peer_address(&addr, sender, sender_path); + alen = sizeof(addr); + + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen, + mt->user_data); + }); + } + + mt->check_destroy(mt); + } + else { + mrp_log_error("Failed to decode custom data message."); + } + + return TRUE; +} + + +static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data) +{ + mrp_transport_t *mt = (mrp_transport_t *)user_data; + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t addr; + socklen_t alen; + const char *sender, *sender_path; + void *data; + size_t size; + + MRP_UNUSED(dbus); + + data = raw_decode(dmsg, &size, &sender_path); + + if (data != NULL) { + sender = mrp_dbus_msg_sender(dmsg); + + if (mt->connected) { + if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender)) + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvraw(mt, data, size, mt->user_data); + }); + } + else { + peer_address(&addr, sender, sender_path); + alen = sizeof(addr); + + MRP_TRANSPORT_BUSY(mt, { + mt->evt.recvrawfrom(mt, data, size, &addr, alen, + mt->user_data); + }); + } + + mt->check_destroy(mt); + } + else { + mrp_log_error("Failed to decode raw message."); + } + + return TRUE; +} + + +static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up, + const char *owner, void *user_data) +{ + dbus_t *t = (dbus_t *)user_data; + mrp_sockaddr_t addr; + + MRP_UNUSED(dbus); + MRP_UNUSED(name); + + if (up) { + peer_address(&addr, owner, t->remote.db_path); + copy_address(&t->remote, (mrp_dbusaddr_t *)&addr); + t->peer_resolved = TRUE; + } + else { + /* + * XXX TODO: + * It would be really tempting here to call + * mt->evt.closed(mt, ECONNRESET, mt->user_data) + * to notify the user about the fact our peer went down. + * However, that would not be in line with the other + * transports which call the closed event handler only + * upon foricble transport closes upon errors. + * + * The transport interface abstraction (especially the + * available set of events) anyway needs some eyeballing, + * so the right thing to do might be to define a new event + * for disconnection and call the handler for that here... + */ + } + +} + + +static int dbus_connect(mrp_transport_t *mt, mrp_sockaddr_t *addrp, + socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + + if (!check_address(addrp, addrlen)) { + errno = EINVAL; + return FALSE; + } + + if (t->dbus == NULL) { + t->dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL); + + if (t->dbus == NULL) { + errno = ECONNRESET; + return FALSE; + } + } + else { + /* XXX TODO: check given address against address of the bus */ + } + + if (!t->bound) + if (!dbus_autobind(mt, addrp)) + return FALSE; + + if (mrp_dbus_follow_name(t->dbus, addr->db_addr, peer_state_cb, t)) { + copy_address(&t->remote, addr); + + return TRUE; + } + else + return FALSE; +} + + +static int dbus_disconnect(mrp_transport_t *mt) +{ + dbus_t *t = (dbus_t *)mt; + + if (t->connected && t->remote.db_addr != NULL) { + mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t); + mrp_clear(&t->remote); + t->peer_resolved = FALSE; + } + + return TRUE; +} + + +static int dbus_sendmsgto(mrp_transport_t *mt, mrp_msg_t *msg, + mrp_sockaddr_t *addrp, socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + mrp_dbus_msg_t *m; + int success; + + if (check_address(addrp, addrlen)) { + if (t->dbus == NULL && !dbus_autobind(mt, addrp)) + return FALSE; + + m = msg_encode(t->dbus, addr->db_addr, addr->db_path, + TRANSPORT_INTERFACE, TRANSPORT_MESSAGE, + t->local.db_path, msg); + + if (m != NULL) { + if (mrp_dbus_send_msg(t->dbus, m)) + success = TRUE; + else { + errno = ECOMM; + success = FALSE; + } + + mrp_dbus_msg_unref(m); + } + else + success = FALSE; + } + else { + errno = EINVAL; + success = FALSE; + } + + return success; +} + + +static int dbus_sendmsg(mrp_transport_t *mt, mrp_msg_t *msg) +{ + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote; + socklen_t alen = sizeof(t->remote); + + return dbus_sendmsgto(mt, msg, addr, alen); +} + + +static int dbus_sendrawto(mrp_transport_t *mt, void *data, size_t size, + mrp_sockaddr_t *addrp, socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + mrp_dbus_msg_t *m; + int success; + + + MRP_UNUSED(mt); + MRP_UNUSED(data); + MRP_UNUSED(size); + MRP_UNUSED(addr); + MRP_UNUSED(addrlen); + + if (check_address(addrp, addrlen)) { + if (t->dbus == NULL && !dbus_autobind(mt, addrp)) + return FALSE; + + m = raw_encode(t->dbus, addr->db_addr, addr->db_path, + TRANSPORT_INTERFACE, TRANSPORT_RAW, + t->local.db_path, data, size); + + if (m != NULL) { + if (mrp_dbus_send_msg(t->dbus, m)) + success = TRUE; + else { + errno = ECOMM; + success = FALSE; + } + + mrp_dbus_msg_unref(m); + } + else + success = FALSE; + } + else { + errno = EINVAL; + success = FALSE; + } + + return success; +} + + +static int dbus_sendraw(mrp_transport_t *mt, void *data, size_t size) +{ + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote; + socklen_t alen = sizeof(t->remote); + + return dbus_sendrawto(mt, data, size, addr, alen); +} + + +static int dbus_senddatato(mrp_transport_t *mt, void *data, uint16_t tag, + mrp_sockaddr_t *addrp, socklen_t addrlen) +{ + dbus_t *t = (dbus_t *)mt; + mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp; + mrp_dbus_msg_t *m; + int success; + + if (check_address(addrp, addrlen)) { + if (t->dbus == NULL && !dbus_autobind(mt, addrp)) + return FALSE; + + m = data_encode(t->dbus, addr->db_addr, addr->db_path, + TRANSPORT_INTERFACE, TRANSPORT_DATA, + t->local.db_path, data, tag); + + if (m != NULL) { + if (mrp_dbus_send_msg(t->dbus, m)) + success = TRUE; + else { + errno = ECOMM; + success = FALSE; + } + + mrp_dbus_msg_unref(m); + } + else + success = FALSE; + } + else { + errno = EINVAL; + success = FALSE; + } + + return success; +} + + +static int dbus_senddata(mrp_transport_t *mt, void *data, uint16_t tag) +{ + dbus_t *t = (dbus_t *)mt; + mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote; + socklen_t alen = sizeof(t->remote); + + return dbus_senddatato(mt, data, tag, addr, alen); +} + + +static const char *get_array_signature(uint16_t type) +{ +#define MAP(from, to) \ + case MRP_MSG_FIELD_##from: \ + return MRP_DBUS_TYPE_##to##_AS_STRING; + + switch (type) { + MAP(STRING, STRING); + MAP(BOOL , BOOLEAN); + MAP(UINT8 , UINT16); + MAP(SINT8 , INT16); + MAP(UINT16, UINT16); + MAP(SINT16, INT16); + MAP(UINT32, UINT32); + MAP(SINT32, INT32); + MAP(UINT64, UINT64); + MAP(SINT64, INT64); + MAP(DOUBLE, DOUBLE); + MAP(BLOB , BYTE); + default: + return NULL; + } +} + + +static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + mrp_msg_t *msg) +{ + /* + * Notes: There is a type mismatch between our and DBUS types for + * 8-bit integers (D-BUS does not have a signed 8-bit type) + * and boolean types (D-BUS has uint32_t booleans, C99 fails + * to specify the type and gcc uses a signed char). The + * QUIRKY versions of the macros take care of these mismatches. + */ + +#define BASIC_SIMPLE(_i, _mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = &(_val); \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + +#define BASIC_STRING(_i, _mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = (_val); \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + +#define BASIC_QUIRKY(_i, _mtype, _dtype, _mval, _dval) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + _dval = _mval; \ + vptr = &_dval; \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_SIMPLE(_i, _mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = &_val; \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_STRING(_i, _mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = _val; \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + _dvar = _mvar; \ + vptr = &_dvar; \ + \ + if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \ + goto fail; \ + break + + mrp_dbus_msg_t *m; + mrp_list_hook_t *p, *n; + mrp_msg_field_t *f; + uint16_t base; + uint32_t asize, i; + const char *sig; + int type, len; + void *vptr; + uint32_t bln; + uint16_t u16, blb; + int16_t s16; + + m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member); + + if (m == NULL) + return NULL; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, + (void *)sender_id)) + goto fail; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &msg->nfield)) + goto fail; + + mrp_list_foreach(&msg->fields, p, n) { + f = mrp_list_entry(p, typeof(*f), hook); + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) || + !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type)) + goto fail; + + switch (f->type) { + BASIC_STRING(m, STRING, STRING , f->str); + BASIC_QUIRKY(m, BOOL , BOOLEAN, f->bln, bln); + BASIC_QUIRKY(m, UINT8 , UINT16 , f->u8 , u16); + BASIC_QUIRKY(m, SINT8 , INT16 , f->s8 , s16); + BASIC_SIMPLE(m, UINT16, UINT16 , f->u16); + BASIC_SIMPLE(m, SINT16, INT16 , f->s16); + BASIC_SIMPLE(m, UINT32, UINT32 , f->u32); + BASIC_SIMPLE(m, SINT32, INT32 , f->s32); + BASIC_SIMPLE(m, UINT64, UINT64 , f->u64); + BASIC_SIMPLE(m, SINT64, INT64 , f->s64); + BASIC_SIMPLE(m, DOUBLE, DOUBLE , f->dbl); + + case MRP_MSG_FIELD_BLOB: + vptr = f->blb; + len = (int)f->size[0]; + sig = get_array_signature(f->type); + asize = len; + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, NULL)) + goto fail; + + for (i = 0; i < asize; i++) { + blb = ((uint8_t *)f->blb)[i]; + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &blb)) + goto fail; + } + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + break; + + default: + if (f->type & MRP_MSG_FIELD_ARRAY) { + base = f->type & ~(MRP_MSG_FIELD_ARRAY); + asize = f->size[0]; + sig = get_array_signature(base); + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + for (i = 0; i < asize; i++) { + switch (base) { + ARRAY_STRING(m, STRING, STRING , f->astr[i]); + ARRAY_QUIRKY(m, BOOL , BOOLEAN, f->abln[i], bln); + ARRAY_QUIRKY(m, UINT8 , UINT16 , f->au8[i] , u16); + ARRAY_QUIRKY(m, SINT8 , INT16 , f->as8[i] , s16); + ARRAY_SIMPLE(m, UINT16, UINT16 , f->au16[i]); + ARRAY_SIMPLE(m, SINT16, INT16 , f->as16[i]); + ARRAY_SIMPLE(m, UINT32, UINT32 , f->au32[i]); + ARRAY_SIMPLE(m, SINT32, INT32 , f->as32[i]); + ARRAY_SIMPLE(m, UINT64, UINT64 , f->au64[i]); + ARRAY_SIMPLE(m, DOUBLE, DOUBLE , f->adbl[i]); + + case MRP_MSG_FIELD_BLOB: + goto fail; + + default: + goto fail; + } + } + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + } + else + goto fail; + } + } + + return m; + + fail: + if (m != NULL) + mrp_dbus_msg_unref(m); + + errno = ECOMM; + + return FALSE; + +#undef BASIC_SIMPLE +#undef BASIC_STRING +#undef BASIC_QUIRKY +#undef ARRAY_SIMPLE +#undef ARRAY_STRING +#undef ARRAY_QUIRKY +} + + +static mrp_msg_t *msg_decode(mrp_dbus_msg_t *m, const char **sender_id) +{ +#define BASIC_SIMPLE(_i, _mtype, _dtype, _var) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \ + &(_var))) \ + goto fail; \ + \ + if (!mrp_msg_append(msg, tag, type, (_var))) \ + goto fail; \ + break + +#define BASIC_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \ + &(_dvar))) \ + goto fail; \ + \ + _mvar = _dvar; \ + if (!mrp_msg_append(msg, tag, type, (_mvar))) \ + goto fail; \ + break + +#define ARRAY_SIMPLE(_i, _mtype, _dtype, _var) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \ + &(_var))) \ + goto fail; \ + break + +#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \ + &(_dvar))) \ + goto fail; \ + \ + _mvar = _dvar; \ + break + +#define APPEND_ARRAY(_type, _var) \ + case MRP_MSG_FIELD_##_type: \ + if (!mrp_msg_append(msg, tag, \ + MRP_MSG_FIELD_ARRAY | \ + MRP_MSG_FIELD_##_type, \ + n, _var)) \ + goto fail; \ + break + + mrp_msg_t *msg; + mrp_msg_value_t v; + uint16_t u16; + int16_t s16; + uint32_t u32; + uint16_t nfield, tag, type, base, i; + uint32_t n, j; + int asize; + const char *sender, *sig; + + msg = NULL; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield)) + goto fail; + + msg = mrp_msg_create_empty(); + + if (msg == NULL) + goto fail; + + for (i = 0; i < nfield; i++) { + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type)) + goto fail; + + switch (type) { + BASIC_SIMPLE(m, STRING, STRING , v.str); + BASIC_QUIRKY(m, BOOL , BOOLEAN, v.bln, u32); + BASIC_QUIRKY(m, UINT8 , UINT16 , v.u8 , u16); + BASIC_QUIRKY(m, SINT8 , INT16 , v.s8 , s16); + BASIC_SIMPLE(m, UINT16, UINT16 , v.u16); + BASIC_SIMPLE(m, SINT16, INT16 , v.s16); + BASIC_SIMPLE(m, UINT32, UINT32 , v.u32); + BASIC_SIMPLE(m, SINT32, INT32 , v.s32); + BASIC_SIMPLE(m, UINT64, UINT64 , v.u64); + BASIC_SIMPLE(m, SINT64, INT64 , v.s64); + BASIC_SIMPLE(m, DOUBLE, DOUBLE , v.dbl); + + case MRP_MSG_FIELD_BLOB: + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + { + uint8_t blb[n]; + + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL)) + goto fail; + + for (j = 0; j < n; j++) { + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, + blb + j)) + goto fail; + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + + asize = n; + if (!mrp_msg_append(msg, tag, type, asize, blb)) + goto fail; + } + break; + + default: + if (!(type & MRP_MSG_FIELD_ARRAY)) + goto fail; + + base = type & ~(MRP_MSG_FIELD_ARRAY); + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + sig = get_array_signature(base); + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + { + char *astr[n]; + uint32_t dbln[n]; + bool abln[n]; + uint8_t au8 [n]; + int8_t as8 [n]; + uint16_t au16[n]; + int16_t as16[n]; + uint32_t au32[n]; + int32_t as32[n]; + uint64_t au64[n]; + int64_t as64[n]; + double adbl[n]; + + for (j = 0; j < n; j++) { + switch (base) { + ARRAY_SIMPLE(m, STRING, STRING , astr[j]); + ARRAY_QUIRKY(m, BOOL , BOOLEAN, abln[j], dbln[j]); + ARRAY_QUIRKY(m, UINT8 , UINT16 , au8[j] , au16[j]); + ARRAY_QUIRKY(m, SINT8 , INT16 , as8[j] , as16[j]); + ARRAY_SIMPLE(m, UINT16, UINT16 , au16[j]); + ARRAY_SIMPLE(m, SINT16, INT16 , as16[j]); + ARRAY_SIMPLE(m, UINT32, UINT32 , au32[j]); + ARRAY_SIMPLE(m, SINT32, INT32 , as32[j]); + ARRAY_SIMPLE(m, UINT64, UINT64 , au64[j]); + ARRAY_SIMPLE(m, SINT64, INT64 , as64[j]); + ARRAY_SIMPLE(m, DOUBLE, DOUBLE , adbl[j]); + default: + goto fail; + } + } + + switch (base) { + APPEND_ARRAY(STRING, astr); + APPEND_ARRAY(BOOL , abln); + APPEND_ARRAY(UINT8 , au8 ); + APPEND_ARRAY(SINT8 , as8 ); + APPEND_ARRAY(UINT16, au16); + APPEND_ARRAY(SINT16, as16); + APPEND_ARRAY(UINT32, au32); + APPEND_ARRAY(SINT32, as32); + APPEND_ARRAY(UINT64, au64); + APPEND_ARRAY(SINT64, as64); + APPEND_ARRAY(DOUBLE, adbl); + default: + goto fail; + } + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + } + } + + if (sender_id != NULL) + *sender_id = sender; + + return msg; + + fail: + mrp_msg_unref(msg); + errno = EBADMSG; + + return NULL; + +#undef BASIC_SIMPLE +#undef BASIC_QUIRKY +#undef ARRAY_SIMPLE +#undef ARRAY_QUIRKY +#undef APPEND_ARRAY +} + + +static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + void *data, uint16_t tag) +{ +#define BASIC_SIMPLE(_mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = &(_val); \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + +#define BASIC_STRING(_mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = _val; \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + +#define BASIC_QUIRKY(_mtype, _dtype, _mval, _dval) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + _dval = _mval; \ + vptr = &_dval; \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_SIMPLE(_mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = &_val; \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_STRING(_mtype, _dtype, _val) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + vptr = _val; \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + +#define ARRAY_QUIRKY(_mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + type = MRP_DBUS_TYPE_##_dtype; \ + _dvar = _mvar; \ + vptr = &_dvar; \ + \ + if (!mrp_dbus_msg_append_basic(m, type, vptr)) \ + goto fail; \ + break + + mrp_dbus_msg_t *m; + mrp_data_descr_t *descr; + mrp_data_member_t *fields, *f; + int nfield; + uint16_t type, base; + mrp_msg_value_t *v; + void *vptr; + uint32_t n, j; + int i, blblen; + const char *sig; + uint16_t u16; + int16_t s16; + uint32_t bln, asize; + + m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member); + + if (m == NULL) + return NULL; + + descr = mrp_msg_find_type(tag); + + if (descr == NULL) + goto fail; + + fields = descr->fields; + nfield = descr->nfield; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, + (void *)sender_id)) + goto fail; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &tag)) + goto fail; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &nfield)) + goto fail; + + for (i = 0, f = fields; i < nfield; i++, f++) { + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) || + !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type)) + goto fail; + + v = (mrp_msg_value_t *)(data + f->offs); + + switch (f->type) { + BASIC_STRING(STRING, STRING , v->str); + BASIC_QUIRKY(BOOL , BOOLEAN, v->bln, bln); + BASIC_QUIRKY(UINT8 , UINT16 , v->u8 , u16); + BASIC_QUIRKY(SINT8 , INT16 , v->s8 , s16); + BASIC_SIMPLE(UINT16, UINT16 , v->u16); + BASIC_SIMPLE(SINT16, INT16 , v->s16); + BASIC_SIMPLE(UINT32, UINT32 , v->u32); + BASIC_SIMPLE(SINT32, INT32 , v->s32); + BASIC_SIMPLE(UINT64, UINT64 , v->u64); + BASIC_SIMPLE(SINT64, INT64 , v->s64); + BASIC_SIMPLE(DOUBLE, DOUBLE , v->dbl); + + case MRP_MSG_FIELD_BLOB: + sig = get_array_signature(f->type); + blblen = mrp_data_get_blob_size(data, descr, i); + asize = blblen; + + if (blblen == -1) + goto fail; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + for (i = 0; i < blblen; i++) + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, + f->blb + i)) + goto fail; + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + break; + + default: + if (!(f->type & MRP_MSG_FIELD_ARRAY)) + goto fail; + + base = f->type & ~(MRP_MSG_FIELD_ARRAY); + n = mrp_data_get_array_size(data, descr, i); + sig = get_array_signature(base); + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + for (j = 0; j < n; j++) { + switch (base) { + ARRAY_STRING(STRING, STRING , v->astr[j]); + ARRAY_QUIRKY(BOOL , BOOLEAN, v->abln[j], bln); + ARRAY_QUIRKY(UINT8 , UINT16 , v->au8[j] , u16); + ARRAY_QUIRKY(SINT8 , INT16 , v->as8[j] , s16); + ARRAY_SIMPLE(UINT16, UINT16 , v->au16[j]); + ARRAY_SIMPLE(SINT16, INT16 , v->as16[j]); + ARRAY_SIMPLE(UINT32, UINT32 , v->au32[j]); + ARRAY_SIMPLE(SINT32, INT32 , v->as32[j]); + ARRAY_SIMPLE(UINT64, UINT64 , v->au64[j]); + ARRAY_SIMPLE(DOUBLE, DOUBLE , v->adbl[j]); + + case MRP_MSG_FIELD_BLOB: + goto fail; + + default: + goto fail; + } + } + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + } + } + + return m; + + fail: + if (m != NULL) + mrp_dbus_msg_unref(m); + + errno = ECOMM; + + return NULL; + +#undef BASIC_SIMPLE +#undef BASIC_QUIRKY +#undef ARRAY_SIMPLE +#undef ARRAY_QUIRKY +} + + +static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield, + uint16_t tag) +{ + mrp_data_member_t *f; + int i; + + for (i = 0, f = fields; i < nfield; i++, f++) + if (f->tag == tag) + return f; + + return NULL; +} + + +static void *data_decode(mrp_dbus_msg_t *m, uint16_t *tagp, + const char **sender_id) +{ +#define HANDLE_SIMPLE(_i, _mtype, _dtype, _var) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \ + &(_var))) \ + goto fail; \ + break + +#define HANDLE_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \ + case MRP_MSG_FIELD_##_mtype: \ + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \ + &(_dvar))) \ + goto fail; \ + \ + _mvar = _dvar; \ + break + + void *data; + mrp_data_descr_t *descr; + mrp_data_member_t *fields, *f; + int nfield; + uint16_t tag, type, base; + mrp_msg_value_t *v; + uint32_t n, j, size; + int i; + const char *sender, *sig; + uint32_t u32; + uint16_t u16; + int16_t s16; + + tag = 0; + data = NULL; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag)) + goto fail; + + descr = mrp_msg_find_type(tag); + + if (descr == NULL) + goto fail; + + *tagp = tag; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield)) + goto fail; + + if (nfield != descr->nfield) + goto fail; + + fields = descr->fields; + data = mrp_allocz(descr->size); + + if (MRP_UNLIKELY(data == NULL)) + goto fail; + + for (i = 0; i < nfield; i++) { + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type)) + goto fail; + + f = member_type(fields, nfield, tag); + + if (MRP_UNLIKELY(f == NULL)) + goto fail; + + v = (mrp_msg_value_t *)(data + f->offs); + + switch (type) { + HANDLE_SIMPLE(&im, STRING, STRING , v->str); + HANDLE_QUIRKY(&im, BOOL , BOOLEAN, v->bln, u32); + HANDLE_QUIRKY(&im, UINT8 , UINT16 , v->u8 , u16); + HANDLE_QUIRKY(&im, SINT8 , INT16 , v->s8 , s16); + HANDLE_SIMPLE(&im, UINT16, UINT16 , v->u16); + HANDLE_SIMPLE(&im, SINT16, INT16 , v->s16); + HANDLE_SIMPLE(&im, UINT32, UINT32 , v->u32); + HANDLE_SIMPLE(&im, SINT32, INT32 , v->s32); + HANDLE_SIMPLE(&im, UINT64, UINT64 , v->u64); + HANDLE_SIMPLE(&im, SINT64, INT64 , v->s64); + HANDLE_SIMPLE(&im, DOUBLE, DOUBLE , v->dbl); + + case MRP_MSG_FIELD_BLOB: + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &size)) + goto fail; + + sig = MRP_DBUS_TYPE_BYTE_AS_STRING; + + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + { + uint8_t blb[size]; + + for (j = 0; j < size; j++) + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, + blb + j)) + goto fail; + + v->blb = mrp_alloc(size); + + if (v->blb == NULL && size != 0) + goto fail; + + memcpy(v->blb, blb, size); + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + break; + + default: + if (!(f->type & MRP_MSG_FIELD_ARRAY)) + goto fail; + + base = type & ~(MRP_MSG_FIELD_ARRAY); + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL)) + goto fail; + + size = n; + + switch (base) { + case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break; + case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break; + case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break; + case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break; + case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break; + case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break; + case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break; + case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break; + case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break; + case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break; + case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break; + default: + goto fail; + } + + v->aany = mrp_allocz(size); + if (v->aany == NULL) + goto fail; + + for (j = 0; j < n; j++) { + uint32_t dbln[n]; + uint16_t au16[n]; + int16_t as16[n]; + + switch (base) { + HANDLE_SIMPLE(&ia, STRING, STRING , v->astr[j]); + HANDLE_QUIRKY(&ia, BOOL , BOOLEAN, v->abln[j], dbln[j]); + HANDLE_QUIRKY(&ia, UINT8 , UINT16 , v->au8[j] , au16[j]); + HANDLE_QUIRKY(&ia, SINT8 , INT16 , v->as8[j] , as16[j]); + HANDLE_SIMPLE(&ia, UINT16, UINT16 , v->au16[j]); + HANDLE_SIMPLE(&ia, SINT16, INT16 , v->as16[j]); + HANDLE_SIMPLE(&ia, UINT32, UINT32 , v->au32[j]); + HANDLE_SIMPLE(&ia, SINT32, INT32 , v->as32[j]); + HANDLE_SIMPLE(&ia, UINT64, UINT64 , v->au64[j]); + HANDLE_SIMPLE(&ia, SINT64, INT64 , v->as64[j]); + HANDLE_SIMPLE(&ia, DOUBLE, DOUBLE , v->adbl[j]); + } + + if (base == MRP_MSG_FIELD_STRING) { + v->astr[j] = mrp_strdup(v->astr[j]); + if (v->astr[j] == NULL) + goto fail; + } + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + } + + if (f->type == MRP_MSG_FIELD_STRING) { + v->str = mrp_strdup(v->str); + if (v->str == NULL) + goto fail; + } + } + + if (sender_id != NULL) + *sender_id = sender; + + return data; + + fail: + mrp_data_free(data, tag); + errno = EBADMSG; + + return NULL; +} + + +static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination, + const char *path, const char *interface, + const char *member, const char *sender_id, + void *data, size_t size) +{ + mrp_dbus_msg_t *m; + const char *sig; + uint32_t i, n; + + m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member); + + if (m == NULL) + return NULL; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, + (void *)sender_id)) + goto fail; + + sig = MRP_DBUS_TYPE_BYTE_AS_STRING; + n = size; + + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + for (i = 0; i < n; i++) + if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, data + i)) + goto fail; + + if (!mrp_dbus_msg_close_container(m)) + goto fail; + + return m; + + fail: + mrp_dbus_msg_unref(m); + + errno = ECOMM; + + return NULL; +} + + +static void *raw_decode(mrp_dbus_msg_t *m, size_t *sizep, + const char **sender_id) +{ + const char *sender, *sig; + void *data; + uint32_t n, i; + + data = NULL; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n)) + goto fail; + + sig = MRP_DBUS_TYPE_BYTE_AS_STRING; + + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig)) + goto fail; + + { + uint8_t databuf[n]; + + for (i = 0; i < n; i++) + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, databuf + i)) + goto fail; + + data = mrp_alloc(n); + + if (data == NULL && n != 0) + goto fail; + + memcpy(data, databuf, n); + } + + if (!mrp_dbus_msg_exit_container(m)) + goto fail; + + if (sizep != NULL) + *sizep = (size_t)n; + + if (sender_id != NULL) + *sender_id = sender; + + return data; + + fail: + errno = EBADMSG; + + return NULL; +} + + +MRP_REGISTER_TRANSPORT(dbus, DBUS, dbus_t, dbus_resolve, + dbus_open, dbus_createfrom, dbus_close, NULL, + dbus_bind, NULL, NULL, + dbus_connect, dbus_disconnect, + dbus_sendmsg, dbus_sendmsgto, + dbus_sendraw, dbus_sendrawto, + dbus_senddata, dbus_senddatato, + NULL, NULL); diff --git a/src/common/dbus-sdbus.c b/src/common/dbus-sdbus.c new file mode 100644 index 0000000..a2fb9e9 --- /dev/null +++ b/src/common/dbus-sdbus.c @@ -0,0 +1,1955 @@ +/* + * Copyright (c) 2012, 2013, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUS_SERVICE "org.freedesktop.DBus" +#define BUS_PATH "/org/freedesktop/DBus" +#define BUS_INTERFACE "org.freedesktop.DBus" +#define BUS_NAME_CHANGED "NameOwnerChanged" +#define BUS_GET_OWNER "GetNameOwner" + +/* XXX check these... */ +#define SDBUS_ERROR_FAILED "org.DBus.error.failed" + +/* D-Bus name request flags and reply statuses. */ +#define SDBUS_NAME_REQUEST_REPLACE 0x2 +#define SDBUS_NAME_REQUEST_DONTQ 0x4 + +#define SDBUS_NAME_STATUS_OWNER 0x1 +#define SDBUS_NAME_STATUS_QUEUING 0x2 +#define SDBUS_NAME_STATUS_EXISTS 0x3 +#define SDBUS_NAME_STATUS_GOTIT 0x4 + +#define SDBUS_NAME_STATUS_RELEASED 0x1 +#define SDBUS_NAME_STATUS_UNKNOWN 0x2 +#define SDBUS_NAME_STATUS_FOREIGN 0x3 + +#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000)) +#define MSEC_TO_USEC(msec) ((uint64_t)(msec) * 1000) + +struct mrp_dbus_s { + char *address; /* bus address */ + sd_bus *bus; /* actual D-BUS connection */ + mrp_mainloop_t *ml; /* murphy mainloop */ + mrp_subloop_t *sl; /* subloop for pumping the bus */ + mrp_htbl_t *objects; /* object path (refcount) table */ + mrp_htbl_t *methods; /* method handler table */ + mrp_htbl_t *signals; /* signal handler table */ + mrp_list_hook_t name_trackers; /* peer (name) watchers */ + mrp_list_hook_t calls; /* pending calls */ + uint32_t call_id; /* next call id */ + const char *unique_name; /* our unique D-BUS address */ + int priv; /* whether a private connection */ + int signal_filter; /* if signal dispatching is set up */ + int register_fallback; /* if the fallback object is set up */ + mrp_refcnt_t refcnt; /* reference count */ +}; + + +struct mrp_dbus_msg_s { + sd_bus_message *msg; /* actual D-Bus message */ + mrp_refcnt_t refcnt; /* reference count */ + mrp_list_hook_t arrays; /* implicitly freed related arrays */ +}; + + +typedef struct { + mrp_dbus_type_t type; + mrp_list_hook_t hook; + void *items; + size_t nitem; +} msg_array_t; + +/* + * Notes: + * + * At the moment we administer DBUS method and signal handlers + * in a very primitive way (subject to be changed later). For + * every bus instance we maintain two hash tables, one for methods + * and another for signals. Each method and signal handler is + * hashed in only by it's method/signal name to a linked list of + * method or signal handlers. + * + * When dispatching a method, we look up the chain with a matching + * method name, or the chain for "" in case a matching chain is + * not found, and invoke the handler which best matches the + * received message (by looking at the path, interface and name). + * Only one such handler is invoked at most. + * + * For signals we look up both the chain with a matching name and + * the chain for "" and invoke all signal handlers that match the + * received message (regardless of their return value). + */ + +typedef struct { + char *path; /* object path */ + int cnt; /* reference count */ +} object_t; + +typedef struct { + char *member; /* signal/method name */ + mrp_list_hook_t handlers; /* handlers with matching member */ +} handler_list_t; + +typedef struct { + mrp_list_hook_t hook; + char *sender; + char *path; + char *interface; + char *member; + mrp_dbus_handler_t handler; + void *user_data; +} handler_t; + +#define method_t handler_t +#define signal_t handler_t + + +typedef struct { + mrp_list_hook_t hook; /* hook to name tracker list */ + char *name; /* name to track */ + mrp_dbus_name_cb_t cb; /* status change callback */ + void *user_data; /* opaque callback user data */ + int32_t qid; /* initial query ID */ +} name_tracker_t; + + +typedef struct { + mrp_dbus_t *dbus; /* DBUS connection */ + int32_t id; /* call id */ + mrp_dbus_reply_cb_t cb; /* completion notification callback */ + void *user_data; /* opaque callback data */ + uint64_t serial; /* DBUS call */ + mrp_list_hook_t hook; /* hook to list of pending calls */ + sd_bus_message *msg; /* original message */ +} call_t; + + +typedef struct { + mrp_mainloop_t *ml; /* mainloop for bus connection */ + const char *address; /* address of bus */ +} bus_spec_t; + +static mrp_htbl_t *buses; + + + +static int dispatch_signal(sd_bus *b, int r, sd_bus_message *msg, void *data); +static int dispatch_method(sd_bus *b, int r, sd_bus_message *msg, void *data); + +static void purge_name_trackers(mrp_dbus_t *dbus); +static void purge_calls(mrp_dbus_t *dbus); +static void handler_list_free_cb(void *key, void *entry); +static void handler_free(handler_t *h); +static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, + void *data); +static void call_free(call_t *call); +static void object_free_cb(void *key, void *entry); + + +static int purge_objects(void *key, void *entry, void *user_data) +{ + mrp_dbus_t *dbus = (mrp_dbus_t *)user_data; + object_t *o = (object_t *)entry; + + MRP_UNUSED(key); + + sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus); + + return MRP_HTBL_ITER_MORE; +} + + +static int purge_filters(void *key, void *entry, void *user_data) +{ + mrp_dbus_t *dbus = (mrp_dbus_t *)user_data; + handler_list_t *l = (handler_list_t *)entry; + mrp_list_hook_t *p, *n; + handler_t *h; + + MRP_UNUSED(key); + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + mrp_dbus_remove_filter(dbus, + h->sender, h->path, h->interface, + h->member, NULL); + } + + return MRP_HTBL_ITER_MORE; +} + + +static void dbus_disconnect(mrp_dbus_t *dbus) +{ + if (dbus) { + mrp_htbl_remove(buses, dbus->bus, FALSE); + + if (dbus->objects) { + mrp_htbl_foreach(dbus->signals, purge_objects, dbus); + mrp_htbl_destroy(dbus->objects, TRUE); + } + + if (dbus->signals) { + mrp_htbl_foreach(dbus->signals, purge_filters, dbus); + mrp_htbl_destroy(dbus->signals, TRUE); + } + if (dbus->methods) + mrp_htbl_destroy(dbus->methods, TRUE); + + purge_name_trackers(dbus); + purge_calls(dbus); + + if (dbus->bus != NULL) { + if (dbus->signal_filter) + sd_bus_remove_filter(dbus->bus, dispatch_signal, dbus); + if (dbus->register_fallback) + sd_bus_remove_fallback(dbus->bus, "/", dispatch_method, dbus); + if (dbus->priv) + sd_bus_close(dbus->bus); + else + sd_bus_unref(dbus->bus); + } + + mrp_free(dbus->address); + dbus->bus = NULL; + dbus->ml = NULL; + + mrp_free(dbus); + } +} + + +static int bus_cmp(const void *key1, const void *key2) +{ + return key2 - key1; +} + + +static uint32_t bus_hash(const void *key) +{ + uint32_t h; + + h = (ptrdiff_t)key; + h >>= 2 * sizeof(key); + + return h; +} + + +static int find_bus_by_spec(void *key, void *object, void *user_data) +{ + mrp_dbus_t *dbus = (mrp_dbus_t *)object; + bus_spec_t *spec = (bus_spec_t *)user_data; + + MRP_UNUSED(key); + + if (dbus->ml == spec->ml && !strcmp(dbus->address, spec->address)) + return TRUE; + else + return FALSE; +} + + +static mrp_dbus_t *dbus_get(mrp_mainloop_t *ml, const char *address) +{ + mrp_htbl_config_t hcfg; + bus_spec_t spec; + + if (buses == NULL) { + mrp_clear(&hcfg); + + hcfg.comp = bus_cmp; + hcfg.hash = bus_hash; + hcfg.free = NULL; + + buses = mrp_htbl_create(&hcfg); + + return NULL; + } + else { + spec.ml = ml; + spec.address = address; + + return mrp_htbl_find(buses, find_bus_by_spec, &spec); + } +} + + +mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address, + mrp_dbus_err_t *errp) +{ + mrp_htbl_config_t hcfg; + mrp_dbus_t *dbus; + + if ((dbus = dbus_get(ml, address)) != NULL) + return mrp_dbus_ref(dbus); + + if ((dbus = mrp_allocz(sizeof(*dbus))) == NULL) + return NULL; + + mrp_list_init(&dbus->calls); + mrp_list_init(&dbus->name_trackers); + mrp_refcnt_init(&dbus->refcnt); + + dbus->ml = ml; + + mrp_dbus_error_init(errp); + + /* + * connect to the bus + */ + + if (!strcmp(address, "system")) { + if (sd_bus_open_system(&dbus->bus) != 0) + goto fail; + } + else if (!strcmp(address, "session")) { + if (sd_bus_open_user(&dbus->bus) != 0) + goto fail; + } + else { + dbus->priv = TRUE; + + if (sd_bus_new(&dbus->bus) != 0) + goto fail; + else { + if (sd_bus_set_address(dbus->bus, address) != 0) + goto fail; + + if (sd_bus_start(dbus->bus) != 0) + goto fail; + } + } + + dbus->address = mrp_strdup(address); + if (sd_bus_get_unique_name(dbus->bus, &dbus->unique_name) != 0) + goto fail; + + /* + * set up with mainloop + */ + + if (!mrp_dbus_setup_with_mainloop(ml, dbus->bus)) + goto fail; + + /* + * set up our message dispatchers and take our name on the bus + */ + + if (sd_bus_add_filter(dbus->bus, dispatch_signal, dbus) != 0) { + mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED, + "Failed to set up signal dispatching."); + goto fail; + } + dbus->signal_filter = TRUE; + + if (sd_bus_add_fallback(dbus->bus, "/", dispatch_method, dbus) != 0) { + mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED, + "Failed to set up method dispatching."); + goto fail; + } + dbus->register_fallback = TRUE; + + mrp_clear(&hcfg); + hcfg.comp = mrp_string_comp; + hcfg.hash = mrp_string_hash; + hcfg.free = object_free_cb; + + if ((dbus->objects = mrp_htbl_create(&hcfg)) == NULL) { + mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED, + "Failed to create DBUS object path table."); + goto fail; + } + + hcfg.free = handler_list_free_cb; + + if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) { + mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED, + "Failed to create DBUS method table."); + goto fail; + } + + if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) { + mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED, + "Failed to create DBUS signal table."); + goto fail; + } + + + /* + * install handler for NameOwnerChanged for tracking clients/peers + */ + + if (!mrp_dbus_add_signal_handler(dbus, + BUS_SERVICE, BUS_PATH, BUS_INTERFACE, + BUS_NAME_CHANGED, name_owner_change_cb, + NULL)) { + mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED, + "Failed to install NameOwnerChanged handler."); + goto fail; + } + + /* install a 'safe' filter to avoid receiving all name change signals */ + mrp_dbus_install_filter(dbus, + BUS_SERVICE, BUS_PATH, BUS_INTERFACE, + BUS_NAME_CHANGED, BUS_SERVICE, NULL); + + mrp_list_init(&dbus->name_trackers); + dbus->call_id = 1; + + if (mrp_htbl_insert(buses, dbus->bus, dbus)) + return dbus; + + fail: + dbus_disconnect(dbus); + return NULL; +} + + +mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus) +{ + return mrp_ref_obj(dbus, refcnt); +} + + +int mrp_dbus_unref(mrp_dbus_t *dbus) +{ + if (mrp_unref_obj(dbus, refcnt)) { + dbus_disconnect(dbus); + + return TRUE; + } + else + return FALSE; +} + + +int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_err_t *error) +{ + int flags = SDBUS_NAME_REQUEST_REPLACE | SDBUS_NAME_REQUEST_DONTQ; + int status; + + mrp_dbus_error_init(error); + + status = sd_bus_request_name(dbus->bus, name, flags); + + if (status == SDBUS_NAME_STATUS_OWNER || status == SDBUS_NAME_STATUS_GOTIT) + return TRUE; + else { + mrp_dbus_error_set(error, SDBUS_ERROR_FAILED, "failed to request name"); + + return FALSE; + } +} + + +int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_err_t *error) +{ + int status; + + mrp_dbus_error_init(error); + + status = sd_bus_release_name(dbus->bus, name); + + if (status == SDBUS_NAME_STATUS_RELEASED) + return TRUE; + else { + mrp_dbus_error_set(error, SDBUS_ERROR_FAILED, "failed to release name"); + + return FALSE; + } +} + + +const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus) +{ + return dbus->unique_name; +} + + +static void name_owner_query_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data) +{ + name_tracker_t *t = (name_tracker_t *)data; + const char *owner; + int state; + + if (t->cb != NULL) { /* tracker still active */ + t->qid = 0; + state = !mrp_dbus_msg_is_error(m); + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &owner)) + owner = ""; + + t->cb(dbus, t->name, state, owner, t->user_data); + } + else /* already requested to delete */ + mrp_free(t); +} + + +static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data) +{ + const char *name, *prev, *next; + mrp_list_hook_t *p, *n; + name_tracker_t *t; + + MRP_UNUSED(data); + + if (mrp_dbus_msg_type(m) != MRP_DBUS_MESSAGE_TYPE_SIGNAL) + return FALSE; + + if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &name) || + !mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &prev) || + !mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &next)) + return FALSE; + +#if 0 + /* + * Notes: XXX TODO + * In principle t->cb could call mrp_dbus_forget for some other D-BUS + * address than name. If that happened to be n (== p->hook.next) this + * would result in a crash or memory corruption in the next iteration + * of this loop (when handling n). We can easily get around this + * problem by + * + * 1. administering in mrp_dbus_t that we're handing a NameOwnerChange + * 2. checking for this in mrp_dbus_forget_name and if it is the case + * only marking the affected entry for deletion + * 3. removing entries marked for deletion in this loop (or just + * ignoring them and making another pass in the end removing any + * such entry). + */ +#endif + + mrp_list_foreach(&dbus->name_trackers, p, n) { + t = mrp_list_entry(p, name_tracker_t, hook); + + if (!strcmp(name, t->name)) + t->cb(dbus, name, next && *next, next, t->user_data); + } + + return TRUE; +} + + +int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_name_cb_t cb, void *user_data) +{ + name_tracker_t *t; + + if ((t = mrp_allocz(sizeof(*t))) != NULL) { + if ((t->name = mrp_strdup(name)) != NULL) { + t->cb = cb; + t->user_data = user_data; + + if (mrp_dbus_install_filter(dbus, + BUS_SERVICE, BUS_PATH, BUS_INTERFACE, + BUS_NAME_CHANGED, name, + NULL)) { + mrp_list_append(&dbus->name_trackers, &t->hook); + + t->qid = mrp_dbus_call(dbus, + BUS_SERVICE, BUS_PATH, BUS_INTERFACE, + BUS_GET_OWNER, 5000, + name_owner_query_cb, t, + MRP_DBUS_TYPE_STRING, t->name, + MRP_DBUS_TYPE_INVALID); + return TRUE; + } + else { + mrp_free(t->name); + mrp_free(t); + } + } + } + + return FALSE; +} + + +int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_name_cb_t cb, void *user_data) +{ + mrp_list_hook_t *p, *n; + name_tracker_t *t; + + mrp_dbus_remove_filter(dbus, + BUS_SERVICE, BUS_PATH, BUS_INTERFACE, + BUS_NAME_CHANGED, name, + NULL); + + mrp_list_foreach(&dbus->name_trackers, p, n) { + t = mrp_list_entry(p, name_tracker_t, hook); + + if (t->cb == cb && t->user_data == user_data && !strcmp(t->name,name)) { + mrp_list_delete(&t->hook); + mrp_free(t->name); + + if (!t->qid) + mrp_free(t); + else { + t->cb = NULL; + t->user_data = NULL; + t->name = NULL; + } + + return TRUE; + } + } + + return FALSE; +} + + +static void purge_name_trackers(mrp_dbus_t *dbus) +{ + mrp_list_hook_t *p, *n; + name_tracker_t *t; + + mrp_list_foreach(&dbus->name_trackers, p, n) { + t = mrp_list_entry(p, name_tracker_t, hook); + + mrp_list_delete(p); + mrp_dbus_remove_filter(dbus, + BUS_SERVICE, BUS_PATH, BUS_INTERFACE, + BUS_NAME_CHANGED, t->name, + NULL); + mrp_free(t->name); + mrp_free(t); + } +} + + +static handler_t *handler_alloc(const char *sender, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data) +{ + handler_t *h; + + if ((h = mrp_allocz(sizeof(*h))) != NULL) { + h->sender = mrp_strdup(sender); + h->path = mrp_strdup(path); + h->interface = mrp_strdup(interface); + h->member = mrp_strdup(member); + + if ((path && !h->path) || !h->interface || !h->member) { + handler_free(h); + return NULL; + } + + h->handler = handler; + h->user_data = user_data; + + return h; + } + + return NULL; +} + + +static void handler_free(handler_t *h) +{ + if (h != NULL) { + mrp_free(h->sender); + mrp_free(h->path); + mrp_free(h->interface); + mrp_free(h->member); + + mrp_free(h); + } +} + + +static handler_list_t *handler_list_alloc(const char *member) +{ + handler_list_t *l; + + if ((l = mrp_allocz(sizeof(*l))) != NULL) { + if ((l->member = mrp_strdup(member)) != NULL) + mrp_list_init(&l->handlers); + else { + mrp_free(l); + l = NULL; + } + } + + return l; +} + + +static inline void handler_list_free(handler_list_t *l) +{ + mrp_list_hook_t *p, *n; + handler_t *h; + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + mrp_list_delete(p); + handler_free(h); + } + + mrp_free(l->member); + mrp_free(l); +} + + +static void handler_list_free_cb(void *key, void *entry) +{ + MRP_UNUSED(key); + + handler_list_free((handler_list_t *)entry); +} + + +static inline int handler_specificity(handler_t *h) +{ + int score = 0; + + if (h->path && *h->path) + score |= 0x4; + if (h->interface && *h->interface) + score |= 0x2; + if (h->member && *h->member) + score |= 0x1; + + return score; +} + + +static void handler_list_insert(handler_list_t *l, handler_t *handler) +{ + mrp_list_hook_t *p, *n; + handler_t *h; + int score; + + score = handler_specificity(handler); + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + + if (score >= handler_specificity(h)) { + mrp_list_append(h->hook.prev, &handler->hook); /* add before h */ + return; + } + } + + mrp_list_append(&l->handlers, &handler->hook); +} + + +static handler_t *handler_list_lookup(handler_list_t *l, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, + void *user_data) +{ + mrp_list_hook_t *p, *n; + handler_t *h; + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + + if (h->handler == handler && user_data == h->user_data && + path && !strcmp(path, h->path) && + interface && !strcmp(interface, h->interface) && + member && !strcmp(member, h->member)) + return h; + } + + return NULL; +} + + +static handler_t *handler_list_find(handler_list_t *l, const char *path, + const char *interface, const char *member) +{ +#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field)) + mrp_list_hook_t *p, *n; + handler_t *h; + + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + + if (MATCHES(h, path) && MATCHES(h, interface) && MATCHES(h, member)) + return h; + } + + return NULL; +#undef MATCHES +} + + +static void object_free_cb(void *key, void *entry) +{ + object_t *o = (object_t *)entry; + + MRP_UNUSED(key); + + mrp_free(o->path); + mrp_free(o); +} + + +static object_t *object_add(mrp_dbus_t *dbus, const char *path) +{ + object_t *o; + + o = mrp_alloc(sizeof(*o)); + + if (o != NULL) { + o->path = mrp_strdup(path); + o->cnt = 1; + + if (o->path == NULL) { + mrp_free(o); + return NULL; + } + + if (sd_bus_add_object(dbus->bus, o->path, dispatch_method, dbus) == 0) { + if (mrp_htbl_insert(dbus->objects, o->path, o)) + return o; + else + sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus); + } + + mrp_free(o->path); + mrp_free(o); + } + + return NULL; +} + + +static object_t *object_lookup(mrp_dbus_t *dbus, const char *path) +{ + return mrp_htbl_lookup(dbus->objects, (void *)path); +} + + +static int object_ref(mrp_dbus_t *dbus, const char *path) +{ + object_t *o; + + if ((o = object_lookup(dbus, path)) != NULL) { + o->cnt++; + return TRUE; + } + + if (object_add(dbus, path) != NULL) + return TRUE; + else + return FALSE; +} + + +static void object_unref(mrp_dbus_t *dbus, const char *path) +{ + object_t *o; + + if ((o = object_lookup(dbus, path)) != NULL) { + o->cnt--; + + if (o->cnt <= 0) { + mrp_htbl_remove(dbus->objects, (void *)path, FALSE); + sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus); + + mrp_free(o->path); + mrp_free(o); + } + } +} + + +int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data) +{ + handler_list_t *methods; + handler_t *m; + + if (!object_ref(dbus, path)) + return FALSE; + + if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) { + if ((methods = handler_list_alloc(member)) == NULL) + goto fail; + + mrp_htbl_insert(dbus->methods, methods->member, methods); + } + + m = handler_alloc(NULL, path, interface, member, handler, user_data); + + if (m != NULL) { + handler_list_insert(methods, m); + + return TRUE; + } + + fail: + object_unref(dbus, path); + + return FALSE; +} + + +int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data) +{ + handler_list_t *methods; + handler_t *m; + + if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) + return FALSE; + + m = handler_list_lookup(methods, path, interface, member, + handler, user_data); + if (m != NULL) { + object_unref(dbus, path); + mrp_list_delete(&m->hook); + handler_free(m); + + return TRUE; + } + else + return FALSE; +} + + +int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, mrp_dbus_handler_t handler, + void *user_data) +{ + handler_list_t *signals; + handler_t *s; + + if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) { + if ((signals = handler_list_alloc(member)) == NULL) + return FALSE; + + if (!mrp_htbl_insert(dbus->signals, signals->member, signals)) { + handler_list_free(signals); + return FALSE; + } + } + + s = handler_alloc(sender, path, interface, member, handler, user_data); + if (s != NULL) { + handler_list_insert(signals, s); + return TRUE; + } + else { + handler_free(s); + if (mrp_list_empty(&signals->handlers)) + mrp_htbl_remove(dbus->signals, signals->member, TRUE); + return FALSE; + } +} + + + +int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, mrp_dbus_handler_t handler, + void *user_data) +{ + handler_list_t *signals; + handler_t *s; + + MRP_UNUSED(sender); + + if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) + return FALSE; + + s = handler_list_lookup(signals, path, interface, member, + handler, user_data); + if (s != NULL) { + mrp_list_delete(&s->hook); + handler_free(s); + + if (mrp_list_empty(&signals->handlers)) + mrp_htbl_remove(dbus->signals, (void *)member, TRUE); + + return TRUE; + } + else + return FALSE; +} + + + +int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus, + mrp_dbus_handler_t handler, void *user_data, + const char *sender, const char *path, + const char *interface, const char *member, ...) +{ + va_list ap; + int success; + + + if (mrp_dbus_add_signal_handler(dbus, sender, path, interface, member, + handler, user_data)) { + va_start(ap, member); + success = mrp_dbus_install_filterv(dbus, + sender, path, interface, member, ap); + va_end(ap); + + if (success) + return TRUE; + else + mrp_dbus_del_signal_handler(dbus, sender, path, interface, member, + handler, user_data); + } + + return FALSE; +} + + +int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus, + mrp_dbus_handler_t handler, void *user_data, + const char *sender, const char *path, + const char *interface, const char *member, ...) +{ + va_list ap; + int status; + + status = mrp_dbus_del_signal_handler(dbus, sender, path, interface, member, + handler, user_data); + va_start(ap, member); + status &= mrp_dbus_remove_filterv(dbus, + sender, path, interface, member, ap); + va_end(ap); + + return status; +} + + +int mrp_dbus_install_filterv(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, va_list args) +{ +#define ADD_TAG(tag, value) do { \ + if (value != NULL) { \ + l = snprintf(p, n, "%s%s='%s'", p == filter ? "" : ",", \ + tag, value); \ + if (l >= n) \ + return FALSE; \ + n -= l; \ + p += l; \ + } \ + } while (0) + + va_list ap; + char filter[1024], *p, argn[16], *val; + int n, l, i; + + p = filter; + n = sizeof(filter); + + ADD_TAG("type" , "signal"); + ADD_TAG("sender" , sender); + ADD_TAG("path" , path); + ADD_TAG("interface", interface); + ADD_TAG("member" , member); + + va_copy(ap, args); + i = 0; + while ((val = va_arg(ap, char *)) != NULL) { + snprintf(argn, sizeof(argn), "arg%d", i); + ADD_TAG(argn, val); + i++; + } + va_end(ap); + + if (sd_bus_add_match(dbus->bus, filter, NULL, NULL) != 0) { + mrp_log_error("Failed to install filter '%s'.", filter); + + return FALSE; + } + else + return TRUE; + +} + + +int mrp_dbus_install_filter(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, ...) +{ + va_list ap; + int status; + + va_start(ap, member); + status = mrp_dbus_install_filterv(dbus, + sender, path, interface, member, ap); + va_end(ap); + + return status; +} + + +int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, va_list args) +{ + va_list ap; + char filter[1024], *p, argn[16], *val; + int n, l, i; + + p = filter; + n = sizeof(filter); + + ADD_TAG("type" , "signal"); + ADD_TAG("sender" , sender); + ADD_TAG("path" , path); + ADD_TAG("interface", interface); + ADD_TAG("member" , member); + + va_copy(ap, args); + i = 0; + while ((val = va_arg(ap, char *)) != NULL) { + snprintf(argn, sizeof(argn), "arg%d", i); + ADD_TAG(argn, val); + i++; + } + va_end(ap); + + sd_bus_remove_match(dbus->bus, filter, NULL, NULL); + + return TRUE; +#undef ADD_TAG +} + + +int mrp_dbus_remove_filter(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, ...) +{ + va_list ap; + int status; + + va_start(ap, member); + status = mrp_dbus_remove_filterv(dbus, sender, path, interface, member, ap); + va_end(ap); + + return status; +} + + +static int element_size(mrp_dbus_type_t type) +{ + switch (type) { + case MRP_DBUS_TYPE_BYTE: return sizeof(char); + case MRP_DBUS_TYPE_BOOLEAN: return sizeof(uint32_t); + case MRP_DBUS_TYPE_INT16: + case MRP_DBUS_TYPE_UINT16: return sizeof(uint16_t); + case MRP_DBUS_TYPE_INT32: + case MRP_DBUS_TYPE_UINT32: return sizeof(uint32_t); + case MRP_DBUS_TYPE_INT64: + case MRP_DBUS_TYPE_UINT64: return sizeof(uint64_t); + case MRP_DBUS_TYPE_DOUBLE: return sizeof(double); + case MRP_DBUS_TYPE_STRING: return sizeof(char *); + case MRP_DBUS_TYPE_OBJECT_PATH: return sizeof(char *); + case MRP_DBUS_TYPE_SIGNATURE: return sizeof(char *); + default: + return FALSE; + } + +} + + +static inline mrp_dbus_msg_t *create_message(sd_bus_message *msg, int ref) +{ + mrp_dbus_msg_t *m; + + if (msg != NULL) { + if ((m = mrp_allocz(sizeof(*m))) != NULL) { + mrp_refcnt_init(&m->refcnt); + mrp_list_init(&m->arrays); + if (ref) + m->msg = sd_bus_message_ref(msg); + else + m->msg = msg; + } + + return m; + } + else + return NULL; +} + + +static void free_msg_array(msg_array_t *a) +{ + void *ptr; + size_t esize, i; + int string; + + if (a == NULL) + return; + + mrp_list_delete(&a->hook); + + if ((esize = element_size(a->type)) != 0) { + if (a->type == MRP_DBUS_TYPE_STRING || + a->type == MRP_DBUS_TYPE_OBJECT_PATH || + a->type == MRP_DBUS_TYPE_SIGNATURE) + string = TRUE; + else + string = FALSE; + + if (string) + for (i = 0, ptr = a->items; i < a->nitem; i++, ptr += esize) + mrp_free(ptr); + + mrp_free(a->items); + } + else + mrp_log_error("Hmm... looks like we have a corrupted implicit array."); + + mrp_free(a); +} + + +static void free_message(mrp_dbus_msg_t *m) +{ + mrp_list_hook_t *p, *n; + msg_array_t *a; + + mrp_list_foreach(&m->arrays, p, n) { + a = mrp_list_entry(p, typeof(*a), hook); + free_msg_array(a); + } + + mrp_free(m); +} + + +mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m) +{ + return mrp_ref_obj(m, refcnt); +} + + +int mrp_dbus_msg_unref(mrp_dbus_msg_t *m) +{ + if (mrp_unref_obj(m, refcnt)) { + sd_bus_message_unref(m->msg); + free_message(m); + + return TRUE; + } + else + return FALSE; +} + + +static inline int verify_type(sd_bus_message *msg, int expected_type) +{ + uint8_t type; + + if (sd_bus_message_get_type(msg, &type) != 0 || type == expected_type) + return FALSE; + else + return TRUE; +} + + +static int dispatch_method(sd_bus *bus,int ret, sd_bus_message *msg, void *data) +{ +#define SAFESTR(str) (str ? str : "") + mrp_dbus_t *dbus = (mrp_dbus_t *)data; + mrp_dbus_msg_t *m = NULL; + const char *path = sd_bus_message_get_path(msg); + const char *interface = sd_bus_message_get_interface(msg); + const char *member = sd_bus_message_get_member(msg); + int r = FALSE; + handler_list_t *l; + handler_t *h; + + MRP_UNUSED(bus); + MRP_UNUSED(ret); + + if (!verify_type(msg, MRP_DBUS_MESSAGE_TYPE_METHOD_CALL) || !member) + return r; + + mrp_debug("path='%s', interface='%s', member='%s')...", + SAFESTR(path), SAFESTR(interface), SAFESTR(member)); + + if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) { + retry: + if ((h = handler_list_find(l, path, interface, member)) != NULL) { + sd_bus_message_rewind(msg, TRUE); + + if (m == NULL) + m = create_message(msg, TRUE); + + if (h->handler(dbus, m, h->user_data)) + r = TRUE; + + goto out; + } + } + else { + if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL) + goto retry; + } + + out: + if (!r) + mrp_debug("Unhandled method path=%s, %s.%s.", SAFESTR(path), + SAFESTR(interface), SAFESTR(member)); + + mrp_dbus_msg_unref(m); + + return r; +} + + +static int dispatch_signal(sd_bus *bus,int ret, sd_bus_message *msg, void *data) +{ +#define MATCHES(h, field) (!*field || !h->field || !*h->field || \ + !strcmp(field, h->field)) + mrp_dbus_t *dbus = (mrp_dbus_t *)data; + mrp_dbus_msg_t *m = NULL; + const char *path = sd_bus_message_get_path(msg); + const char *interface = sd_bus_message_get_interface(msg); + const char *member = sd_bus_message_get_member(msg); + mrp_list_hook_t *p, *n; + handler_list_t *l; + handler_t *h; + int retried = FALSE; + int handled = FALSE; + + MRP_UNUSED(bus); + MRP_UNUSED(ret); + + if (!verify_type(msg, MRP_DBUS_MESSAGE_TYPE_SIGNAL) || !member) + return FALSE; + + mrp_debug("%s(path='%s', interface='%s', member='%s')...", + __FUNCTION__, SAFESTR(path), SAFESTR(interface), SAFESTR(member)); + + if ((l = mrp_htbl_lookup(dbus->signals, (void *)member)) != NULL) { + retry: + mrp_list_foreach(&l->handlers, p, n) { + h = mrp_list_entry(p, handler_t, hook); + + if (MATCHES(h,path) && MATCHES(h,interface) && MATCHES(h,member)) { + sd_bus_message_rewind(msg, TRUE); + + if (m == NULL) + m = create_message(msg, TRUE); + + h->handler(dbus, m, h->user_data); + handled = TRUE; + } + } + } + + if (!retried) { + if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) { + retried = TRUE; + goto retry; + } + } + + if (!handled) + mrp_debug("Unhandled signal path=%s, %s.%s.", SAFESTR(path), + SAFESTR(interface), SAFESTR(member)); + + mrp_dbus_msg_unref(m); + + return FALSE; +#undef MATCHES +#undef SAFESTR +} + + +static int append_args_strtype(mrp_dbus_msg_t *msg, const char *types, + va_list ap) +{ + MRP_UNUSED(msg); + MRP_UNUSED(types); + MRP_UNUSED(ap); + + return FALSE; +} + + +static int append_args_inttype(sd_bus_message *msg, int type, va_list ap) +{ + void *vptr; + int atype, elen, i; + void **aptr; + int alen; + char stype[2] = { '\0', '\0' }; + int r = 0; + + (void)append_args_strtype; + + while (type != MRP_DBUS_TYPE_INVALID) { + switch (type) { + case MRP_DBUS_TYPE_BYTE: + case MRP_DBUS_TYPE_BOOLEAN: + case MRP_DBUS_TYPE_INT16: + case MRP_DBUS_TYPE_UINT16: + case MRP_DBUS_TYPE_INT32: + case MRP_DBUS_TYPE_UINT32: + case MRP_DBUS_TYPE_INT64: + case MRP_DBUS_TYPE_UINT64: + case MRP_DBUS_TYPE_DOUBLE: + case MRP_DBUS_TYPE_STRING: + case MRP_DBUS_TYPE_OBJECT_PATH: + case MRP_DBUS_TYPE_SIGNATURE: + case MRP_DBUS_TYPE_UNIX_FD: + vptr = va_arg(ap, void *); + r = sd_bus_message_append_basic(msg, type, vptr); + break; + + case MRP_DBUS_TYPE_ARRAY: + atype = va_arg(ap, int); + aptr = va_arg(ap, void **); + alen = va_arg(ap, int); + + switch (atype) { +#define LEN(_type, _size) case MRP_DBUS_TYPE_##_type: elen = _size; break + LEN(BYTE , sizeof(uint8_t)); + LEN(BOOLEAN , sizeof(uint32_t)); + LEN(INT16 , sizeof(int16_t)); + LEN(UINT16 , sizeof(uint16_t)); + LEN(INT32 , sizeof(int32_t)); + LEN(UINT32 , sizeof(uint32_t)); + LEN(INT64 , sizeof(int64_t)); + LEN(UINT64 , sizeof(uint64_t)); + LEN(DOUBLE , sizeof(double)); + LEN(STRING , sizeof(const char *)); + LEN(OBJECT_PATH, sizeof(const char *)); + LEN(SIGNATURE , sizeof(const char *)); + LEN(UNIX_FD , sizeof(int)); +#undef LEN + default: + return FALSE; + } + + stype[0] = atype; + if (sd_bus_message_open_container(msg, type, stype) != 0) + return FALSE; + for (i = 0; i < alen; i++, aptr += elen) + if (sd_bus_message_append_basic(msg, atype, aptr) != 0) + return FALSE; + if (sd_bus_message_close_container(msg) != 0) + return FALSE; + else + return TRUE; + break; + + default: + return FALSE; + } + + type = va_arg(ap, int); + } + + return (r == 0 ? TRUE : FALSE); +} + + +static int call_reply_cb(sd_bus *bus, int ret, sd_bus_message *msg, + void *user_data) +{ + call_t *call = (call_t *)user_data; + mrp_dbus_msg_t *reply = create_message(msg, TRUE); + sd_bus_error error; + + MRP_UNUSED(bus); + + call->serial = 0; + mrp_list_delete(&call->hook); + + if (ret == 0) { + reply = create_message(msg, TRUE); + sd_bus_message_rewind(reply->msg, TRUE); + } + else { + sd_bus_message *err = NULL; + + if (ret == ETIMEDOUT) + error = SD_BUS_ERROR_MAKE(MRP_DBUS_ERROR_TIMEOUT, + "D-Bus call timed out"); + else + error = SD_BUS_ERROR_MAKE(MRP_DBUS_ERROR_FAILED, + "D-Bus call failed"); + + if (sd_bus_message_new_method_error(bus, call->msg, &error, &err) == 0) + reply = create_message(err, FALSE); + else + reply = NULL; + } + + call->cb(call->dbus, reply, call->user_data); + call_free(call); + mrp_dbus_msg_unref(reply); + + return TRUE; +} + + +int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, const char *path, + const char *interface, const char *member, int timeout, + mrp_dbus_reply_cb_t cb, void *user_data, int type, ...) +{ + va_list ap; + int32_t id; + call_t *call; + sd_bus_message *msg; + int success; + + call = NULL; + + if (sd_bus_message_new_method_call(dbus->bus, dest, path, interface, member, + &msg) != 0) + return 0; + + if (cb != NULL) { + if ((call = mrp_allocz(sizeof(*call))) != NULL) { + mrp_list_init(&call->hook); + + call->dbus = dbus; + call->id = dbus->call_id++; + call->cb = cb; + call->user_data = user_data; + + id = call->id; + } + else + goto fail; + } + else + id = dbus->call_id++; + + if (type == MRP_DBUS_TYPE_INVALID) + success = TRUE; + else { + va_start(ap, type); + success = append_args_inttype(msg, type, ap); + va_end(ap); + } + + if (!success) + goto fail; + + if (cb == NULL) { + sd_bus_message_set_no_reply(msg, TRUE); + if (sd_bus_send(dbus->bus, msg, NULL) != 0) + goto fail; + sd_bus_message_unref(msg); + } + else { + if (sd_bus_send_with_reply(dbus->bus, msg, call_reply_cb, call, + timeout * 1000, &call->serial) != 0) + goto fail; + + mrp_list_append(&dbus->calls, &call->hook); + call->msg = msg; + } + + return id; + + fail: + sd_bus_message_unref(msg); + call_free(call); + + return 0; +} + + +int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *m) +{ + /*bus_message_dump(m->msg);*/ + + if (sd_bus_send(dbus->bus, m->msg, NULL) == 0) + return TRUE; + else + return FALSE; +} + + +int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id) +{ + mrp_list_hook_t *p, *n; + call_t *call; + + mrp_list_foreach(&dbus->calls, p, n) { + call = mrp_list_entry(p, call_t, hook); + + if (call->id == id) { + mrp_list_delete(p); + + sd_bus_send_with_reply_cancel(dbus->bus, call->serial); + call->serial = 0; + + call_free(call); + return TRUE; + } + } + + return FALSE; +} + + +int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, int type, ...) +{ + va_list ap; + sd_bus_message *rpl; + int success; + + if (sd_bus_message_new_method_return(dbus->bus, m->msg, &rpl) != 0) + return FALSE; + + va_start(ap, type); + success = append_args_inttype(rpl, type, ap); + va_end(ap); + + if (!success) + goto fail; + + if (sd_bus_send(dbus->bus, rpl, NULL) != 0) + goto fail; + + sd_bus_message_unref(rpl); + + return TRUE; + + fail: + sd_bus_message_unref(rpl); + + return FALSE; +} + + +int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, + const char *errname, const char *errmsg, int type, ...) +{ + va_list ap; + sd_bus_message *rpl; + int success; + sd_bus_error err = SD_BUS_ERROR_NULL;; + + sd_bus_error_set_const(&err, errname, errmsg); + + if (sd_bus_message_new_method_error(dbus->bus, m->msg, &err, &rpl) != 0) + return FALSE; + + va_start(ap, type); + success = append_args_inttype(rpl, type, ap); + va_end(ap); + + if (!success) + goto fail; + + if (sd_bus_send(dbus->bus, rpl, NULL) != 0) + goto fail; + + sd_bus_message_unref(rpl); + + return TRUE; + + fail: + sd_bus_message_unref(rpl); + + return FALSE; +} + + +static void call_free(call_t *call) +{ + if (call != NULL) { + sd_bus_message_unref(call->msg); + mrp_free(call); + } +} + + +static void purge_calls(mrp_dbus_t *dbus) +{ + mrp_list_hook_t *p, *n; + call_t *call; + + mrp_list_foreach(&dbus->calls, p, n) { + call = mrp_list_entry(p, call_t, hook); + + mrp_list_delete(&call->hook); + + if (call->serial != 0) + sd_bus_send_with_reply_cancel(dbus->bus, call->serial); + + mrp_free(call); + } +} + + +int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path, + const char *interface, const char *member, int type, ...) +{ + va_list ap; + sd_bus_message *msg; + int success; + + if (sd_bus_message_new_signal(dbus->bus, path, interface, member, + &msg) != 0) + return 0; + + va_start(ap, type); + success = append_args_inttype(msg, type, ap); + va_end(ap); + + if (!success) + goto fail; + + if (dest != NULL) + if (sd_bus_message_set_destination(msg, dest) != 0) + goto fail; + + if (sd_bus_send(dbus->bus, msg, NULL) != 0) + goto fail; + + sd_bus_message_unref(msg); + + return TRUE; + + fail: + sd_bus_message_unref(msg); + + return 0; +} + + +mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *dbus, + const char *destination, + const char *path, + const char *interface, + const char *member) +{ + sd_bus_message *msg; + + if (sd_bus_message_new_method_call(dbus->bus, destination, + path, interface, member, &msg) == 0) + return create_message(msg, FALSE); + else + return NULL; +} + + +mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *dbus, + mrp_dbus_msg_t *msg) +{ + sd_bus_message *req, *rpl; + + req = (sd_bus_message *)msg; + + if (sd_bus_message_new_method_return(dbus->bus, req, &rpl) == 0) + return create_message(rpl, FALSE); + else + return NULL; +} + + +mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, + mrp_dbus_err_t *err) +{ + sd_bus_message *req, *rpl; + + req = m->msg; + + if (sd_bus_message_new_method_error(dbus->bus, req, err, &rpl) == 0) + return create_message(rpl, FALSE); + else + return NULL; +} + + +mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *dbus, + const char *destination, + const char *path, + const char *interface, + const char *member) +{ + sd_bus_message *msg = NULL; + + if (sd_bus_message_new_signal(dbus->bus, path, interface, member, + &msg) == 0) { + if (destination != NULL) { + if (sd_bus_message_set_destination(msg, destination) != 0) { + sd_bus_message_unref(msg); + msg = NULL; + } + } + } + + return create_message(msg, FALSE); +} + + +mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *m) +{ + uint8_t type; + + if (sd_bus_message_get_type(m->msg, &type) == 0) + return (mrp_dbus_msg_type_t)type; + else + return MRP_DBUS_MESSAGE_TYPE_INVALID; +} + +#define WRAP_GETTER(type, what) \ + type mrp_dbus_msg_##what(mrp_dbus_msg_t *m) \ + { \ + return sd_bus_message_get_##what((sd_bus_message *)m->msg); \ + } \ + struct __mrp_dbus_allow_trailing_semicolon + +WRAP_GETTER(const char *, path); +WRAP_GETTER(const char *, interface); +WRAP_GETTER(const char *, member); +WRAP_GETTER(const char *, destination); +WRAP_GETTER(const char *, sender); + +#undef WRAP_GETTER + + +int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type, + const char *contents) +{ + return sd_bus_message_open_container(m->msg, type, contents) == 0; +} + + +int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m) +{ + return sd_bus_message_close_container(m->msg) == 0; +} + + +int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep) +{ + return sd_bus_message_append_basic(m->msg, type, valuep) == 0; +} + + +int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *m, char type, + const char *contents) +{ + return sd_bus_message_enter_container(m->msg, type, contents) == 1; +} + + +int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m) +{ + return sd_bus_message_exit_container(m->msg) == 1; +} + + +int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep) +{ + return sd_bus_message_read_basic(m->msg, type, valuep) == 1; +} + + +int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type, + void **itemsp, size_t *nitemp) +{ + char sub[2] = { (char)type, '\0' }; + msg_array_t *a; + int offs; + size_t esize; + + if ((esize = element_size(type)) == 0) + return FALSE; + + if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sub)) + return FALSE; + + if ((a = mrp_allocz(sizeof(*a))) == NULL) + goto fail; + + a->type = type; + mrp_list_init(&a->hook); + + offs = 0; + while (mrp_dbus_msg_arg_type(m, NULL) != MRP_DBUS_TYPE_INVALID) { + if (!mrp_realloc(a->items, offs + esize)) + goto fail; + + if (!mrp_dbus_msg_read_basic(m, type, a->items + offs)) + goto fail; + else + a->nitem++; + + offs += esize; + } + + mrp_dbus_msg_exit_container(m); + + mrp_list_append(&m->arrays, &a->hook); + *itemsp = a->items; + *nitemp = a->nitem; + + return TRUE; + + fail: + mrp_dbus_msg_exit_container(m); + free_msg_array(a); + + return FALSE; +} + + +mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents) +{ + char type; + + if (sd_bus_message_peek_type(m->msg, &type, contents) >= 0) + return (mrp_dbus_type_t)type; + else + return MRP_DBUS_TYPE_INVALID; +} diff --git a/src/common/dbus-sdbus.h b/src/common/dbus-sdbus.h new file mode 100644 index 0000000..982b0f6 --- /dev/null +++ b/src/common/dbus-sdbus.h @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2012, 2013, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MURPHY_SD_BUS_H__ +#define __MURPHY_SD_BUS_H__ + +#include +#include + +#include +#include + +/** Type for a D-Bus (connection). */ +struct mrp_dbus_s; +typedef struct mrp_dbus_s mrp_dbus_t; + +/** Type for a D-Bus message. */ +typedef struct mrp_dbus_msg_s mrp_dbus_msg_t; + +/** Type for a D-Bus error. */ +typedef sd_bus_error mrp_dbus_err_t; + +/** D-BUS method or signal callback type. */ +typedef int (*mrp_dbus_handler_t)(mrp_dbus_t *, mrp_dbus_msg_t *, void *); + +/** Create a new connection to the given bus. */ +mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address, + mrp_dbus_err_t *errp); +#define mrp_dbus_get mrp_dbus_connect + +/** Set up an sd-bus instance with a mainloop. */ +int mrp_dbus_setup_sd_bus(mrp_mainloop_t *ml, sd_bus *bus); + +/** Increase the reference count of the given DBus (connection). */ +mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus); + +/** Decrease the reference count of the given DBus (connection). */ +int mrp_dbus_unref(mrp_dbus_t *dbus); + +/** Acquire the given name on the given bus (connection). */ +int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_err_t *error); + +/** Release the given name on the given bus (connection). */ +int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_err_t *error); + +/** Type for a name tracking callback. */ +typedef void (*mrp_dbus_name_cb_t)(mrp_dbus_t *, const char *, int, + const char *, void *); +/** Start tracking the given name. */ +int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_name_cb_t cb, void *user_data); +/** Stop tracking the given name. */ +int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name, + mrp_dbus_name_cb_t cb, void *user_data); + +/** Export a method to the bus. */ +int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data); + +/** Remove an exported method. */ +int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path, + const char *interface, const char *member, + mrp_dbus_handler_t handler, void *user_data); + +/** Install a filter and add a handler for the given signal on the bus. */ +MRP_NULLTERM int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus, + mrp_dbus_handler_t handler, + void *user_data, + const char *sender, + const char *path, + const char *interface, + const char *member, ...); + +/** Remove the signal handler and filter for the given signal on the bus. */ +MRP_NULLTERM int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus, + mrp_dbus_handler_t handler, + void *user_data, + const char *sender, + const char *path, + const char *interface, + const char *member, ...); + +/** Install a filter for the given message on the bus. */ +MRP_NULLTERM int mrp_dbus_install_filter(mrp_dbus_t *dbus, + const char *sender, + const char *path, + const char *interface, + const char *member, ...); + +/** Install a filter for the given message on the bus. */ +int mrp_dbus_install_filterv(mrp_dbus_t *dbus, + const char *sender, + const char *path, + const char *interface, + const char *member, + va_list ap); + +/** Remove a filter for the given message on the bus. */ +MRP_NULLTERM int mrp_dbus_remove_filter(mrp_dbus_t *dbus, + const char *sender, + const char *path, + const char *interface, + const char *member, ...); + +/** Remove a filter for the given message on the bus. */ +int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, + const char *sender, + const char *path, + const char *interface, + const char *member, + va_list ap); + +/** Add a signal handler for the gvien signal on the bus. */ +int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, mrp_dbus_handler_t handler, + void *user_data); + +/** Remove the given signal handler for the given signal on the bus. */ +int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender, + const char *path, const char *interface, + const char *member, mrp_dbus_handler_t handler, + void *user_data); + +/** Type of a method call reply callback. */ +typedef void (*mrp_dbus_reply_cb_t)(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply, + void *user_data); + +/** Call the given method on the bus. */ +int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, + const char *path, const char *interface, + const char *member, int timeout, + mrp_dbus_reply_cb_t cb, void *user_data, + int dbus_type, ...); + +/** Cancel an ongoing method call on the bus. */ +int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id); + +/** Send a reply to the given method call on the bus. */ +int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, int type, ...); + +/** Send an error reply to the given method call on the bus. */ +int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, + const char *errname, const char *errmsg, + int type, ...); + +/** Emit the given signal on the bus. */ +int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path, + const char *interface, const char *member, int type, ...); + +/** Send the given message on the bus. */ +int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg); + +/** Get our unique name on the bus. */ +const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus); + +/** Initialize the given error. */ +static inline mrp_dbus_err_t *mrp_dbus_error_init(mrp_dbus_err_t *err) +{ + if (err != NULL) + memset(err, 0, sizeof(*err)); + + return err; +} + + +/** Set the given error buffer up with the error name and message. */ +static inline mrp_dbus_err_t *mrp_dbus_error_set(mrp_dbus_err_t *err, + const char *name, + const char *message) +{ + sd_bus_error_set(err, name, "%s", message); + + return err; +} + + +/** Get the error message from the given bus error message. */ +static inline const char *mrp_dbus_errmsg(mrp_dbus_err_t *err) +{ + if (err && sd_bus_error_is_set(err)) + return err->message; + else + return "unknown DBUS error"; +} + + +/** Increase the reference count of a message. */ +mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m); + +/** Decrease the reference count of a message, freeing it if necessary. */ +int mrp_dbus_msg_unref(mrp_dbus_msg_t *m); + + +/** Create a new method call message. */ +mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *bus, + const char *destination, + const char *path, + const char *interface, + const char *member); + +/** Create a new method return message. */ +mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *bus, + mrp_dbus_msg_t *msg); + +/** Create a new error reply message. */ +mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *bus, mrp_dbus_msg_t *msg, + mrp_dbus_err_t *err); + +/** Create a new signal message. */ +mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *bus, + const char *destination, + const char *path, + const char *interface, + const char *member); + +/** Bus message types. */ +typedef enum { +#ifndef SD_BUS_MESSAGE_TYPE_INVALID +# define SD_BUS_MESSAGE_TYPE_INVALID _SD_BUS_MESSAGE_TYPE_INVALID +#endif +# define MAP(t, f) MRP_DBUS_MESSAGE_TYPE_##t = SD_BUS_MESSAGE_TYPE_##f + MAP(INVALID , INVALID), + MAP(METHOD_CALL , METHOD_CALL), + MAP(METHOD_RETURN, METHOD_RETURN), + MAP(ERROR , METHOD_ERROR), + MAP(SIGNAL , SIGNAL) +# undef MAP +} mrp_dbus_msg_type_t; + +/** Get the type of the given message. */ +mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *msg); + +/** Message type checking convenience functions. */ +#define TYPE_CHECK_FUNCTION(type, TYPE) \ + static inline int mrp_dbus_msg_is_##type(mrp_dbus_msg_t *msg) \ + { \ + return mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_##TYPE; \ + } \ + struct __mrp_dbus_allow_traling_semicolon + +TYPE_CHECK_FUNCTION(method_call , METHOD_CALL); +TYPE_CHECK_FUNCTION(method_return, METHOD_RETURN); +TYPE_CHECK_FUNCTION(error , ERROR); +TYPE_CHECK_FUNCTION(signal , SIGNAL); + +/** Message argument types. */ +typedef enum { +#ifndef SD_BUS_TYPE_INVALID +# define SD_BUS_TYPE_INVALID _SD_BUS_TYPE_INVALID +#endif +#define TYPE(t) MRP_DBUS_TYPE_##t = SD_BUS_TYPE_##t + TYPE(INVALID), + TYPE(BYTE), + TYPE(BOOLEAN), + TYPE(INT16), + TYPE(UINT16), + TYPE(INT32), + TYPE(UINT32), + TYPE(INT64), + TYPE(UINT64), + TYPE(DOUBLE), + TYPE(STRING), + TYPE(OBJECT_PATH), + TYPE(SIGNATURE), + TYPE(UNIX_FD), + TYPE(ARRAY), + TYPE(VARIANT), + TYPE(STRUCT), + TYPE(DICT_ENTRY), + TYPE(STRUCT_BEGIN), + TYPE(STRUCT_END), + TYPE(DICT_ENTRY_BEGIN), + TYPE(DICT_ENTRY_END) +#undef TYPE +} mrp_dbus_type_t; + +/** Message argument types as strings. */ +static const char _type_as_string[][2] = { +#define MAP(_type) [SD_BUS_TYPE_##_type] = { SD_BUS_TYPE_##_type, '\0' } + MAP(BYTE), + MAP(BOOLEAN), + MAP(INT16), + MAP(UINT16), + MAP(INT32), + MAP(UINT32), + MAP(INT64), + MAP(UINT64), + MAP(DOUBLE), + MAP(STRING), + MAP(OBJECT_PATH), + MAP(SIGNATURE), + MAP(UNIX_FD), + MAP(ARRAY), + MAP(VARIANT), + MAP(STRUCT), + MAP(DICT_ENTRY), + MAP(STRUCT_BEGIN), + MAP(STRUCT_END), + MAP(DICT_ENTRY_BEGIN), + MAP(DICT_ENTRY_END) +#undef MAP +}; + +#define _STRTYPE(_type) _type_as_string[SD_BUS_TYPE_##_type] +#define _EVAL(_type) _type +#define MRP_DBUS_TYPE_BYTE_AS_STRING _EVAL(_STRTYPE(BYTE)) +#define MRP_DBUS_TYPE_BOOLEAN_AS_STRING _EVAL(_STRTYPE(BOOLEAN)) +#define MRP_DBUS_TYPE_INT16_AS_STRING _EVAL(_STRTYPE(INT16)) +#define MRP_DBUS_TYPE_UINT16_AS_STRING _EVAL(_STRTYPE(UINT16)) +#define MRP_DBUS_TYPE_INT32_AS_STRING _EVAL(_STRTYPE(INT32)) +#define MRP_DBUS_TYPE_UINT32_AS_STRING _EVAL(_STRTYPE(UINT32)) +#define MRP_DBUS_TYPE_INT64_AS_STRING _EVAL(_STRTYPE(INT64)) +#define MRP_DBUS_TYPE_UINT64_AS_STRING _EVAL(_STRTYPE(UINT64)) +#define MRP_DBUS_TYPE_DOUBLE_AS_STRING _EVAL(_STRTYPE(DOUBLE)) +#define MRP_DBUS_TYPE_STRING_AS_STRING _EVAL(_STRTYPE(STRING)) +#define MRP_DBUS_TYPE_OBJECT_PATH_AS_STRING _EVAL(_STRTYPE(OBJECT_PATH)) +#define MRP_DBUS_TYPE_SIGNATURE_AS_STRING _EVAL(_STRTYPE(SIGNATURE)) +#define MRP_DBUS_TYPE_UNIX_FD_AS_STRING _EVAL(_STRTYPE(UNIX_FD)) +#define MRP_DBUS_TYPE_ARRAY_AS_STRING _EVAL(_STRTYPE(ARRAY)) +#define MRP_DBUS_TYPE_VARIANT_AS_STRING _EVAL(_STRTYPE(VARIANT)) +#define MRP_DBUS_TYPE_STRUCT_AS_STRING _EVAL(_STRTYPE(STRUCT)) +#define MRP_DBUS_TYPE_DICT_ENTRY_AS_STRING _EVAL(_STRTYPE(DICT_ENTRY)) + +/** Get the path of the given message. */ +const char *mrp_dbus_msg_path(mrp_dbus_msg_t *msg); + +/** Get the interface of the given message. */ +const char *mrp_dbus_msg_interface(mrp_dbus_msg_t *msg); + +/** Get the member of the given message. */ +const char *mrp_dbus_msg_member(mrp_dbus_msg_t *msg); + +/** Get the destination of the given message. */ +const char *mrp_dbus_msg_destination(mrp_dbus_msg_t *msg); + +/** Get the sender of the given message. */ +const char *mrp_dbus_msg_sender(mrp_dbus_msg_t *msg); + +/** Open a new container of the given type and cotained types. */ +int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type, + const char *contents); + +/** Close the current container. */ +int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m); + +/** Append an argument of a basic type to the given message. */ +int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep); + +/** Get the type of the current message argument. */ +mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents); + +/** Open the current container (of the given type and contents) for reading. */ +int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *msg, char type, + const char *contents); + +/** Exit from the container being currently read. */ +int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m); + +/** Read the next argument (of basic type) from the given message. */ +int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep); + +/** Read the next array of one of the basic types. */ +int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type, + void **itemsp, size_t *nitemp); + +/** Set up an sd_bus to be pumped by a murphy mainloop. */ +int mrp_dbus_setup_with_mainloop(mrp_mainloop_t *ml, sd_bus *bus); +#endif /* __MURPHY_SD_BUS_H__ */ diff --git a/src/common/dbus-transport.c b/src/common/dbus-transport.c index dd16dc6..59e0fcb 100644 --- a/src/common/dbus-transport.c +++ b/src/common/dbus-transport.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #define DBUS "dbus" diff --git a/src/common/libdbus-glue.c b/src/common/libdbus-glue.c new file mode 100644 index 0000000..d1bb093 --- /dev/null +++ b/src/common/libdbus-glue.c @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2012, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include + +typedef struct dbus_glue_s dbus_glue_t; + +typedef struct { + dbus_glue_t *glue; + mrp_io_watch_t *mw; + DBusWatch *dw; + mrp_list_hook_t hook; +} watch_t; + + +typedef struct { + dbus_glue_t *glue; + mrp_timer_t *mt; + DBusTimeout *dt; + mrp_list_hook_t hook; +} timeout_t; + + +struct dbus_glue_s { + DBusConnection *conn; + mrp_mainloop_t *ml; + mrp_list_hook_t watches; + mrp_list_hook_t timers; + mrp_deferred_t *pump; +}; + + +static dbus_int32_t data_slot = -1; + +static void dispatch_watch(mrp_io_watch_t *mw, int fd, mrp_io_event_t events, + void *user_data) +{ + watch_t *watch = (watch_t *)user_data; + DBusConnection *conn = watch->glue->conn; + unsigned int mask = 0; + + MRP_UNUSED(mw); + MRP_UNUSED(fd); + + if (events & MRP_IO_EVENT_IN) + mask |= DBUS_WATCH_READABLE; + if (events & MRP_IO_EVENT_OUT) + mask |= DBUS_WATCH_WRITABLE; + if (events & MRP_IO_EVENT_HUP) + mask |= DBUS_WATCH_HANGUP; + if (events & MRP_IO_EVENT_ERR) + mask |= DBUS_WATCH_ERROR; + + dbus_connection_ref(conn); + dbus_watch_handle(watch->dw, mask); + dbus_connection_unref(conn); +} + + +static void watch_freed_cb(void *data) +{ + watch_t *watch = (watch_t *)data; + + if (watch != NULL) { + mrp_list_delete(&watch->hook); + mrp_del_io_watch(watch->mw); + mrp_free(watch); + } +} + + +static dbus_bool_t add_watch(DBusWatch *dw, void *data) +{ + dbus_glue_t *glue = (dbus_glue_t *)data; + watch_t *watch; + mrp_io_watch_t *mw; + mrp_io_event_t mask; + int fd; + unsigned int flags; + + mrp_debug("adding D-BUS watch %p (%s)", dw, + dbus_watch_get_enabled(dw) ? "enabled" : "disabled"); + + if (!dbus_watch_get_enabled(dw)) + return TRUE; + + fd = dbus_watch_get_unix_fd(dw); + flags = dbus_watch_get_flags(dw); + mask = MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR; + + if (flags & DBUS_WATCH_READABLE) + mask |= MRP_IO_EVENT_IN; + if (flags & DBUS_WATCH_WRITABLE) + mask |= MRP_IO_EVENT_OUT; + + mrp_debug("event mask for fd %d: %s%s", fd, + mask & MRP_IO_EVENT_IN ? "read" : "", + mask & MRP_IO_EVENT_OUT ? "write" : ""); + + if ((watch = mrp_allocz(sizeof(*watch))) != NULL) { + mrp_list_init(&watch->hook); + mw = mrp_add_io_watch(glue->ml, fd, mask, dispatch_watch, watch); + + if (mw != NULL) { + watch->glue = glue; + watch->mw = mw; + watch->dw = dw; + dbus_watch_set_data(dw, watch, watch_freed_cb); + mrp_list_append(&glue->watches, &watch->hook); + + return TRUE; + } + else + mrp_free(watch); + } + + return FALSE; +} + + +static void del_watch(DBusWatch *dw, void *data) +{ + watch_t *watch = (watch_t *)dbus_watch_get_data(dw); + + MRP_UNUSED(data); + + mrp_debug("deleting D-BUS watch %p...", dw); + + if (watch != NULL) { + mrp_del_io_watch(watch->mw); + watch->mw = NULL; + } +} + + +static void toggle_watch(DBusWatch *dw, void *data) +{ + mrp_debug("Toggling D-BUS watch %p...", dw); + + if (dbus_watch_get_enabled(dw)) + add_watch(dw, data); + else + del_watch(dw, data); +} + + +static void dispatch_timeout(mrp_timer_t *mt, void *user_data) +{ + timeout_t *timer = (timeout_t *)user_data; + + MRP_UNUSED(mt); + + mrp_debug("dispatching D-BUS timeout %p...", timer->dt); + + dbus_timeout_handle(timer->dt); +} + + +static void timeout_freed_cb(void *data) +{ + timeout_t *timer = (timeout_t *)data; + + if (timer != NULL) { + mrp_list_delete(&timer->hook); + mrp_del_timer(timer->mt); + + mrp_free(timer); + } +} + + +static dbus_bool_t add_timeout(DBusTimeout *dt, void *data) +{ + dbus_glue_t *glue = (dbus_glue_t *)data; + timeout_t *timer; + mrp_timer_t *mt; + unsigned int msecs; + + mrp_debug("adding D-BUS timeout %p...", dt); + + if ((timer = mrp_allocz(sizeof(*timer))) != NULL) { + mrp_list_init(&timer->hook); + msecs = dbus_timeout_get_interval(dt); + mt = mrp_add_timer(glue->ml, msecs, dispatch_timeout, timer); + + if (mt != NULL) { + timer->glue = glue; + timer->mt = mt; + timer->dt = dt; + dbus_timeout_set_data(dt, timer, timeout_freed_cb); + mrp_list_append(&glue->timers, &timer->hook); + + return TRUE; + } + else + mrp_free(timer); + } + + return FALSE; +} + + +static void del_timeout(DBusTimeout *dt, void *data) +{ + timeout_t *timer = (timeout_t *)dbus_timeout_get_data(dt); + + MRP_UNUSED(data); + + mrp_debug("deleting D-BUS timeout %p...", dt); + + if (timer != NULL) { + mrp_del_timer(timer->mt); + timer->mt = NULL; + } +} + + +static void toggle_timeout(DBusTimeout *dt, void *data) +{ + mrp_debug("toggling D-BUS timeout %p...", dt); + + if (dbus_timeout_get_enabled(dt)) + add_timeout(dt, data); + else + del_timeout(dt, data); +} + + +static void wakeup_mainloop(void *data) +{ + dbus_glue_t *glue = (dbus_glue_t *)data; + + mrp_debug("waking up mainloop..."); + + mrp_enable_deferred(glue->pump); +} + + +static void glue_free_cb(void *data) +{ + dbus_glue_t *glue = (dbus_glue_t *)data; + mrp_list_hook_t *p, *n; + watch_t *watch; + timeout_t *timer; + + mrp_list_foreach(&glue->watches, p, n) { + watch = mrp_list_entry(p, typeof(*watch), hook); + + mrp_list_delete(&watch->hook); + mrp_del_io_watch(watch->mw); + + mrp_free(watch); + } + + mrp_list_foreach(&glue->timers, p, n) { + timer = mrp_list_entry(p, typeof(*timer), hook); + + mrp_list_delete(&timer->hook); + mrp_del_timer(timer->mt); + + mrp_free(timer); + } + + mrp_free(glue); +} + + +static void pump_cb(mrp_deferred_t *d, void *user_data) +{ + dbus_glue_t *glue = (dbus_glue_t *)user_data; + + mrp_debug("dispatching dbus connection %p...", glue->conn); + + if (dbus_connection_dispatch(glue->conn) == DBUS_DISPATCH_COMPLETE) + mrp_disable_deferred(d); +} + + +static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus status, + void *user_data) +{ + dbus_glue_t *glue = (dbus_glue_t *)user_data; + + MRP_UNUSED(conn); + + switch (status) { + case DBUS_DISPATCH_COMPLETE: + mrp_debug("dispatching status for %p: complete", conn); + mrp_disable_deferred(glue->pump); + break; + + case DBUS_DISPATCH_DATA_REMAINS: + case DBUS_DISPATCH_NEED_MEMORY: + default: + mrp_debug("dispatching status for %p: not complete yet", conn); + mrp_enable_deferred(glue->pump); + break; + } +} + + +int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn) +{ + dbus_glue_t *glue; + + if (!dbus_connection_allocate_data_slot(&data_slot)) + return FALSE; + + if (dbus_connection_get_data(conn, data_slot) != NULL) + return FALSE; + + if ((glue = mrp_allocz(sizeof(*glue))) != NULL) { + mrp_list_init(&glue->watches); + mrp_list_init(&glue->timers); + glue->pump = mrp_add_deferred(ml, pump_cb, glue); + + if (glue->pump == NULL) { + mrp_free(glue); + return FALSE; + } + + glue->ml = ml; + glue->conn = conn; + } + else + return FALSE; + + if (!dbus_connection_set_data(conn, data_slot, glue, glue_free_cb)) + return FALSE; + + dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb, + glue, NULL); + + dbus_connection_set_wakeup_main_function(conn, wakeup_mainloop, + glue, NULL); + + return + dbus_connection_set_watch_functions(conn, add_watch, del_watch, + toggle_watch, glue, NULL) && + dbus_connection_set_timeout_functions(conn, add_timeout, del_timeout, + toggle_timeout, glue, NULL); +} diff --git a/src/common/dbus.c b/src/common/libdbus.c similarity index 99% rename from src/common/dbus.c rename to src/common/libdbus.c index b6e683a..e956911 100644 --- a/src/common/dbus.c +++ b/src/common/libdbus.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #define DBUS_ADMIN_SERVICE "org.freedesktop.DBus" diff --git a/src/common/dbus.h b/src/common/libdbus.h similarity index 100% rename from src/common/dbus.h rename to src/common/libdbus.h diff --git a/src/common/murphy-dbus-libdbus.pc.in b/src/common/murphy-dbus-libdbus.pc.in new file mode 100644 index 0000000..d6efb6f --- /dev/null +++ b/src/common/murphy-dbus-libdbus.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: murphy-dbus +Description: Murphy policy framework, libdbus based dbus library. +Requires: murphy-common +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lmurphy-dbus-libdbus @LIBDBUS_LIBS@ +Cflags: -I${includedir} @LIBDBUS_CFLAGS@ diff --git a/src/common/murphy-dbus-sdbus.pc.in b/src/common/murphy-dbus-sdbus.pc.in new file mode 100644 index 0000000..94008ee --- /dev/null +++ b/src/common/murphy-dbus-sdbus.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: murphy-sd-bus +Description: Murphy policy framework, systemd-bus based dbus library. +Requires: murphy-common +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lmurphy-sd-bus @SDBUS_LIBS@ +Cflags: -I${includedir} @SDBUS_CFLAGS@ diff --git a/src/common/murphy-dbus.pc.in b/src/common/murphy-libdbus.pc.in similarity index 69% rename from src/common/murphy-dbus.pc.in rename to src/common/murphy-libdbus.pc.in index 0b77d58..bcda7b4 100644 --- a/src/common/murphy-dbus.pc.in +++ b/src/common/murphy-libdbus.pc.in @@ -7,5 +7,5 @@ Name: murphy-dbus Description: Murphy policy framework, dbus library. Requires: murphy-common Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lmurphy-dbus @DBUS_LIBS@ -Cflags: -I${includedir} @DBUS_CFLAGS@ +Libs: -L${libdir} -lmurphy-libdbus @LIBDBUS_LIBS@ +Cflags: -I${includedir} @LIBDBUS_CFLAGS@ diff --git a/src/common/tests/Makefile.am b/src/common/tests/Makefile.am index c13266a..50dac52 100644 --- a/src/common/tests/Makefile.am +++ b/src/common/tests/Makefile.am @@ -20,8 +20,8 @@ hash_test_LDADD = ../../libmurphy-common.la # mainloop test mainloop_test_SOURCES = mainloop-test.c -mainloop_test_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) -mainloop_test_LDADD = ../../libmurphy-common.la $(GLIB_LIBS) $(DBUS_LIBS) +mainloop_test_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) $(LIBDBUS_CFLAGS) +mainloop_test_LDADD = ../../libmurphy-common.la $(GLIB_LIBS) $(LIBDBUS_LIBS) if PULSE_ENABLED mainloop_test_CFLAGS += $(PULSE_CFLAGS) mainloop_test_LDADD += ../../libmurphy-pulse.la $(PULSE_LIBS) @@ -71,13 +71,46 @@ process_watch_test_SOURCES = process-test.c process_watch_test_CFLAGS = $(AM_CFLAGS) process_watch_test_LDADD = ../../libmurphy-common.la -if DBUS_ENABLED -transport_test_LDADD += ../../libmurphy-dbus.la +if LIBDBUS_ENABLED +transport_test_LDADD += ../../libmurphy-libdbus.la + +noinst_PROGRAMS += mainloop-test -# DBUS test +# DBUS tests +noinst_PROGRAMS += dbus-test dbus_test_SOURCES = dbus-test.c -dbus_test_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -dbus_test_LDADD = ../../libmurphy-dbus.la ../../libmurphy-common.la $(DBUS_LIBS) +dbus_test_CFLAGS = $(AM_CFLAGS) $(LIBDBUS_CFLAGS) +dbus_test_LDADD = ../../libmurphy-libdbus.la ../../libmurphy-common.la $(LIBDBUS_LIBS) + +noinst_PROGRAMS += libdbus-test libdbus-transport-test +libdbus_test_SOURCES = libdbus-test.c +libdbus_test_CFLAGS = $(AM_CFLAGS) $(LIBDBUS_CFLAGS) +libdbus_test_LDADD = ../../libmurphy-dbus-libdbus.la ../../libmurphy-common.la $(LIBDBUS_LIBS) + +libdbus_transport_test_SOURCES = libdbus-transport-test.c +libdbus_transport_test_CFLAGS = $(AM_CFLAGS) +libdbus_transport_test_LDADD = ../../libmurphy-common.la \ + ../../libmurphy-dbus-libdbus.la +endif + +if SDBUS_ENABLED +noinst_PROGRAMS += sdbus-test dbus-sdbus-test sdbus-transport-test sdbus-error-message + +sdbus_test_SOURCES = sdbus-test.c +sdbus_test_CFLAGS = $(AM_CFLAGS) $(SDBUS_CFLAGS) +sdbus_test_LDADD = ../../libmurphy-common.la $(SDBUS_LIBS) + +dbus_sdbus_test_SOURCES = dbus-sdbus-test.c +dbus_sdbus_test_CFLAGS = $(AM_CFLAGS) $(SDBUS_CFLAGS) +dbus_sdbus_test_LDADD = \ + ../../libmurphy-common.la \ + ../../libmurphy-dbus-sdbus.la + +sdbus_transport_test_SOURCES = libdbus-transport-test.c +sdbus_transport_test_CFLAGS = $(AM_CFLAGS) +sdbus_transport_test_LDADD = \ + ../../libmurphy-common.la \ + ../../libmurphy-dbus-sdbus.la endif # fragbuf test diff --git a/src/common/tests/dbus-sdbus-test.c b/src/common/tests/dbus-sdbus-test.c new file mode 100644 index 0000000..a7fcb86 --- /dev/null +++ b/src/common/tests/dbus-sdbus-test.c @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2012, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE +#include + +#include +#include + +#define SERVER_NAME "org.test.murphy-server" +#define SERVER_PATH "/server" +#define SERVER_INTERFACE "Murphy.Server" +#define PING "ping" +#define CLIENT_NAME "org.test.murphy-client" +#define CLIENT_PATH "/client" +#define CLIENT_INTERFACE "Murphy.Client" +#define PONG "pong" + + +typedef struct { + char *busaddr; + char *srvname; + int server; + int log_mask; + const char *log_target; + mrp_mainloop_t *ml; + mrp_timer_t *timer; + uint32_t seqno; + mrp_dbus_t *dbus; + const char *name; + int32_t cid; + int server_up; + int all_pongs; +} context_t; + + +static mrp_dbus_msg_t *create_pong_signal(mrp_dbus_t *dbus, const char *dest, + uint32_t seq) +{ + const char *sig = "u"; + mrp_dbus_msg_t *msg; + + msg = mrp_dbus_msg_signal(dbus, dest, SERVER_PATH, SERVER_INTERFACE, PONG); + + if (msg != NULL) { + if (mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, sig) && + mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT32, &seq) && + mrp_dbus_msg_close_container(msg)) + return msg; + else + mrp_dbus_msg_unref(msg); + } + + return NULL; +} + + +static uint32_t parse_pong_signal(mrp_dbus_msg_t *msg) +{ + const char *sig = "u"; + uint32_t seq; + + if (mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, sig) && + mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq) && + mrp_dbus_msg_exit_container(msg)) + return seq; + else + return (uint32_t)-1; +} + + +static int ping_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data) +{ + context_t *c = (context_t *)user_data; + mrp_dbus_msg_t *pong; + uint32_t seq; + const char *dest; + + MRP_UNUSED(c); + + if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq)) + mrp_log_info("-> ping request #%u", seq); + else + mrp_log_error("-> malformed ping request"); + + if (mrp_dbus_reply(dbus, msg, MRP_DBUS_TYPE_UINT32, &seq, + MRP_DBUS_TYPE_INVALID)) + mrp_log_info("<- ping reply #%u", seq); + else + mrp_log_error("Failed to send ping reply #%u.", seq); + + if (seq & 0x1) + dest = mrp_dbus_msg_sender(msg); + else + dest = NULL; + + if ((pong = create_pong_signal(dbus, dest, seq)) != NULL) { + if (mrp_dbus_send_msg(dbus, pong)) + mrp_log_info("<- pong %s #%u", dest ? "signal" : "broadcast", seq); + else + mrp_log_error("Failed to send pong signal #%u.", seq); + + mrp_dbus_msg_unref(pong); + } + else + mrp_log_error("Failed to create pong signal #%u.", seq); + + return TRUE; +} + + +static int name_owner_changed(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, + void *user_data) +{ + context_t *c = (context_t *)user_data; + const char *name, *prev, *next; + + MRP_UNUSED(c); + MRP_UNUSED(dbus); + + if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &name) && + mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &prev) && + mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &next)) + mrp_log_info("Name %s was reassigned from %s to %s...", name, + prev, next); + else + mrp_log_error("Failed to parse NameOwnerChanged signal."); + + return TRUE; +} + + +static void server_setup(context_t *c) +{ + c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL); + + if (c->dbus == NULL) { + mrp_log_error("Failed to create D-BUS connection to '%s' bus.", + c->busaddr); + exit(1); + } + + c->name = mrp_dbus_get_unique_name(c->dbus); + mrp_log_info("Our address is %s on the bus...", + c->name ? c->name : "unknown"); + + if (c->srvname && *c->srvname) { + if (!mrp_dbus_acquire_name(c->dbus, c->srvname, NULL)) { + mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.", + c->srvname, c->busaddr); + exit(1); + } + } + + if (!mrp_dbus_export_method(c->dbus, SERVER_PATH, SERVER_INTERFACE, + PING, ping_handler, c)) { + mrp_log_error("Failed to export D-BUS method '%s'.", PING); + exit(1); + } + + if (!mrp_dbus_subscribe_signal(c->dbus, name_owner_changed, c, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "NameOwnerChanged", + NULL)) { + mrp_log_error("Failed to subscribe to NameOwnerChanged signals."); + exit(1); + } +} + + +void server_cleanup(context_t *c) +{ + if (c->srvname && *c->srvname) + mrp_dbus_release_name(c->dbus, c->srvname, NULL); + + mrp_dbus_remove_method(c->dbus, SERVER_PATH, SERVER_INTERFACE, + PING, ping_handler, c); + mrp_dbus_unref(c->dbus); +} + + +static void ping_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data) +{ + context_t *c = (context_t *)user_data; + uint32_t seq; + + MRP_UNUSED(dbus); + MRP_UNUSED(user_data); + + if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_ERROR) { + mrp_log_error("Received errorping reply."); + + return; + } + + if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq)) + mrp_log_info("-> ping reply #%u", seq); + else + mrp_log_error("Received malformedping reply."); + + c->cid = 0; +} + + +static void ping_request(context_t *c) +{ + uint32_t seq; + + if (c->cid != 0) { + mrp_log_warning("Previous ping request still unanswered..."); + return; + } + + seq = c->seqno++; + c->cid = mrp_dbus_call(c->dbus, + c->srvname, SERVER_PATH, SERVER_INTERFACE, + PING, 500, ping_reply, c, + MRP_DBUS_TYPE_UINT32, &seq, + MRP_DBUS_TYPE_INVALID); + + if (c->cid > 0) + mrp_log_info("<- ping request #%u", seq); + else + mrp_log_warning("Failed to send ping request #%u.", seq); +} + + +static void send_cb(mrp_timer_t *t, void *user_data) +{ + context_t *c = (context_t *)user_data; + + MRP_UNUSED(t); + + ping_request(c); +} + + +static int pong_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data) +{ + context_t *c = (context_t *)user_data; + uint32_t seq; + + MRP_UNUSED(c); + MRP_UNUSED(dbus); + + if ((seq = parse_pong_signal(msg)) != (uint32_t)-1) + mrp_log_info("-> pong signal #%u", seq); + else + mrp_log_error("-> malformed pong signal"); + + return TRUE; +} + + +static void server_status_cb(mrp_dbus_t *dbus, const char *name, int up, + const char *owner, void *user_data) +{ + context_t *c = (context_t *)user_data; + + MRP_UNUSED(dbus); + MRP_UNUSED(name); + + if (up) { + mrp_log_info("%s came up (as %s)", name, owner); + + if (c->timer == NULL) { + c->timer = mrp_add_timer(c->ml, 1000, send_cb, c); + + if (c->timer == NULL) { + mrp_log_error("Failed to create D-BUS sending timer."); + exit(1); + } + } + } + else { + mrp_log_info("%s went down", name); + + if (c->timer != NULL) { + mrp_del_timer(c->timer); + c->timer = NULL; + } + } +} + + +static void client_setup(context_t *c) +{ + const char *dest; + + c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL); + + if (c->dbus == NULL) { + mrp_log_error("Failed to create D-BUS connection to '%s' bus.", + c->busaddr); + exit(1); + } + + c->name = mrp_dbus_get_unique_name(c->dbus); + mrp_log_info("Our address is %s on the bus...", + c->name ? c->name : "unknown"); + + mrp_dbus_follow_name(c->dbus, c->srvname, server_status_cb, c); + + if (c->all_pongs) { + mrp_log_info("Subscribing for all pong signals..."); + dest = NULL; + } + else { + mrp_log_info("Subscribing only for pong signals to us..."); + dest = c->name; + } + + if (!mrp_dbus_subscribe_signal(c->dbus, pong_handler, c, + dest, SERVER_PATH, SERVER_INTERFACE, + PONG, NULL)) { + mrp_log_error("Failed to subscribe for signal '%s/%s.%s'.", SERVER_PATH, + SERVER_INTERFACE, PONG); + exit(1); + } + + c->timer = mrp_add_timer(c->ml, 1000, send_cb, c); + + if (c->timer == NULL) { + mrp_log_error("Failed to create D-BUS sending timer."); + exit(1); + } +} + + +static void client_cleanup(context_t *c) +{ + mrp_dbus_forget_name(c->dbus, c->srvname, server_status_cb, c); + mrp_del_timer(c->timer); + mrp_dbus_unsubscribe_signal(c->dbus, pong_handler, c, + c->name, SERVER_PATH, SERVER_INTERFACE, + PONG, NULL); + mrp_dbus_unref(c->dbus); +} + + +static void print_usage(const char *argv0, int exit_code, const char *fmt, ...) +{ + va_list ap; + + if (fmt && *fmt) { + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + } + + printf("usage: %s [options]\n\n" + "The possible options are:\n" + " -s, --server run as test server (default)\n" + " -b, --bus connect the given D-BUS\n" + " If omitted, defaults to the session bus.\n" + " -a, --all-pongs subscribe for all pong signals\n" + " If omitted, only pong with the client address are handled.\n" + " -t, --log-target=TARGET log target to use\n" + " TARGET is one of stderr,stdout,syslog, or a logfile path\n" + " -l, --log-level=LEVELS logging level to use\n" + " LEVELS is a comma separated list of info, error and warning\n" + " -v, --verbose increase logging verbosity\n" + " -d, --debug site enable debug message for \n" + " -h, --help show help on usage\n", + argv0); + + if (exit_code < 0) + return; + else + exit(exit_code); +} + + +static void config_set_defaults(context_t *ctx) +{ + mrp_clear(ctx); + ctx->busaddr = "session"; + ctx->srvname = SERVER_NAME; + ctx->server = FALSE; + ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG); + ctx->log_target = MRP_LOG_TO_STDERR; +} + + +int parse_cmdline(context_t *ctx, int argc, char **argv) +{ +# define OPTIONS "sab:n:l:t:vd:h" + struct option options[] = { + { "server" , no_argument , NULL, 's' }, + { "bus" , required_argument, NULL, 'b' }, + { "name" , required_argument, NULL, 'n' }, + { "all-pongs" , no_argument , NULL, 'a' }, + { "log-level" , required_argument, NULL, 'l' }, + { "log-target", required_argument, NULL, 't' }, + { "verbose" , optional_argument, NULL, 'v' }, + { "debug" , required_argument, NULL, 'd' }, + { "help" , no_argument , NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + int opt; + + config_set_defaults(ctx); + + while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) { + switch (opt) { + case 's': + ctx->server = TRUE; + break; + + case 'b': + ctx->busaddr = optarg; + break; + + case 'n': + ctx->srvname = optarg; + break; + + case 'a': + ctx->all_pongs = TRUE; + break; + + case 'v': + ctx->log_mask <<= 1; + ctx->log_mask |= 1; + break; + + case 'l': + ctx->log_mask = mrp_log_parse_levels(optarg); + if (ctx->log_mask < 0) + print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg); + break; + + case 't': + ctx->log_target = mrp_log_parse_target(optarg); + if (!ctx->log_target) + print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg); + break; + + case 'd': + ctx->log_mask |= MRP_LOG_MASK_DEBUG; + mrp_debug_set_config(optarg); + mrp_debug_enable(TRUE); + break; + + case 'h': + print_usage(argv[0], -1, ""); + exit(0); + break; + + default: + print_usage(argv[0], EINVAL, "invalid option '%c'", opt); + } + } + + return TRUE; +} + + +static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data) +{ + mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h); + context_t *c = (context_t *)user_data; + + MRP_UNUSED(c); + + switch (signum) { + case SIGINT: + mrp_log_info("Got SIGINT, stopping..."); + if (ml != NULL) + mrp_mainloop_quit(ml, 0); + else + exit(0); + break; + + case SIGTERM: + mrp_log_info("Got SIGTERM, stopping..."); + if (ml != NULL) + mrp_mainloop_quit(ml, 0); + else + exit(0); + break; + } +} + + +int main(int argc, char *argv[]) +{ + context_t c; + + mrp_clear(&c); + + if (!parse_cmdline(&c, argc, argv)) + exit(1); + + mrp_log_set_mask(c.log_mask); + mrp_log_set_target(c.log_target); + + if (c.server) + mrp_log_info("Running as server, using D-BUS '%s'...", c.busaddr); + else + mrp_log_info("Running as client, using D-BUS '%s'...", c.busaddr); + + c.ml = mrp_mainloop_create(); + + if (c.ml == NULL) { + mrp_log_error("Failed to create mainloop."); + exit(1); + } + + mrp_add_sighandler(c.ml, SIGINT , signal_handler, &c); + + if (c.server) + server_setup(&c); + else + client_setup(&c); + + mrp_mainloop_run(c.ml); + + if (c.server) + server_cleanup(&c); + else + client_cleanup(&c); + + return 0; +} diff --git a/src/common/tests/dbus-test.c b/src/common/tests/dbus-test.c index 240903e..e0ab062 100644 --- a/src/common/tests/dbus-test.c +++ b/src/common/tests/dbus-test.c @@ -40,7 +40,7 @@ #include #include -#include +#include #define SERVER_NAME "org.test.murphy-server" #define SERVER_PATH "/server" @@ -54,6 +54,7 @@ typedef struct { char *busaddr; + char *srvname; int server; int log_mask; const char *log_target; @@ -121,10 +122,12 @@ static void server_setup(context_t *c) mrp_log_info("Our address is %s on the bus...", c->name ? c->name : "unknown"); - if (!mrp_dbus_acquire_name(c->dbus, SERVER_NAME, NULL)) { - mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.", - SERVER_NAME, c->busaddr); - exit(1); + if (c->srvname && *c->srvname) { + if (!mrp_dbus_acquire_name(c->dbus, c->srvname, NULL)) { + mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.", + c->srvname, c->busaddr); + exit(1); + } } if (!mrp_dbus_export_method(c->dbus, SERVER_PATH, SERVER_INTERFACE, @@ -137,7 +140,8 @@ static void server_setup(context_t *c) void server_cleanup(context_t *c) { - mrp_dbus_release_name(c->dbus, SERVER_NAME, NULL); + if (c->srvname && *c->srvname) + mrp_dbus_release_name(c->dbus, c->srvname, NULL); mrp_dbus_remove_method(c->dbus, SERVER_PATH, SERVER_INTERFACE, PING, ping_handler, c); mrp_dbus_unref(c->dbus); @@ -152,6 +156,23 @@ static void ping_reply(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data) MRP_UNUSED(dbus); MRP_UNUSED(user_data); + if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_ERROR) { + const char *ename, *emsg; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &ename, + DBUS_TYPE_STRING, &emsg, + DBUS_TYPE_INVALID)) { + ename = ""; + emsg = ""; + } + + mrp_log_error("Received error reply (%s, %s) to ping.", ename, emsg); + + c->cid = 0; + return; + } + if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &seq, DBUS_TYPE_INVALID)) @@ -174,7 +195,7 @@ static void ping_request(context_t *c) seq = c->seqno++; c->cid = mrp_dbus_call(c->dbus, - SERVER_NAME, SERVER_PATH, SERVER_INTERFACE, + c->srvname, SERVER_PATH, SERVER_INTERFACE, PING, 500, ping_reply, c, DBUS_TYPE_UINT32, &seq, DBUS_TYPE_INVALID); @@ -263,7 +284,7 @@ static void client_setup(context_t *c) mrp_log_info("Our address is %s on the bus...", c->name ? c->name : "unknown"); - mrp_dbus_follow_name(c->dbus, SERVER_NAME, server_status_cb, c); + mrp_dbus_follow_name(c->dbus, c->srvname, server_status_cb, c); if (c->all_pongs) { mrp_log_info("Subscribing for all pong signals..."); @@ -293,7 +314,7 @@ static void client_setup(context_t *c) static void client_cleanup(context_t *c) { - mrp_dbus_follow_name(c->dbus, SERVER_NAME, server_status_cb, c); + mrp_dbus_follow_name(c->dbus, c->srvname, server_status_cb, c); mrp_del_timer(c->timer); mrp_dbus_subscribe_signal(c->dbus, pong_handler, c, c->name, SERVER_PATH, SERVER_INTERFACE, @@ -339,6 +360,7 @@ static void config_set_defaults(context_t *ctx) { mrp_clear(ctx); ctx->busaddr = "session"; + ctx->srvname = SERVER_NAME; ctx->server = FALSE; ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG); ctx->log_target = MRP_LOG_TO_STDERR; @@ -347,10 +369,11 @@ static void config_set_defaults(context_t *ctx) int parse_cmdline(context_t *ctx, int argc, char **argv) { -# define OPTIONS "sab:l:t:vdh" +# define OPTIONS "sab:n:l:t:vdh" struct option options[] = { { "server" , no_argument , NULL, 's' }, { "bus" , required_argument, NULL, 'b' }, + { "name" , required_argument, NULL, 'n' }, { "all-pongs" , no_argument , NULL, 'a' }, { "log-level" , required_argument, NULL, 'l' }, { "log-target", required_argument, NULL, 't' }, @@ -375,6 +398,10 @@ int parse_cmdline(context_t *ctx, int argc, char **argv) ctx->busaddr = optarg; break; + case 'n': + ctx->srvname = optarg; + break; + case 'a': ctx->all_pongs = TRUE; break; diff --git a/src/common/tests/libdbus-test.c b/src/common/tests/libdbus-test.c new file mode 100644 index 0000000..1f68bc4 --- /dev/null +++ b/src/common/tests/libdbus-test.c @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2012, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE +#include + +#include +#include + +#define SERVER_NAME "org.test.murphy-server" +#define SERVER_PATH "/server" +#define SERVER_INTERFACE "Murphy.Server" +#define PING "ping" +#define CLIENT_NAME "org.test.murphy-client" +#define CLIENT_PATH "/client" +#define CLIENT_INTERFACE "Murphy.Client" +#define PONG "pong" + + +typedef struct { + char *busaddr; + int server; + int log_mask; + const char *log_target; + mrp_mainloop_t *ml; + mrp_timer_t *timer; + uint32_t seqno; + mrp_dbus_t *dbus; + const char *name; + int32_t cid; + int server_up; + int all_pongs; +} context_t; + + +static int ping_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data) +{ + context_t *c = (context_t *)user_data; + uint32_t seq; + const char *dest; + + MRP_UNUSED(c); + + if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_METHOD_CALL) { + if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq)) + mrp_log_info("-> ping request #%u", seq); + else + mrp_log_error("-> malformed ping request"); + + if (!mrp_dbus_reply(dbus, msg, + MRP_DBUS_TYPE_UINT32, &seq, + MRP_DBUS_TYPE_INVALID)) + mrp_log_error("Failed to send ping reply #%u.", seq); + else + mrp_log_info("<- ping reply #%u", seq); + + if (seq & 0x1) + dest = mrp_dbus_msg_sender(msg); + else + dest = NULL; + + if (!mrp_dbus_signal(dbus, dest, SERVER_PATH, SERVER_INTERFACE, PONG, + MRP_DBUS_TYPE_UINT32, &seq, + MRP_DBUS_TYPE_INVALID)) + mrp_log_error("Failed to send pong signal #%u.", seq); + else + mrp_log_info("<- pong %s #%u", dest ? "signal" : "broadcast", seq); + } + + return TRUE; +} + + +static void server_setup(context_t *c) +{ + c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL); + + if (c->dbus == NULL) { + mrp_log_error("Failed to create D-BUS connection to '%s' bus.", + c->busaddr); + exit(1); + } + + c->name = mrp_dbus_get_unique_name(c->dbus); + mrp_log_info("Our address is %s on the bus...", + c->name ? c->name : "unknown"); + + if (!mrp_dbus_acquire_name(c->dbus, SERVER_NAME, NULL)) { + mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.", + SERVER_NAME, c->busaddr); + exit(1); + } + + if (!mrp_dbus_export_method(c->dbus, SERVER_PATH, SERVER_INTERFACE, + PING, ping_handler, c)) { + mrp_log_error("Failed to export D-BUS method '%s'.", PING); + exit(1); + } +} + + +void server_cleanup(context_t *c) +{ + mrp_dbus_release_name(c->dbus, SERVER_NAME, NULL); + mrp_dbus_remove_method(c->dbus, SERVER_PATH, SERVER_INTERFACE, + PING, ping_handler, c); + mrp_dbus_unref(c->dbus); +} + + +static void ping_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data) +{ + context_t *c = (context_t *)user_data; + uint32_t seq; + + MRP_UNUSED(dbus); + MRP_UNUSED(user_data); + + if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq)) + mrp_log_info("-> ping reply #%u", seq); + else + mrp_log_error("Received malformed ping reply."); + + c->cid = 0; +} + + +static void ping_request(context_t *c) +{ + uint32_t seq; + + if (c->cid != 0) { + mrp_log_warning("Previous ping request still unanswered..."); + return; + } + + seq = c->seqno++; + c->cid = mrp_dbus_call(c->dbus, + SERVER_NAME, SERVER_PATH, SERVER_INTERFACE, + PING, 500, ping_reply, c, + MRP_DBUS_TYPE_UINT32, &seq, + MRP_DBUS_TYPE_INVALID); + + if (c->cid > 0) + mrp_log_info("<- ping request #%u", seq); + else + mrp_log_warning("Failed to send ping request #%u.", seq); +} + + +static void send_cb(mrp_timer_t *t, void *user_data) +{ + context_t *c = (context_t *)user_data; + + MRP_UNUSED(t); + + ping_request(c); +} + + +static int pong_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data) +{ + context_t *c = (context_t *)user_data; + uint32_t seq; + + MRP_UNUSED(c); + MRP_UNUSED(dbus); + + if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_SIGNAL) { + if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq)) + mrp_log_info("-> pong signal #%u", seq); + else + mrp_log_error("-> malformed pong signal"); + } + + return TRUE; +} + + +static void server_status_cb(mrp_dbus_t *dbus, const char *name, int up, + const char *owner, void *user_data) +{ + context_t *c = (context_t *)user_data; + + MRP_UNUSED(dbus); + MRP_UNUSED(name); + + if (up) { + mrp_log_info("%s came up (as %s)", name, owner); + + if (c->timer == NULL) { + c->timer = mrp_add_timer(c->ml, 1000, send_cb, c); + + if (c->timer == NULL) { + mrp_log_error("Failed to create D-BUS sending timer."); + exit(1); + } + } + } + else { + mrp_log_info("%s went down", name); + + if (c->timer != NULL) { + mrp_del_timer(c->timer); + c->timer = NULL; + } + } +} + + +static void client_setup(context_t *c) +{ + const char *dest; + + c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL); + + if (c->dbus == NULL) { + mrp_log_error("Failed to create D-BUS connection to '%s' bus.", + c->busaddr); + exit(1); + } + + c->name = mrp_dbus_get_unique_name(c->dbus); + mrp_log_info("Our address is %s on the bus...", + c->name ? c->name : "unknown"); + + mrp_dbus_follow_name(c->dbus, SERVER_NAME, server_status_cb, c); + + if (c->all_pongs) { + mrp_log_info("Subscribing for all pong signals..."); + dest = NULL; + } + else { + mrp_log_info("Subscribing only for pong signals to us..."); + dest = c->name; + } + + if (!mrp_dbus_subscribe_signal(c->dbus, pong_handler, c, + dest, SERVER_PATH, SERVER_INTERFACE, + PONG, NULL)) { + mrp_log_error("Failed to subscribe for signal '%s/%s.%s'.", SERVER_PATH, + SERVER_INTERFACE, PONG); + exit(1); + } + + c->timer = mrp_add_timer(c->ml, 1000, send_cb, c); + + if (c->timer == NULL) { + mrp_log_error("Failed to create D-BUS sending timer."); + exit(1); + } +} + + +static void client_cleanup(context_t *c) +{ + mrp_dbus_follow_name(c->dbus, SERVER_NAME, server_status_cb, c); + mrp_del_timer(c->timer); + mrp_dbus_subscribe_signal(c->dbus, pong_handler, c, + c->name, SERVER_PATH, SERVER_INTERFACE, + PONG, NULL); + mrp_dbus_unref(c->dbus); +} + + +static void print_usage(const char *argv0, int exit_code, const char *fmt, ...) +{ + va_list ap; + + if (fmt && *fmt) { + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + } + + printf("usage: %s [options]\n\n" + "The possible options are:\n" + " -s, --server run as test server (default)\n" + " -b, --bus connect the given D-BUS\n" + " If omitted, defaults to the session bus.\n" + " -a, --all-pongs subscribe for all pong signals\n" + " If omitted, only pong with the client address are handled.\n" + " -t, --log-target=TARGET log target to use\n" + " TARGET is one of stderr,stdout,syslog, or a logfile path\n" + " -l, --log-level=LEVELS logging level to use\n" + " LEVELS is a comma separated list of info, error and warning\n" + " -v, --verbose increase logging verbosity\n" + " -d, --debug enable debug messages\n" + " -h, --help show help on usage\n", + argv0); + + if (exit_code < 0) + return; + else + exit(exit_code); +} + + +static void config_set_defaults(context_t *ctx) +{ + mrp_clear(ctx); + ctx->busaddr = "session"; + ctx->server = FALSE; + ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG); + ctx->log_target = MRP_LOG_TO_STDERR; +} + + +int parse_cmdline(context_t *ctx, int argc, char **argv) +{ +# define OPTIONS "sab:l:t:vdh" + struct option options[] = { + { "server" , no_argument , NULL, 's' }, + { "bus" , required_argument, NULL, 'b' }, + { "all-pongs" , no_argument , NULL, 'a' }, + { "log-level" , required_argument, NULL, 'l' }, + { "log-target", required_argument, NULL, 't' }, + { "verbose" , optional_argument, NULL, 'v' }, + { "debug" , no_argument , NULL, 'd' }, + { "help" , no_argument , NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + int opt, debug; + + debug = FALSE; + config_set_defaults(ctx); + + while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) { + switch (opt) { + case 's': + ctx->server = TRUE; + break; + + case 'b': + ctx->busaddr = optarg; + break; + + case 'a': + ctx->all_pongs = TRUE; + break; + + case 'v': + ctx->log_mask <<= 1; + ctx->log_mask |= 1; + break; + + case 'l': + ctx->log_mask = mrp_log_parse_levels(optarg); + if (ctx->log_mask < 0) + print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg); + break; + + case 't': + ctx->log_target = mrp_log_parse_target(optarg); + if (!ctx->log_target) + print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg); + break; + + case 'd': + debug = TRUE; + break; + + case 'h': + print_usage(argv[0], -1, ""); + exit(0); + break; + + default: + print_usage(argv[0], EINVAL, "invalid option '%c'", opt); + } + } + + if (debug) + ctx->log_mask |= MRP_LOG_MASK_DEBUG; + + return TRUE; +} + + +static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data) +{ + mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h); + context_t *c = (context_t *)user_data; + + MRP_UNUSED(c); + + switch (signum) { + case SIGINT: + mrp_log_info("Got SIGINT, stopping..."); + if (ml != NULL) + mrp_mainloop_quit(ml, 0); + else + exit(0); + break; + + case SIGTERM: + mrp_log_info("Got SIGTERM, stopping..."); + if (ml != NULL) + mrp_mainloop_quit(ml, 0); + else + exit(0); + break; + } +} + + +int main(int argc, char *argv[]) +{ + context_t c; + + mrp_clear(&c); + + if (!parse_cmdline(&c, argc, argv)) + exit(1); + + mrp_log_set_mask(c.log_mask); + mrp_log_set_target(c.log_target); + + if (c.server) + mrp_log_info("Running as server, using D-BUS '%s'...", c.busaddr); + else + mrp_log_info("Running as client, using D-BUS '%s'...", c.busaddr); + + c.ml = mrp_mainloop_create(); + + if (c.ml == NULL) { + mrp_log_error("Failed to create mainloop."); + exit(1); + } + + mrp_add_sighandler(c.ml, SIGINT , signal_handler, &c); + + if (c.server) + server_setup(&c); + else + client_setup(&c); + + mrp_mainloop_run(c.ml); + + if (c.server) + server_cleanup(&c); + else + client_cleanup(&c); + + return 0; +} diff --git a/src/common/tests/libdbus-transport-test.c b/src/common/tests/libdbus-transport-test.c new file mode 100644 index 0000000..06c2320 --- /dev/null +++ b/src/common/tests/libdbus-transport-test.c @@ -0,0 +1,847 @@ +/* + * Copyright (c) 2012, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE +#include + +#include + +/* + * tags for generic message fields + */ + +#define TAG_SEQ ((uint16_t)0x1) +#define TAG_MSG ((uint16_t)0x2) +#define TAG_U8 ((uint16_t)0x3) +#define TAG_S8 ((uint16_t)0x4) +#define TAG_U16 ((uint16_t)0x5) +#define TAG_S16 ((uint16_t)0x6) +#define TAG_DBL ((uint16_t)0x7) +#define TAG_BLN ((uint16_t)0x8) +#define TAG_ASTR ((uint16_t)0x9) +#define TAG_AU32 ((uint16_t)0xa) +#define TAG_RPL ((uint16_t)0xb) +#define TAG_END MRP_MSG_FIELD_END + +#define U32_GUARD (uint32_t)-1 + +/* + * our test custom data type + */ + +#define TAG_CUSTOM 0x1 + +typedef struct { + uint32_t seq; + char *msg; + uint8_t u8; + int8_t s8; + uint16_t u16; + int16_t s16; + double dbl; + bool bln; + char **astr; + uint32_t nstr; + uint32_t fsck; + uint32_t *au32; + char *rpl; +} custom_t; + + +MRP_DATA_DESCRIPTOR(custom_descr, TAG_CUSTOM, custom_t, + MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32), + MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING), + MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ), + MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ), + MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16), + MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16), + MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE), + MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ), + MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING), + MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32), + MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32), + MRP_DATA_ARRAY_COUNT(custom_t, astr, nstr, + MRP_MSG_FIELD_STRING), + MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD, + MRP_MSG_FIELD_UINT32)); + +MRP_DATA_DESCRIPTOR(buggy_descr, TAG_CUSTOM, custom_t, + MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32), + MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING), + MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ), + MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ), + MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16), + MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16), + MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE), + MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ), + MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING), + MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32), + MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32), + MRP_DATA_ARRAY_COUNT(custom_t, astr, fsck, + MRP_MSG_FIELD_STRING), + MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD, + MRP_MSG_FIELD_UINT32)); + +mrp_data_descr_t *data_descr; + + +typedef enum { + MODE_DEFAULT = 0, + MODE_MESSAGE = 1, + MODE_DATA = 2, + MODE_RAW = 3, +} msg_mode_t; + + +typedef struct { + mrp_mainloop_t *ml; + mrp_transport_t *lt, *t; + char *addrstr; + mrp_sockaddr_t addr; + socklen_t alen; + const char *atype; + int server; + int sock; + mrp_io_watch_t *iow; + mrp_timer_t *timer; + int mode; + int buggy; + int connect; + int stream; + int log_mask; + const char *log_target; + uint32_t seqno; +} context_t; + + +void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data); +void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr, + socklen_t addrlen, void *user_data); + +void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data); +void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag, + mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data); + +void recvraw(mrp_transport_t *t, void *data, size_t size, void *user_data); +void recvrawfrom(mrp_transport_t *t, void *data, size_t size, + mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data); + + +void dump_msg(mrp_msg_t *msg, FILE *fp) +{ + mrp_msg_dump(msg, fp); +} + + +void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr, + socklen_t addrlen, void *user_data) +{ + context_t *c = (context_t *)user_data; + mrp_msg_field_t *f; + uint32_t seq; + char buf[256]; + int status; + + mrp_log_info("received a message"); + dump_msg(msg, stdout); + + if (c->server) { + seq = 0; + if ((f = mrp_msg_find(msg, TAG_SEQ)) != NULL) { + if (f->type == MRP_MSG_FIELD_UINT32) + seq = f->u32; + } + + snprintf(buf, sizeof(buf), "reply to message #%u", seq); + + if (!mrp_msg_append(msg, TAG_RPL, MRP_MSG_FIELD_STRING, buf, + TAG_END)) { + mrp_log_info("failed to append to received message"); + exit(1); + } + + if (c->connect) + status = mrp_transport_send(t, msg); + else + status = mrp_transport_sendto(t, msg, addr, addrlen); + + if (status) + mrp_log_info("reply successfully sent"); + else + mrp_log_error("failed to send reply"); + + /* message unreffed by transport layer */ + } +} + + +void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data) +{ + return recvfrom_msg(t, msg, NULL, 0, user_data); +} + + +void dump_custom(custom_t *msg, FILE *fp) +{ + uint32_t i; + + mrp_data_dump(msg, data_descr, fp); + fprintf(fp, "{\n"); + fprintf(fp, " seq = %u\n" , msg->seq); + fprintf(fp, " msg = '%s'\n", msg->msg); + fprintf(fp, " u8 = %u\n" , msg->u8); + fprintf(fp, " s8 = %d\n" , msg->s8); + fprintf(fp, " u16 = %u\n" , msg->u16); + fprintf(fp, " s16 = %d\n" , msg->s16); + fprintf(fp, " dbl = %f\n" , msg->dbl); + fprintf(fp, " bln = %s\n" , msg->bln ? "true" : "false"); + fprintf(fp, " astr = (%u)\n", msg->nstr); + for (i = 0; i < msg->nstr; i++) + fprintf(fp, " %s\n", msg->astr[i]); + fprintf(fp, " au32 =\n"); + for (i = 0; msg->au32[i] != U32_GUARD; i++) + fprintf(fp, " %u\n", msg->au32[i]); + fprintf(fp, " rpl = '%s'\n", msg->rpl); + fprintf(fp, "}\n"); +} + + +void free_custom(custom_t *msg) +{ + mrp_data_free(msg, data_descr->tag); +} + + +void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag, + mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data) +{ + context_t *c = (context_t *)user_data; + custom_t *msg = (custom_t *)data; + custom_t rpl; + char buf[256]; + uint32_t au32[] = { 9, 8, 7, 6, 5, -1 }; + int status; + + mrp_log_info("received custom message of type 0x%x", tag); + dump_custom(data, stdout); + + if (tag != data_descr->tag) { + mrp_log_error("Tag 0x%x != our custom type (0x%x).", + tag, data_descr->tag); + exit(1); + } + + if (c->server) { + rpl = *msg; + snprintf(buf, sizeof(buf), "reply to message #%u", msg->seq); + rpl.rpl = buf; + rpl.au32 = au32; + + if (c->connect) + status = mrp_transport_senddata(t, &rpl, data_descr->tag); + else + status = mrp_transport_senddatato(t, &rpl, data_descr->tag, + addr, addrlen); + if (status) + mrp_log_info("reply successfully sent"); + else + mrp_log_error("failed to send reply"); + } + + free_custom(msg); +} + + +void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data) +{ + recvfrom_data(t, data, tag, NULL, 0, user_data); +} + + +void dump_raw(void *data, size_t size, FILE *fp) +{ + int len = (int)size; + + fprintf(fp, "[%*.*s]\n", len, len, (char *)data); +} + + +void recvfrom_raw(mrp_transport_t *t, void *data, size_t size, + mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data) +{ + context_t *c = (context_t *)user_data; + char rpl[256]; + size_t rpl_size; + int status; + + rpl_size = snprintf(rpl, sizeof(rpl), "reply to message [%*.*s]", + (int)size, (int)size, (char *)data); + + mrp_log_info("received raw message"); + dump_raw(data, size, stdout); + + if (strncmp((char *)data, "reply to ", 9) != 0) { + if (c->connect) + status = mrp_transport_sendraw(t, rpl, rpl_size); + else + status = mrp_transport_sendrawto(t, rpl, rpl_size, addr, addrlen); + + if (status) + mrp_log_info("reply successfully sent"); + else + mrp_log_error("failed to send reply"); + } +} + + +void recv_raw(mrp_transport_t *t, void *data, size_t size, void *user_data) +{ + recvfrom_raw(t, data, size, NULL, 0, user_data); +} + + + +void closed_evt(mrp_transport_t *t, int error, void *user_data) +{ + context_t *c = (context_t *)user_data; + + MRP_UNUSED(t); + MRP_UNUSED(c); + + if (error) { + mrp_log_error("Connection closed with error %d (%s).", error, + strerror(error)); + exit(1); + } + else { + mrp_log_info("Peer has closed the connection."); + exit(0); + } +} + + +void connection_evt(mrp_transport_t *lt, void *user_data) +{ + context_t *c = (context_t *)user_data; + int flags; + + flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK; + c->t = mrp_transport_accept(lt, c, flags); + + if (c->t == NULL) { + mrp_log_error("Failed to accept new connection."); + exit(1); + } +} + + +void type_init(context_t *c) +{ + if (c->buggy && c->server) { + data_descr = &buggy_descr; + mrp_log_info("Deliberately using buggy data descriptor..."); + } + else + data_descr = &custom_descr; + + if (!mrp_msg_register_type(data_descr)) { + mrp_log_error("Failed to register custom data type."); + exit(1); + } +} + + +void server_init(context_t *c) +{ + static mrp_transport_evt_t evt = { + { .recvmsg = NULL }, + { .recvmsgfrom = NULL }, + .closed = NULL, + .connection = NULL, + }; + + int flags; + + type_init(c); + + switch (c->mode) { + case MODE_DATA: + evt.recvdata = recv_data; + evt.recvdatafrom = recvfrom_data; + break; + case MODE_RAW: + evt.recvraw = recv_raw; + evt.recvrawfrom = recvfrom_raw; + break; + case MODE_MESSAGE: + default: + evt.recvmsg = recv_msg; + evt.recvmsgfrom = recvfrom_msg; + } + + if (c->stream) { + evt.connection = connection_evt; + evt.closed = closed_evt; + } + + flags = MRP_TRANSPORT_REUSEADDR; + + switch (c->mode) { + case MODE_DATA: flags |= MRP_TRANSPORT_MODE_DATA; break; + case MODE_RAW: flags |= MRP_TRANSPORT_MODE_RAW; break; + default: + case MODE_MESSAGE: flags |= MRP_TRANSPORT_MODE_MSG; + } + + c->lt = mrp_transport_create(c->ml, c->atype, &evt, c, flags); + + if (c->lt == NULL) { + mrp_log_error("Failed to create listening server transport."); + exit(1); + } + + if (!mrp_transport_bind(c->lt, &c->addr, c->alen)) { + mrp_log_error("Failed to bind transport to address %s.", c->addrstr); + exit(1); + } + + if (c->stream) { + if (!mrp_transport_listen(c->lt, 0)) { + mrp_log_error("Failed to listen on server transport."); + exit(1); + } + } +} + + +void send_msg(context_t *c) +{ + mrp_msg_t *msg; + uint32_t seq; + char buf[256]; + char *astr[] = { "this", "is", "an", "array", "of", "strings" }; + uint32_t au32[] = { 1, 2, 3, + 1 << 16, 2 << 16, 3 << 16, + 1 << 24, 2 << 24, 3 << 24 }; + uint32_t nstr = MRP_ARRAY_SIZE(astr); + uint32_t nu32 = MRP_ARRAY_SIZE(au32); + int status; + + seq = c->seqno++; + snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq); + + msg = mrp_msg_create(TAG_SEQ , MRP_MSG_FIELD_UINT32, seq, + TAG_MSG , MRP_MSG_FIELD_STRING, buf, + TAG_U8 , MRP_MSG_FIELD_UINT8 , seq & 0xf, + TAG_S8 , MRP_MSG_FIELD_SINT8 , -(seq & 0xf), + TAG_U16 , MRP_MSG_FIELD_UINT16, seq, + TAG_S16 , MRP_MSG_FIELD_SINT16, - seq, + TAG_DBL , MRP_MSG_FIELD_DOUBLE, seq / 3.0, + TAG_BLN , MRP_MSG_FIELD_BOOL , seq & 0x1, + TAG_ASTR, MRP_MSG_FIELD_ARRAY_OF(STRING), nstr, astr, + TAG_AU32, MRP_MSG_FIELD_ARRAY_OF(UINT32), nu32, au32, + TAG_END); + + if (msg == NULL) { + mrp_log_error("Failed to create new message."); + exit(1); + } + + if (c->connect) + status = mrp_transport_send(c->t, msg); + else + status = mrp_transport_sendto(c->t, msg, &c->addr, c->alen); + + if (!status) { + mrp_log_error("Failed to send message #%d.", seq); + exit(1); + } + else + mrp_log_info("Message #%d succesfully sent.", seq); + + mrp_msg_unref(msg); +} + + +void send_data(context_t *c) +{ + uint32_t seq = c->seqno++; + custom_t msg; + char buf[256]; + char *astr[] = { "this", "is", "a", "test", "string", "array" }; + uint32_t au32[] = { 1, 2, 3, 4, 5, 6, 7, -1 }; + int status; + + msg.seq = seq; + snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq); + msg.msg = buf; + msg.u8 = seq & 0xf; + msg.s8 = -(seq & 0xf); + msg.u16 = seq; + msg.s16 = - seq; + msg.dbl = seq / 3.0; + msg.bln = seq & 0x1; + msg.astr = astr; + msg.nstr = MRP_ARRAY_SIZE(astr); + msg.fsck = 1000; + msg.au32 = au32; + msg.rpl = ""; + + if (c->connect) + status = mrp_transport_senddata(c->t, &msg, data_descr->tag); + else + status = mrp_transport_senddatato(c->t, &msg, data_descr->tag, + &c->addr, c->alen); + + if (!status) { + mrp_log_error("Failed to send message #%d.", msg.seq); + exit(1); + } + else + mrp_log_info("Message #%d succesfully sent.", msg.seq); +} + + +void send_raw(context_t *c) +{ + uint32_t seq = c->seqno++; + char msg[256]; + size_t size; + int status; + + size = snprintf(msg, sizeof(msg), "this is message #%u", seq); + + if (c->connect) + status = mrp_transport_sendraw(c->t, msg, size); + else + status = mrp_transport_sendrawto(c->t, msg, size, &c->addr, c->alen); + + if (!status) { + mrp_log_error("Failed to send raw message #%d.", seq); + exit(1); + } + else + mrp_log_info("Message #%u succesfully sent.", seq); +} + + + +void send_cb(mrp_timer_t *t, void *user_data) +{ + context_t *c = (context_t *)user_data; + + MRP_UNUSED(t); + + switch (c->mode) { + case MODE_DATA: send_data(c); break; + case MODE_RAW: send_raw(c); break; + default: + case MODE_MESSAGE: send_msg(c); + } +} + + +void client_init(context_t *c) +{ + static mrp_transport_evt_t evt = { + { .recvmsg = NULL }, + { .recvmsgfrom = NULL }, + .closed = closed_evt, + .connection = NULL + }; + + int flags; + + type_init(c); + + switch (c->mode) { + case MODE_DATA: + evt.recvdata = recv_data; + evt.recvdatafrom = recvfrom_data; + flags = MRP_TRANSPORT_MODE_DATA; + break; + case MODE_RAW: + evt.recvraw = recv_raw; + evt.recvrawfrom = recvfrom_raw; + flags = MRP_TRANSPORT_MODE_RAW; + break; + default: + case MODE_MESSAGE: + evt.recvmsg = recv_msg; + evt.recvmsgfrom = recvfrom_msg; + flags = MRP_TRANSPORT_MODE_MSG; + } + + c->t = mrp_transport_create(c->ml, c->atype, &evt, c, flags); + + if (c->t == NULL) { + mrp_log_error("Failed to create new transport."); + exit(1); + } + + if (!strcmp(c->atype, "unxd")) { + char addrstr[] = "unxd:@stream-test-client"; + mrp_sockaddr_t addr; + socklen_t alen; + + alen = mrp_transport_resolve(NULL, addrstr, &addr, sizeof(addr), NULL); + if (alen <= 0) { + mrp_log_error("Failed to resolve transport address '%s'.", addrstr); + exit(1); + } + + if (!mrp_transport_bind(c->t, &addr, alen)) { + mrp_log_error("Failed to bind to transport address '%s'.", addrstr); + exit(1); + } + } + + if (c->connect) { + if (!mrp_transport_connect(c->t, &c->addr, c->alen)) { + mrp_log_error("Failed to connect to %s.", c->addrstr); + exit(1); + } + } + + + c->timer = mrp_add_timer(c->ml, 1000, send_cb, c); + + if (c->timer == NULL) { + mrp_log_error("Failed to create send timer."); + exit(1); + } +} + + +static void print_usage(const char *argv0, int exit_code, const char *fmt, ...) +{ + va_list ap; + + if (fmt && *fmt) { + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + } + + printf("usage: %s [options] [transport-address]\n\n" + "The possible options are:\n" + " -s, --server run as test server (default)\n" + " -C, --connect connect transport\n" + " For connection-oriented transports, this is automatic.\n" + " -a, --address address to use\n" + " -c, --custom use custom messages\n" + " -m, --message use generic messages (default)\n" + " -r, --raw use raw messages\n" + " -b, --buggy use buggy data descriptors\n" + " -t, --log-target=TARGET log target to use\n" + " TARGET is one of stderr,stdout,syslog, or a logfile path\n" + " -l, --log-level=LEVELS logging level to use\n" + " LEVELS is a comma separated list of info, error and warning\n" + " -v, --verbose increase logging verbosity\n" + " -d, --debug enable debug messages\n" + " -h, --help show help on usage\n", + argv0); + + if (exit_code < 0) + return; + else + exit(exit_code); +} + + +static void config_set_defaults(context_t *ctx) +{ + mrp_clear(ctx); + ctx->addrstr = "tcp4:127.0.0.1:3000"; + ctx->server = FALSE; + ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG); + ctx->log_target = MRP_LOG_TO_STDERR; +} + + +int parse_cmdline(context_t *ctx, int argc, char **argv) +{ +# define OPTIONS "scmrbCa:l:t:v:d:h" + struct option options[] = { + { "server" , no_argument , NULL, 's' }, + { "address" , required_argument, NULL, 'a' }, + { "custom" , no_argument , NULL, 'c' }, + { "message" , no_argument , NULL, 'm' }, + { "raw" , no_argument , NULL, 'r' }, + { "connect" , no_argument , NULL, 'C' }, + + { "buggy" , no_argument , NULL, 'b' }, + { "log-level" , required_argument, NULL, 'l' }, + { "log-target", required_argument, NULL, 't' }, + { "verbose" , optional_argument, NULL, 'v' }, + { "debug" , required_argument, NULL, 'd' }, + { "help" , no_argument , NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + int opt; + + config_set_defaults(ctx); + + while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) { + switch (opt) { + case 's': + ctx->server = TRUE; + break; + + case 'c': + if (ctx->mode == MODE_DEFAULT) + ctx->mode = MODE_DATA; + else { + mrp_log_error("Multiple modes requested."); + exit(1); + } + break; + + case 'm': + if (ctx->mode == MODE_DEFAULT) + ctx->mode = MODE_MESSAGE; + else { + mrp_log_error("Multiple modes requested."); + exit(1); + } + break; + + case 'r': + if (ctx->mode == MODE_DEFAULT) + ctx->mode = MODE_RAW; + else { + mrp_log_error("Multiple modes requested."); + exit(1); + } + break; + + case 'b': + ctx->buggy = TRUE; + break; + + case 'C': + ctx->connect = TRUE; + break; + + case 'a': + ctx->addrstr = optarg; + break; + + case 'v': + ctx->log_mask <<= 1; + ctx->log_mask |= 1; + break; + + case 'l': + ctx->log_mask = mrp_log_parse_levels(optarg); + if (ctx->log_mask < 0) + print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg); + break; + + case 't': + ctx->log_target = mrp_log_parse_target(optarg); + if (!ctx->log_target) + print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg); + break; + + case 'd': + ctx->log_mask |= MRP_LOG_MASK_DEBUG; + mrp_debug_set_config(optarg); + mrp_debug_enable(TRUE); + break; + + case 'h': + print_usage(argv[0], -1, ""); + exit(0); + break; + + default: + print_usage(argv[0], EINVAL, "invalid option '%c'", opt); + } + } + + return TRUE; +} + + +int main(int argc, char *argv[]) +{ + context_t c; + + if (!parse_cmdline(&c, argc, argv)) + exit(1); + + mrp_log_set_mask(c.log_mask); + mrp_log_set_target(c.log_target); + + if (c.server) + mrp_log_info("Running as server, using address '%s'...", c.addrstr); + else + mrp_log_info("Running as client, using address '%s'...", c.addrstr); + + switch (c.mode) { + case MODE_DATA: mrp_log_info("Using custom data messages..."); break; + case MODE_RAW: mrp_log_info("Using raw messages..."); break; + default: + case MODE_MESSAGE: mrp_log_info("Using generic messages..."); + } + + if (!strncmp(c.addrstr, "tcp", 3) || !strncmp(c.addrstr, "unxs", 4) || + !strncmp(c.addrstr, "wsck", 4)) { + c.stream = TRUE; + c.connect = TRUE; + } + + c.alen = mrp_transport_resolve(NULL, c.addrstr, + &c.addr, sizeof(c.addr), &c.atype); + if (c.alen <= 0) { + mrp_log_error("Failed to resolve transport address '%s'.", c.addrstr); + exit(1); + } + + c.ml = mrp_mainloop_create(); + + if (c.server) + server_init(&c); + else + client_init(&c); + + mrp_mainloop_run(c.ml); + + return 0; +} diff --git a/src/common/tests/sdbus-test.c b/src/common/tests/sdbus-test.c new file mode 100644 index 0000000..f785139 --- /dev/null +++ b/src/common/tests/sdbus-test.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include + +#include "sd-bus.h" +#include "bus-message.h" + +#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000)) + +typedef struct { + sd_bus *bus; + mrp_mainloop_t *ml; + mrp_subloop_t *sl; +} bus_t; + +static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data) +{ + MRP_UNUSED(user_data); + + switch (signum) { + case SIGINT: + case SIGTERM: + case SIGQUIT: + mrp_log_info("Received signal %d (%s), exiting...", signum, + strsignal(signum)); + mrp_mainloop_quit(mrp_get_sighandler_mainloop(h), 0); + } +} + + +static int bus_prepare(void *user_data) +{ + MRP_UNUSED(user_data); + + return FALSE; +} + + +static int bus_query(void *user_data, struct pollfd *fds, int nfd, + int *timeout) +{ + bus_t *b = (bus_t *)user_data; + uint64_t usec; + + mrp_log_info("nfd: %d", nfd); + + if (nfd > 0) { + fds[0].fd = sd_bus_get_fd(b->bus); + fds[0].events = sd_bus_get_events(b->bus) | POLLIN; + fds[0].revents = 0; + + if (sd_bus_get_timeout(b->bus, &usec) < 0) + *timeout = -1; + else + *timeout = USEC_TO_MSEC(usec); + + mrp_log_info("fd: %d, events: 0x%x, timeout: %u", fds[0].fd, + fds[0].events, *timeout); + } + + return 1; +} + + +static int bus_check(void *user_data, struct pollfd *fds, int nfd) +{ + MRP_UNUSED(user_data); + + if (nfd > 0 && fds[0].revents != 0) + return TRUE; + else + return FALSE; +} + + +static void bus_dispatch(void *user_data) +{ + bus_t *b = (bus_t *)user_data; + + if (sd_bus_process(b->bus, NULL) > 0) + sd_bus_flush(b->bus); +} + + +static int bus_signal_cb(sd_bus *bus, int ret, sd_bus_message *m, void *user_data) +{ + mrp_log_info("%s(): got bus signal...", __FUNCTION__); + + bus_message_dump(m); + + return 0; +} + + +static int bus_method_cb(sd_bus *bus, int ret, sd_bus_message *m, void *user_data) +{ + mrp_log_info("%s(): got bus method call message %p...", __FUNCTION__, m); + + bus_message_dump(m); + + if (!strcmp(sd_bus_message_get_member(m), "unhandled")) + return FALSE; + else + return TRUE; +} + + +static int bus_return_cb(sd_bus *bus, int ret, sd_bus_message *m, void *user_data) +{ + mrp_log_info("%s(): got bus method reply...", __FUNCTION__); + + bus_message_dump(m); + + return 0; +} + + +static void emit_signal(mrp_timer_t *t, void *user_data) +{ + sd_bus *bus = (sd_bus *)user_data; + + sd_bus_emit_signal(bus, "/foo/bar", "foo.bar", "foobar", NULL); +} + + +static void call_method(mrp_timer_t *t, void *user_data) +{ + sd_bus *bus = (sd_bus *)user_data; + sd_bus_message *msg = NULL; + int r; + uint64_t serial; + + r = sd_bus_message_new_method_call(bus, "org.freedesktop.DBus", + "/", "org.freedesktop.DBus", "GetId", + &msg); + + if (r != 0) { + mrp_log_error("Failed to create new method call message."); + return; + } + + r = sd_bus_send_with_reply(bus, msg, bus_return_cb, NULL, 100000 * 1000, &serial); + + if (r != 0) + mrp_log_error("Failed to call method... (r = %d)", r); +} + + +int main(int argc, char *argv[]) +{ + static mrp_subloop_ops_t bus_ops = { + .prepare = bus_prepare, + .query = bus_query, + .check = bus_check, + .dispatch = bus_dispatch + }; + + mrp_mainloop_t *ml = NULL; + mrp_timer_t *ts = NULL; + mrp_timer_t *tm = NULL; + sd_bus *bus = NULL; + int r; + bus_t *b; + + mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_INFO)); + + ml = mrp_mainloop_create(); + r = sd_bus_open_user(&bus); + + if (ml == NULL || r != 0) + goto fail; + + mrp_add_sighandler(ml, SIGINT , signal_handler, NULL); + mrp_add_sighandler(ml, SIGTERM, signal_handler, NULL); + mrp_add_sighandler(ml, SIGQUIT, signal_handler, NULL); + + b = mrp_allocz(sizeof(*b)); + + if (b == NULL) + goto fail; + + sd_bus_add_match(bus, "type='signal'" , bus_signal_cb, bus); +#if 0 + sd_bus_add_match(bus, "type='method_call'" , bus_method_cb, bus); + sd_bus_add_match(bus, "type='method_return'", bus_return_cb, bus); +#else + sd_bus_add_fallback(bus, "/", bus_method_cb, bus); +#endif + + while (sd_bus_process(bus, NULL) > 0) + sd_bus_flush(bus); + + b->bus = bus; + b->ml = ml; + b->sl = mrp_add_subloop(ml, &bus_ops, b); + + if (b->sl == NULL) { + mrp_log_error("Failed to register D-Bus subloop."); + exit(1); + } + +#if 0 + if ((ts = mrp_add_timer(ml, 1000, emit_signal, bus)) == NULL) { + mrp_log_error("Failed to create signal emission timer."); + exit(1); + } +#endif + + if ((ts = mrp_add_timer(ml, 1000, call_method, bus)) == NULL) { + mrp_log_error("Failed to create method call timer."); + exit(1); + } + + mrp_mainloop_run(ml); + + fail: + mrp_del_timer(ts); + mrp_del_timer(tm); + + sd_bus_unref(bus); + mrp_mainloop_destroy(ml); + + return 0; +} diff --git a/src/plugins/plugin-resource-dbus.c b/src/plugins/plugin-resource-dbus.c index ac2f403..cca6d2a 100644 --- a/src/plugins/plugin-resource-dbus.c +++ b/src/plugins/plugin-resource-dbus.c @@ -42,7 +42,7 @@ #include #include -#include +#include #include -- 2.7.4