[SSSD] [PATCH] Implement type-safe getters for primitive types and their arrays

Jakub Hrozek jhrozek at redhat.com
Mon May 12 21:27:52 UTC 2014


Hi,

the attached patchset implement the
org.freedesktop.DBus.Properties.Get() interface for properties of
primitive types and arrays of primitive types (except for array of bools
as DBus bool is of different size than C-sized bool).

Getters are always synchronous, mostly to allow sane GetAll processing.
The getters also never error out, but rather return a default value.

The first two patches add utility functions, one by me and one by Pavel
that make it easier to return a primitive type or an array in a variant.
I wish the DBus library itself had a helper like this...

Patch 3/6 just allows the messages to be passed through to the IFP
responder.

The whole patchset is based on Stef's WIP branch and expands on it, that's
patch 4/6. There is a setter stub that, while not finished, is not
harmful either and can be expanded on later.

Patch 5/6 is the meat of the whole patchset. It implements the sbus
codegen to generate getter functions based on the XML description of the
interface and adds unit test for all the supported types and all
supported arrays. Pavel was kind enough to contribute several bugfixes
for this patch -- thank you!

Patch 6/6 is a bugfix by Pavel, one that I agree with, you can consider
the patch acked by me.

The next patch I'm going to send will implement a 'raw property getter'
that would allow the interface developer to return complex values for a
property value.
-------------- next part --------------
>From f5c17fe154cc396f7a497dcc389c8a2fd5037a7d Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 12 May 2014 18:59:35 +0200
Subject: [PATCH 1/6] SBUS: Utility function sbus_request_return_as_variant

Adds a utility function that returns a single value with a given type in
a variant. This utility function will be used by the Get property call.
---
 src/sbus/sssd_dbus.h         |  4 ++++
 src/sbus/sssd_dbus_request.c | 57 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)

diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h
index 15ca521184f82c7424549907b7afa912f891c3ad..efb0296813e9946f2b2fa1fd867592315cc4ee4f 100644
--- a/src/sbus/sssd_dbus.h
+++ b/src/sbus/sssd_dbus.h
@@ -246,6 +246,10 @@ int sbus_request_return_and_finish(struct sbus_request *dbus_req,
                                    int first_arg_type,
                                    ...);
 
+int sbus_request_return_as_variant(struct sbus_request *dbus_req,
+                                   int type,
+                                   const void *value);
+
 /*
  * Return an error for a DBus method call request. The @error is a normal
  * DBusError.
diff --git a/src/sbus/sssd_dbus_request.c b/src/sbus/sssd_dbus_request.c
index 2d23931aa07a83c15c30b31615d5db7cfee11a0a..af439b284024b2b335adc0119f131dd6046c61bc 100644
--- a/src/sbus/sssd_dbus_request.c
+++ b/src/sbus/sssd_dbus_request.c
@@ -125,6 +125,63 @@ int sbus_request_return_and_finish(struct sbus_request *dbus_req,
     return ret;
 }
 
+int sbus_request_return_as_variant(struct sbus_request *dbus_req,
+                                   int type,
+                                   const void *value)
+{
+    DBusMessage *reply;
+    dbus_bool_t dbret;
+    DBusMessageIter iter;
+    DBusMessageIter valiter;
+    char strtype[2];
+    int ret;
+
+    snprintf(strtype, 2, "%c", type);
+
+    reply = dbus_message_new_method_return(dbus_req->message);
+    if (reply == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory allocating DBus message\n");
+        sbus_request_finish(dbus_req, NULL);
+        return ENOMEM;
+    }
+
+    dbus_message_iter_init_append(reply, &iter);
+    dbret = dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                             strtype, &valiter);
+    if (!dbret) {
+        sbus_request_finish(dbus_req, NULL);
+        ret = EINVAL;
+        goto done;
+    }
+
+    dbret = dbus_message_iter_append_basic(&valiter, type, value);
+    if (!dbret) {
+        sbus_request_finish(dbus_req, NULL);
+        ret = EINVAL;
+        goto done;
+    }
+
+    dbret = dbus_message_iter_close_container(&iter, &valiter);
+    if (!dbret) {
+        sbus_request_finish(dbus_req, NULL);
+        ret = EINVAL;
+        goto done;
+    }
+
+    if (dbret) {
+        ret = sbus_request_finish(dbus_req, reply);
+    } else {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't build DBus message\n");
+        sbus_request_finish(dbus_req, NULL);
+        ret = EINVAL;
+    }
+
+done:
+    dbus_message_unref(reply);
+    return ret;
+}
+
+
 int sbus_request_fail_and_finish(struct sbus_request *dbus_req,
                                  const DBusError *error)
 {
-- 
1.9.0

-------------- next part --------------
>From a0fe9eb748301ed5df475ff55e375ba2bcd75f31 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina at redhat.com>
Date: Mon, 12 May 2014 19:00:36 +0200
Subject: [PATCH 2/6] SBUS: Utility function
 sbus_request_return_array_as_variant

Adds a utility function that returns an array of types values, each of a
given size, with a given type in a variant. This utility function will be
used by the GetAll property call.
---
 src/sbus/sssd_dbus.h         |   6 +++
 src/sbus/sssd_dbus_request.c | 103 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 109 insertions(+)

diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h
index efb0296813e9946f2b2fa1fd867592315cc4ee4f..a19f1d9c65656ce38e0e587b6cf71c3c947f4593 100644
--- a/src/sbus/sssd_dbus.h
+++ b/src/sbus/sssd_dbus.h
@@ -250,6 +250,12 @@ int sbus_request_return_as_variant(struct sbus_request *dbus_req,
                                    int type,
                                    const void *value);
 
+int sbus_request_return_array_as_variant(struct sbus_request *dbus_req,
+                                         int type,
+                                         uint8_t *values,
+                                         const int len,
+                                         const size_t item_size);
+
 /*
  * Return an error for a DBus method call request. The @error is a normal
  * DBusError.
diff --git a/src/sbus/sssd_dbus_request.c b/src/sbus/sssd_dbus_request.c
index af439b284024b2b335adc0119f131dd6046c61bc..f2870077d6dc96b94c083ccdc9fc9a4c00c0428d 100644
--- a/src/sbus/sssd_dbus_request.c
+++ b/src/sbus/sssd_dbus_request.c
@@ -182,6 +182,109 @@ done:
 }
 
 
+int sbus_request_return_array_as_variant(struct sbus_request *dbus_req,
+                                         int type,
+                                         uint8_t *values,
+                                         const int len,
+                                         const size_t item_size)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    DBusMessage *reply = NULL;
+    dbus_bool_t dbret;
+    DBusMessageIter iter;
+    DBusMessageIter variant_iter;
+    DBusMessageIter array_iter;
+    char *variant_type = NULL;
+    char *array_type = NULL;
+    void *addr = NULL;
+    int ret;
+    int i;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    variant_type = talloc_asprintf(tmp_ctx, DBUS_TYPE_ARRAY_AS_STRING "%c",
+                                   type);
+    if (variant_type == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    array_type = talloc_asprintf(tmp_ctx, "%c", type);
+    if (array_type == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    reply = dbus_message_new_method_return(dbus_req->message);
+    if (reply == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory allocating DBus message\n");
+        sbus_request_finish(dbus_req, NULL);
+        ret = ENOMEM;
+        goto done;
+    }
+
+    dbus_message_iter_init_append(reply, &iter);
+    dbret = dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                             variant_type, &variant_iter);
+    if (!dbret) {
+        sbus_request_finish(dbus_req, NULL);
+        ret = EINVAL;
+        goto done;
+    }
+
+    /* Open container for values */
+    dbret = dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+                                             array_type, &array_iter);
+    if (!dbret) {
+        sbus_request_finish(dbus_req, NULL);
+        ret = EINVAL;
+        goto done;
+    }
+
+    for (i = 0; i < len; i++) {
+        addr = values + i * item_size;
+        dbret = dbus_message_iter_append_basic(&array_iter, type, addr);
+        if (!dbret) {
+            sbus_request_finish(dbus_req, NULL);
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    dbret = dbus_message_iter_close_container(&variant_iter, &array_iter);
+    if (!dbret) {
+        sbus_request_finish(dbus_req, NULL);
+        ret = EINVAL;
+        goto done;
+    }
+
+    dbret = dbus_message_iter_close_container(&iter, &variant_iter);
+    if (!dbret) {
+        sbus_request_finish(dbus_req, NULL);
+        ret = EINVAL;
+        goto done;
+    }
+
+    if (dbret) {
+        ret = sbus_request_finish(dbus_req, reply);
+    } else {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Couldn't build DBus message\n");
+        sbus_request_finish(dbus_req, NULL);
+        ret = EINVAL;
+    }
+
+done:
+    if (reply != NULL) {
+        dbus_message_unref(reply);
+    }
+
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 int sbus_request_fail_and_finish(struct sbus_request *dbus_req,
                                  const DBusError *error)
 {
-- 
1.9.0

-------------- next part --------------
>From 5733ceac904342a7a979d0efe066afd9f093ebe9 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 22 Apr 2014 21:50:28 +0200
Subject: [PATCH 3/6] IFP: Allow Set, Get and GetAll from DBus.Properties

The InfoPipe will support all three of:
    DBus.Properties.Get
    DBus.Properties.GetAll
    DBus.Properties.Set

Hence it must allow these calls to be received.
---
 src/responder/ifp/org.freedesktop.sssd.infopipe.conf | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/responder/ifp/org.freedesktop.sssd.infopipe.conf b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
index 56e460aa11c2534e95092fb10216b05c50c04d59..4f2a906ba812d46b6bca401d3fc14baa5c18de50 100644
--- a/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
+++ b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
@@ -19,6 +19,16 @@
     <allow send_destination="org.freedesktop.sssd.infopipe"
            send_interface="org.freedesktop.DBus.Introspectable"/>
 
+    <allow send_destination="org.freedesktop.sssd.infopipe"
+           send_interface="org.freedesktop.DBus.Properties"
+           send_member="GetAll"/>
+    <allow send_destination="org.freedesktop.sssd.infopipe"
+           send_interface="org.freedesktop.DBus.Properties"
+           send_member="Get"/>
+    <allow send_destination="org.freedesktop.sssd.infopipe"
+           send_interface="org.freedesktop.DBus.Properties"
+           send_member="Set"/>
+
     <allow send_interface="org.freedesktop.sssd.infopipe"/>
   </policy>
 
-- 
1.9.0

-------------- next part --------------
>From d23886c484bc955bf4bc03998601aeec1650ce02 Mon Sep 17 00:00:00 2001
From: Stef Walter <stefw at redhat.com>
Date: Tue, 25 Feb 2014 18:31:03 +0100
Subject: [PATCH 4/6] WIP properties

---
 Makefile.am                              |   1 +
 src/sbus/sbus_codegen                    |  72 +++++++++++++++---
 src/sbus/sssd_dbus.h                     |   4 +
 src/sbus/sssd_dbus_connection.c          |  17 +++--
 src/sbus/sssd_dbus_meta.h                |   2 +
 src/sbus/sssd_dbus_private.h             |   8 ++
 src/sbus/sssd_dbus_properties.c          | 122 +++++++++++++++++++++++++++++++
 src/tests/sbus_codegen_tests_generated.c |   4 +-
 src/util/util_errors.c                   |   1 +
 src/util/util_errors.h                   |   1 +
 10 files changed, 213 insertions(+), 19 deletions(-)
 create mode 100644 src/sbus/sssd_dbus_properties.c

diff --git a/Makefile.am b/Makefile.am
index 35ab9d7860b8de8c4562903782017c91fe82fe2f..d034b5fd6251b0fc2c9f9ea7762f471daf2641da 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -607,6 +607,7 @@ libsss_util_la_SOURCES = \
     src/sbus/sssd_dbus_connection.c \
     src/sbus/sssd_dbus_meta.c \
     src/sbus/sssd_dbus_introspect.c \
+    src/sbus/sssd_dbus_properties.c \
     src/sbus/sssd_dbus_request.c \
     src/sbus/sssd_dbus_server.c \
     src/util/util.c \
diff --git a/src/sbus/sbus_codegen b/src/sbus/sbus_codegen
index 6c4b8ec9a1f64cc243afb0fe5d12617b3ef586f0..96248a62886ad03439b4a4996f4668c1b610d990 100755
--- a/src/sbus/sbus_codegen
+++ b/src/sbus/sbus_codegen
@@ -114,10 +114,9 @@ BASIC_TYPES = {
  'o': ( "DBUS_TYPE_OBJECT_PATH", "const char *", "const char *" ),
 }
 
-class Arg(Base):
-    def __init__(self, method, name, type):
+class Typed(Base):
+    def __init__(self, name, type):
         Base.__init__(self, name)
-        self.method = method
         self.type = type
         self.is_basic = False
         self.is_array = False
@@ -135,6 +134,11 @@ class Arg(Base):
             else:
                 self.is_basic = True
 
+class Arg(Typed):
+    def __init__(self, method, name, type):
+        Typed.__init__(self, name, type)
+        self.method = method
+
 class Method(Base):
     def __init__(self, iface, name):
         Base.__init__(self, name)
@@ -166,11 +170,10 @@ class Signal(Base):
     def fq_c_name(self):
         return "%s_%s" % (self.iface.c_name(), self.c_name())
 
-class Property(Base):
-    def __init__(self, iface, name, signature, access):
-        Base.__init__(self, name)
+class Property(Typed):
+    def __init__(self, iface, name, type, access):
+        Typed.__init__(self, name, type)
         self.iface = iface
-        self.signature = signature
         self.readable = False
         self.writable = False
         if access == 'readwrite':
@@ -234,7 +237,7 @@ def method_function_pointer(meth, name, with_names=False):
              with_names and "data" or "",
              method_arg_types(meth.in_args, with_names))
 
-def forward_invoker(signature, args):
+def forward_method_invoker(signature, args):
     out("")
     out("/* invokes a handler with a '%s' DBus signature */", signature)
     out("static int invoke_%s_method(struct sbus_request *dbus_req, void *function_ptr);", signature)
@@ -275,6 +278,53 @@ def source_method_invoker(signature, args):
     out(");")
     out("}")
 
+def source_getter_invoker(prop):
+    out("")
+    out("/* invokes a getter with a '%s' DBus type */", type)
+    out("static int invoke_%s_getter(struct sbus_request *request, struct sbus_interface *intf, void *function_ptr)", prop.type)
+    out("{")
+    out("    DBusError error = DBUS_ERROR_INIT;")
+    out("    int ret;")
+    for i in range(0, len(args)):
+        arg = args[i]
+        if arg.is_array:
+            out("    %s *arg_%d;", arg.dbus_type, i)
+            out("    int len_%d;", i)
+        else:
+            out("    %s arg_%d;", arg.dbus_type, i)
+    out("    int (*handler)(struct sbus_request *, void *%s) = function_ptr;", method_arg_types(args))
+    out("")
+    out("    if (!dbus_message_get_args(request->message, &error,")
+    for i in range(0, len(args)):
+        arg = args[i]
+        if arg.is_array:
+            out("                               DBUS_TYPE_ARRAY, %s, &arg_%d, &len_%d,",
+                arg.dbus_constant, i, i)
+        else:
+            out("                               %s, &arg_%d,", arg.dbus_constant, i)
+    out("                               DBUS_TYPE_INVALID)) {")
+    out("         ret = sbus_request_fail_and_finish(request, error.name, error.message);")
+    out("         dbus_error_free(&error);")
+    out("         return ret;")
+    out("    }")
+    out("")
+
+    out("    ret = (handler)(request, intf", new_line=False)
+    for i in range(0, len(args)):
+        arg = args[i]
+        out(",\n                    arg_%d", i, new_line=False)
+        if arg.is_array:
+            out(",\n                    len_%d", i, new_line=False)
+    out(");")
+    out("")
+
+    for i in range(0, len(args)):
+        arg = args[i]
+        if arg.type in ["as", "ao"]:
+            out("    dbus_free_string_array((char **)arg_%d);", i)
+    out("    return ret;")
+    out("}")
+
 def forward_method_invokers(ifaces):
     invokers = { }
     for iface in ifaces:
@@ -284,7 +334,7 @@ def forward_method_invokers(ifaces):
             signature = meth.in_signature()
             if signature in invokers:
                 continue
-            forward_invoker(signature, meth.in_args)
+            forward_method_invoker(signature, meth.in_args)
             invokers[signature] = meth
     return invokers
 
@@ -392,7 +442,7 @@ def source_properties(iface, properties):
     for prop in properties:
         out("    {")
         out("        \"%s\", /* name */", prop.name)
-        out("        \"%s\", /* signature */", prop.signature)
+        out("        \"%s\", /* type */", prop.type)
         if prop.readable and prop.writable:
             out("        SBUS_PROPERTY_READABLE | SBUS_PROPERTY_WRITABLE,")
         elif prop.readable:
@@ -480,8 +530,6 @@ def header_vtable(iface, methods):
     for meth in iface.methods:
         out("    %s;", method_function_pointer(meth, meth.c_name(), with_names=True))
 
-    # TODO: Property getters and setters will go here
-
     out("};")
 
 def header_constants(iface):
diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h
index a19f1d9c65656ce38e0e587b6cf71c3c947f4593..9c236479e5474b884d8e051ea221c77a40ed6ac7 100644
--- a/src/sbus/sssd_dbus.h
+++ b/src/sbus/sssd_dbus.h
@@ -80,6 +80,9 @@ struct sbus_vtable {
 #define DBUS_INTROSPECT_INTERFACE "org.freedesktop.DBus.Introspectable"
 #define DBUS_INTROSPECT_METHOD "Introspect"
 
+/* Special interface and method for D-BUS properties */
+#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+
 struct sbus_interface {
     const char *path;
     struct sbus_vtable *vtable;
@@ -257,6 +260,7 @@ int sbus_request_return_array_as_variant(struct sbus_request *dbus_req,
                                          const size_t item_size);
 
 /*
+
  * Return an error for a DBus method call request. The @error is a normal
  * DBusError.
  *
diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c
index 63eac8ffa5728246a80daafe02f1df65b1493d87..5a66ac79a7e93eab4def66d4cac538b1873ffbc8 100644
--- a/src/sbus/sssd_dbus_connection.c
+++ b/src/sbus/sssd_dbus_connection.c
@@ -437,6 +437,7 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn,
 
     /* Validate the method interface */
     if (strcmp(msg_interface, intf_p->intf->vtable->meta->name) == 0 ||
+             strcmp(msg_interface, DBUS_PROPERTIES_INTERFACE) == 0 ||
             (strcmp(msg_interface, DBUS_INTROSPECT_INTERFACE) == 0 &&
                 strcmp(msg_method, DBUS_INTROSPECT_METHOD) == 0)) {
 
@@ -514,11 +515,7 @@ static void sbus_handler_got_caller_id(struct tevent_req *req)
             dbus_error = DBUS_ERROR_NOT_SUPPORTED;
             goto fail;
         }
-    } else {
-        /* Special case: check for Introspection request
-         * This is usually only useful for system bus connections
-         */
-        if (strcmp(msg_interface, DBUS_INTROSPECT_INTERFACE) == 0 &&
+    } else if (strcmp(msg_interface, DBUS_INTROSPECT_INTERFACE) == 0 &&
                 strcmp(msg_method, DBUS_INTROSPECT_METHOD) == 0) {
             DEBUG(SSSDBG_TRACE_LIBS, "Got introspection request\n");
             ictx = talloc(dbus_req->conn, struct sbus_introspect_ctx);
@@ -531,7 +528,17 @@ static void sbus_handler_got_caller_id(struct tevent_req *req)
             ictx->iface = interface;
             handler_data = ictx;
             method = &introspect_method;
+    } else if (strcmp(msg_interface, DBUS_PROPERTIES_INTERFACE) == 0) {
+        ret = sbus_properties_dispatch(dbus_req);
+        if (ret == ERR_SBUS_NOSUP) {
+            /* No known method matched */
+            dbus_error = DBUS_ERROR_NOT_SUPPORTED;
+            goto fail;
         }
+        /* sbus_properties_dispatch handles all other errors
+         * or success internally
+         */
+        return;
     }
 
     if (handler_fn == NULL) {
diff --git a/src/sbus/sssd_dbus_meta.h b/src/sbus/sssd_dbus_meta.h
index 997aa9e464df91d2378ae42fcf7fdaaa05a9a2f3..0ad21df97f8cb321628896f0e06088db4533101a 100644
--- a/src/sbus/sssd_dbus_meta.h
+++ b/src/sbus/sssd_dbus_meta.h
@@ -60,6 +60,8 @@ struct sbus_property_meta {
     const char *name;
     const char *type;
     int flags;
+    size_t vtable_offset_set;
+    sbus_method_invoker_fn invoker_set;
 };
 
 struct sbus_signal_meta {
diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h
index 65189b5ff980e34e653a0bc10760290a223e60b6..fa11bc1c8ef625ba6c6e9382d7ccd146eabdbb26 100644
--- a/src/sbus/sssd_dbus_private.h
+++ b/src/sbus/sssd_dbus_private.h
@@ -68,6 +68,10 @@ struct sbus_connection {
     struct sbus_watch_ctx *watch_list;
 };
 
+/* Looks up a vtable func, in a struct derived from struct sbus_vtable */
+#define VTABLE_FUNC(vtable, offset) \
+    (*((void **)((char *)(vtable) + (offset))))
+
 /* =Watches=============================================================== */
 
 struct sbus_watch_ctx {
@@ -137,4 +141,8 @@ struct tevent_req *sbus_get_sender_id_send(TALLOC_CTX *mem_ctx,
                                            const char *sender);
 int sbus_get_sender_id_recv(struct tevent_req *req, int64_t *_uid);
 
+/* =Properties============================================================ */
+
+int sbus_properties_dispatch(struct sbus_request *dbus_req);
+
 #endif /* _SSSD_DBUS_PRIVATE_H_ */
diff --git a/src/sbus/sssd_dbus_properties.c b/src/sbus/sssd_dbus_properties.c
new file mode 100644
index 0000000000000000000000000000000000000000..835b3078b5abd124ab98265401aa48533a306ee3
--- /dev/null
+++ b/src/sbus/sssd_dbus_properties.c
@@ -0,0 +1,122 @@
+/*
+    Authors:
+        Stef Walter <stefw at redhat.com>
+
+    Copyright (C) 2014 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "sbus/sssd_dbus.h"
+#include "sbus/sssd_dbus_meta.h"
+#include "sbus/sssd_dbus_private.h"
+
+static int
+dispatch_properties_set(struct sbus_connection *conn,
+                        struct sbus_interface *intf,
+                        DBusMessage *message)
+{
+    const char *signature;
+    const struct sbus_interface_meta *meta;
+    const struct sbus_property_meta *property;
+    const char *interface_name;
+    const char *property_name;
+    const char *type;
+    struct sbus_request *req;
+    sbus_msg_handler_fn handler_fn;
+    DBusMessageIter iter;
+    DBusMessageIter variant;
+
+    req = sbus_new_request(conn, intf, message);
+    if (!req)
+        return ENOMEM;
+
+    meta = intf->vtable->meta;
+
+    signature = dbus_message_get_signature(message);
+    if (strcmp (signature, "ssv") != 0) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_INVALID_ARGS,
+                                   "Invalid argument types passed " \
+                                   "to Set method"));
+    }
+
+    dbus_message_iter_init (message, &iter);
+    dbus_message_iter_get_basic (&iter, &interface_name);
+    dbus_message_iter_next (&iter);
+    dbus_message_iter_get_basic (&iter, &property_name);
+    dbus_message_iter_next (&iter);
+
+    if (strcmp (interface_name, meta->name) != 0) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_UNKNOWN_INTERFACE,
+                                   "No such interface"));
+    }
+
+    property = sbus_meta_find_property (intf->vtable->meta, property_name);
+    if (property == NULL) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_UNKNOWN_PROPERTY,
+                                   "No such property"));
+    }
+
+    if (!(property->flags & SBUS_PROPERTY_WRITABLE)) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_PROPERTY_READ_ONLY,
+                                   "Property is not writable"));
+    }
+
+    dbus_message_iter_recurse(&iter, &variant);
+    type = dbus_message_iter_get_signature (&variant);
+    if (strcmp (property->type, type) != 0) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_INVALID_ARGS,
+                                   "Invalid data type for property"));
+    }
+
+    handler_fn = VTABLE_FUNC(intf->vtable, property->vtable_offset_set);
+    if (!handler_fn) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_NOT_SUPPORTED,
+                                   "Not implemented"));
+    }
+
+    sbus_request_invoke_or_finish(req, handler_fn,
+                                  intf->instance_data,
+                                  property->invoker_set);
+    return EOK;
+}
+
+int sbus_properties_dispatch(struct sbus_request *dbus_req)
+{
+    const char *member;
+
+    member = dbus_message_get_member(dbus_req->message);
+
+    /* Set is handled a lot like a method invocation */
+    if (strcmp(member, "Set") == 0) {
+        return dispatch_properties_set(dbus_req->conn,
+                                       dbus_req->intf,
+                                       dbus_req->message);
+    }
+
+    return ERR_SBUS_NOSUP;
+}
diff --git a/src/tests/sbus_codegen_tests_generated.c b/src/tests/sbus_codegen_tests_generated.c
index 4ba2577489ddb2e1c7c72a37b97561946f4f23b8..40d40cdc471b90f6b0d3fb52033f7ec9817c1772 100644
--- a/src/tests/sbus_codegen_tests_generated.c
+++ b/src/tests/sbus_codegen_tests_generated.c
@@ -94,7 +94,7 @@ const struct sbus_signal_meta com_planetexpress_Ship__signals[] = {
 const struct sbus_property_meta com_planetexpress_Ship__properties[] = {
     {
         "Color", /* name */
-        "s", /* signature */
+        "s", /* type */
         SBUS_PROPERTY_READABLE,
     },
     { NULL, }
@@ -231,7 +231,7 @@ const struct sbus_method_meta test_pilot__methods[] = {
 const struct sbus_property_meta test_pilot__properties[] = {
     {
         "FullName", /* name */
-        "s", /* signature */
+        "s", /* type */
         SBUS_PROPERTY_READABLE | SBUS_PROPERTY_WRITABLE,
     },
     { NULL, }
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index 90faa3e4217876a5174193f9e407bf87aef3bce2..6b13e7dff5494e5232f7d3a89f467bd404af65cf 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -58,6 +58,7 @@ struct err_string error_to_str[] = {
     { "Malformed extra attribute" }, /* ERR_INVALID_EXTRA_ATTR */
     { "Cannot get bus message sender" }, /* ERR_SBUS_GET_SENDER_ERROR */
     { "Bus message has no sender" }, /* ERR_SBUS_NO_SENDER */
+    { "Bus method not supported" }, /* ERR_SBUS_NOSUP */
 };
 
 
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 4d9f16c0aaea9128b8405048cb32a6d57850e1fc..fb0dc07b0863fadd9bc0c6cb7ebc73d787e04298 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -80,6 +80,7 @@ enum sssd_errors {
     ERR_INVALID_EXTRA_ATTR,
     ERR_SBUS_GET_SENDER_ERROR,
     ERR_SBUS_NO_SENDER,
+    ERR_SBUS_NOSUP,
     ERR_LAST            /* ALWAYS LAST */
 };
 
-- 
1.9.0

-------------- next part --------------
>From 796d392b6cb33d6ded6ae58e1abcca84cf6a9b25 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 23 Apr 2014 03:01:21 +0200
Subject: [PATCH 5/6] SBUS: Implement org.freedesktop.DBus.Properties.Get for
 primitive types

This patch implements type-safe getters for primitive types and their
arrays.  The patch includes unit tests of all supported types and arrays
of these types.

All getter are synchronous. The getters never fail, instead, they return
a default or 'not defined' value.  Making the getters synchronous and
always returning a value will make it significantly easier to implement
the GetAll method.
---
 src/sbus/sbus_codegen                    | 114 ++++---
 src/sbus/sssd_dbus.h                     |   6 +
 src/sbus/sssd_dbus_meta.h                |   2 +
 src/sbus/sssd_dbus_properties.c          |  75 +++++
 src/tests/sbus_codegen_tests.c           | 490 ++++++++++++++++++++++++++++-
 src/tests/sbus_codegen_tests.xml         |  26 ++
 src/tests/sbus_codegen_tests_generated.c | 522 +++++++++++++++++++++++++++++++
 src/tests/sbus_codegen_tests_generated.h |  44 +++
 8 files changed, 1235 insertions(+), 44 deletions(-)

diff --git a/src/sbus/sbus_codegen b/src/sbus/sbus_codegen
index 96248a62886ad03439b4a4996f4668c1b610d990..a8f91b784ffaa5a17620792df7285df4b508749f 100755
--- a/src/sbus/sbus_codegen
+++ b/src/sbus/sbus_codegen
@@ -187,6 +187,16 @@ class Property(Typed):
             raise DBusXmlException('Invalid access type %s'%self.access)
     def fq_c_name(self):
         return "%s_%s" % (self.iface.c_name(), self.c_name())
+    def getter_name(self):
+        return "%s_get_%s" % (self.iface.c_name(), self.c_name())
+    def getter_invoker_name(self):
+        return "invoke_get_%s" % self.type
+    def getter_signature(self, name):
+        sig = "void (*%s)(struct sbus_request *, void *data, %s *" % (name, self.sssd_type)
+        if self.is_array:
+            sig += " *, int *"
+        sig += ")"
+        return sig
 
 class Interface(Base):
     def __init__(self, name):
@@ -237,6 +247,9 @@ def method_function_pointer(meth, name, with_names=False):
              with_names and "data" or "",
              method_arg_types(meth.in_args, with_names))
 
+def property_handlers(prop):
+    return prop.getter_signature(prop.getter_name())
+
 def forward_method_invoker(signature, args):
     out("")
     out("/* invokes a handler with a '%s' DBus signature */", signature)
@@ -280,49 +293,36 @@ def source_method_invoker(signature, args):
 
 def source_getter_invoker(prop):
     out("")
-    out("/* invokes a getter with a '%s' DBus type */", type)
-    out("static int invoke_%s_getter(struct sbus_request *request, struct sbus_interface *intf, void *function_ptr)", prop.type)
+    if prop.is_array:
+        out("/* invokes a getter with an array of '%s' DBus type */", prop.dbus_type)
+    else:
+        out("/* invokes a getter with a '%s' DBus type */", prop.dbus_type)
+    out("static int %s(struct sbus_request *dbus_req, void *function_ptr)",
+        prop.getter_invoker_name())
     out("{")
-    out("    DBusError error = DBUS_ERROR_INIT;")
-    out("    int ret;")
-    for i in range(0, len(args)):
-        arg = args[i]
-        if arg.is_array:
-            out("    %s *arg_%d;", arg.dbus_type, i)
-            out("    int len_%d;", i)
-        else:
-            out("    %s arg_%d;", arg.dbus_type, i)
-    out("    int (*handler)(struct sbus_request *, void *%s) = function_ptr;", method_arg_types(args))
+    if prop.is_array:
+        out("    %s *prop_val;", prop.sssd_type)
+        out("    int prop_len;")
+        out("    %s *out_val;", prop.dbus_type)
+    else:
+        out("    %s prop_val;", prop.sssd_type)
+        out("    %s out_val;", prop.dbus_type)
     out("")
-    out("    if (!dbus_message_get_args(request->message, &error,")
-    for i in range(0, len(args)):
-        arg = args[i]
-        if arg.is_array:
-            out("                               DBUS_TYPE_ARRAY, %s, &arg_%d, &len_%d,",
-                arg.dbus_constant, i, i)
-        else:
-            out("                               %s, &arg_%d,", arg.dbus_constant, i)
-    out("                               DBUS_TYPE_INVALID)) {")
-    out("         ret = sbus_request_fail_and_finish(request, error.name, error.message);")
-    out("         dbus_error_free(&error);")
-    out("         return ret;")
-    out("    }")
+    out("    %s", prop.getter_signature("handler"), new_line=False)
+    out(" = function_ptr;")
     out("")
 
-    out("    ret = (handler)(request, intf", new_line=False)
-    for i in range(0, len(args)):
-        arg = args[i]
-        out(",\n                    arg_%d", i, new_line=False)
-        if arg.is_array:
-            out(",\n                    len_%d", i, new_line=False)
+    out("    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val", new_line=False)
+    if prop.is_array:
+        out(", &prop_len", new_line=False)
     out(");")
-    out("")
 
-    for i in range(0, len(args)):
-        arg = args[i]
-        if arg.type in ["as", "ao"]:
-            out("    dbus_free_string_array((char **)arg_%d);", i)
-    out("    return ret;")
+    out("")
+    out("    out_val = prop_val;")
+    if prop.is_array:
+        out("    return sbus_request_return_array_as_variant(dbus_req, %s, (uint8_t*)out_val, prop_len, sizeof(%s));", prop.dbus_constant, prop.sssd_type)
+    else:
+        out("    return sbus_request_return_as_variant(dbus_req, %s, &out_val);", prop.dbus_constant)
     out("}")
 
 def forward_method_invokers(ifaces):
@@ -342,6 +342,27 @@ def source_method_invokers(invokers):
     for (signature, meth) in invokers.items():
         source_method_invoker(signature, meth.in_args)
 
+def forward_prop_invoker(prop):
+    out("static int %s(struct sbus_request *dbus_req, void *function_ptr);",
+        prop.getter_invoker_name())
+
+def forward_prop_invokers(ifaces):
+    invokers = { }
+    for iface in ifaces:
+        for prop in iface.properties:
+            if not prop.is_basic:
+                continue
+            if prop.type in invokers:
+                continue
+            forward_prop_invoker(prop)
+            invokers[prop.type] = prop
+    return invokers
+
+def source_prop_invokers(invokers):
+    for (type, prop) in invokers.items():
+        if prop.readable:
+            source_getter_invoker(prop)
+
 def source_finisher(meth):
     out("")
     out("int %s_finish(struct sbus_request *req%s)",
@@ -438,6 +459,7 @@ def source_signals(iface, signals):
 def source_properties(iface, properties):
     out("")
     out("/* property info for %s */", iface.name)
+
     out("const struct sbus_property_meta %s__properties[] = {", iface.c_name())
     for prop in properties:
         out("    {")
@@ -451,6 +473,14 @@ def source_properties(iface, properties):
             out("        SBUS_PROPERTY_WRITABLE,")
         else:
             assert False, "should not be reached"
+        if prop.readable:
+            out("        offsetof(struct %s, %s),", iface.c_name(), prop.getter_name())
+            out("        %s,", prop.getter_invoker_name())
+        else:
+            out("        0, /* not readable */")
+            out("        NULL, /* no invoker */")
+        out("        0, /* not writable */")
+        out("        NULL, /* no invoker */")
         out("    },")
     out("    { NULL, }")
     out("};")
@@ -491,7 +521,8 @@ def generate_source(ifaces, filename, include_header=None):
     if include_header:
         out("#include \"%s\"", os.path.basename(include_header))
 
-    invokers = forward_method_invokers(ifaces)
+    meth_invokers = forward_method_invokers(ifaces)
+    prop_invokers = forward_prop_invokers(ifaces)
 
     for iface in ifaces:
 
@@ -510,7 +541,8 @@ def generate_source(ifaces, filename, include_header=None):
         # The sbus_interface structure
         source_interface(iface)
 
-    source_method_invokers(invokers)
+    source_method_invokers(meth_invokers)
+    source_prop_invokers(prop_invokers)
 
 def header_finisher(iface, meth):
     if meth.use_raw_handler():
@@ -529,6 +561,8 @@ def header_vtable(iface, methods):
     # All methods
     for meth in iface.methods:
         out("    %s;", method_function_pointer(meth, meth.c_name(), with_names=True))
+    for prop in iface.properties:
+        out("    %s;", property_handlers(prop))
 
     out("};")
 
@@ -584,7 +618,7 @@ def generate_header(ifaces, filename):
     out(" */")
 
     for iface in ifaces:
-        if iface.methods:
+        if iface.methods or iface.properties:
             header_vtable(iface, iface.methods)
             for meth in iface.methods:
                 header_finisher(iface, meth)
diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h
index 9c236479e5474b884d8e051ea221c77a40ed6ac7..955fa07c3f66b22a61e254f92c37b45823d764c8 100644
--- a/src/sbus/sssd_dbus.h
+++ b/src/sbus/sssd_dbus.h
@@ -30,6 +30,12 @@ struct sbus_request;
 #include <sys/types.h>
 #include "util/util.h"
 
+/* Getters are always synchronous. If a value of the property being retrieved
+ * is not known, a default value or an equivalent of 'None' should be returned.
+ */
+typedef void (*sbus_prop_get_fn)(struct sbus_request *dbus_req,
+                                 void *instance_data);
+
 typedef int (*sbus_msg_handler_fn)(struct sbus_request *dbus_req,
                                    void *instance_data);
 
diff --git a/src/sbus/sssd_dbus_meta.h b/src/sbus/sssd_dbus_meta.h
index 0ad21df97f8cb321628896f0e06088db4533101a..4e170b7d6ce5fb8f68109e496ab96cfdf889d864 100644
--- a/src/sbus/sssd_dbus_meta.h
+++ b/src/sbus/sssd_dbus_meta.h
@@ -60,6 +60,8 @@ struct sbus_property_meta {
     const char *name;
     const char *type;
     int flags;
+    size_t vtable_offset_get;
+    sbus_method_invoker_fn invoker_get;
     size_t vtable_offset_set;
     sbus_method_invoker_fn invoker_set;
 };
diff --git a/src/sbus/sssd_dbus_properties.c b/src/sbus/sssd_dbus_properties.c
index 835b3078b5abd124ab98265401aa48533a306ee3..bdd5b432fe57ca66a4d06f98720578b3a175a65c 100644
--- a/src/sbus/sssd_dbus_properties.c
+++ b/src/sbus/sssd_dbus_properties.c
@@ -105,6 +105,77 @@ dispatch_properties_set(struct sbus_connection *conn,
     return EOK;
 }
 
+static int
+dispatch_properties_get(struct sbus_connection *conn,
+                        struct sbus_interface *intf,
+                        DBusMessage *message)
+{
+    struct sbus_request *req;
+    const char *signature;
+    const struct sbus_interface_meta *meta;
+    DBusMessageIter iter;
+    sbus_msg_handler_fn handler_fn;
+    const struct sbus_property_meta *property;
+    const char *interface_name;
+    const char *property_name;
+
+    req = sbus_new_request(conn, intf, message);
+    if (req == NULL) {
+        return ENOMEM;
+    }
+
+    meta = intf->vtable->meta;
+
+    signature = dbus_message_get_signature(message);
+    /* Interface name, property name */
+    if (strcmp(signature, "ss") != 0) {
+        return sbus_request_fail_and_finish(req,
+                 sbus_error_new(req,
+                                DBUS_ERROR_INVALID_ARGS,
+                                "Invalid argument types passed to Get method"));
+    }
+
+    dbus_message_iter_init(message, &iter);
+    dbus_message_iter_get_basic(&iter, &interface_name);
+    dbus_message_iter_next(&iter);
+    dbus_message_iter_get_basic(&iter, &property_name);
+
+    if (strcmp(interface_name, meta->name) != 0) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_UNKNOWN_INTERFACE,
+                                   "No such interface"));
+    }
+
+    property = sbus_meta_find_property(intf->vtable->meta, property_name);
+    if (property == NULL) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_UNKNOWN_PROPERTY,
+                                   "No such property"));
+    }
+
+    if (!(property->flags & SBUS_PROPERTY_READABLE)) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_ACCESS_DENIED,
+                                   "Property is not readable"));
+    }
+
+    handler_fn = VTABLE_FUNC(intf->vtable, property->vtable_offset_get);
+    if (!handler_fn) {
+        return sbus_request_fail_and_finish(req,
+                    sbus_error_new(req,
+                                   DBUS_ERROR_NOT_SUPPORTED,
+                                   "Not implemented"));
+    }
+
+    sbus_request_invoke_or_finish(req, handler_fn,
+                                  intf->instance_data,
+                                  property->invoker_get);
+    return EOK;
+}
+
 int sbus_properties_dispatch(struct sbus_request *dbus_req)
 {
     const char *member;
@@ -116,6 +187,10 @@ int sbus_properties_dispatch(struct sbus_request *dbus_req)
         return dispatch_properties_set(dbus_req->conn,
                                        dbus_req->intf,
                                        dbus_req->message);
+    } else if (strcmp (member, "Get") == 0) {
+        return dispatch_properties_get(dbus_req->conn,
+                                       dbus_req->intf,
+                                       dbus_req->message);
     }
 
     return ERR_SBUS_NOSUP;
diff --git a/src/tests/sbus_codegen_tests.c b/src/tests/sbus_codegen_tests.c
index a88bbac38e7d4f89ec92043061cf25bb4ed7b972..57ba5bebc965426bf6b2f34bd79a38baf023c4b6 100644
--- a/src/tests/sbus_codegen_tests.c
+++ b/src/tests/sbus_codegen_tests.c
@@ -33,6 +33,9 @@
 #include "tests/sbus_codegen_tests_generated.h"
 #include "util/util_errors.h"
 
+#define N_ELEMENTS(arr) \
+    (sizeof(arr) / sizeof(arr[0]))
+
 /* The following 2 macros were taken from check's project source files (0.9.10)
  * http://check.sourceforge.net/
  */
@@ -329,12 +332,225 @@ static int eject_handler(struct sbus_request *req, void *instance_data,
                                    arg_object_path_array, len_object_path_array);
 }
 
-#define N_ELEMENTS(arr) \
-    (sizeof(arr) / sizeof(arr[0]))
+#define getter_body(in, out) do {           \
+    ck_assert(dbus_req != NULL);            \
+    ck_assert(out != NULL);                 \
+    *out = in;                              \
+} while(0);
 
-struct test_pilot pilot_methods = {
+static const bool pilot_bool = true;
+void pilot_get_boolean_handler(struct sbus_request *dbus_req,
+                               void *instance_data,
+                               bool *val)
+{
+    getter_body(pilot_bool, val);
+}
+
+static const char *pilot_full_name = "Turanga Leela";
+void pilot_get_full_name_handler(struct sbus_request *dbus_req,
+                                 void *instance_data,
+                                 const char **name)
+{
+    getter_body(pilot_full_name, name);
+}
+
+static const uint8_t pilot_byte = 42;
+void pilot_get_byte_handler(struct sbus_request *dbus_req,
+                            void *instance_data,
+                            uint8_t *byte)
+{
+    getter_body(pilot_byte, byte);
+}
+
+static const int16_t pilot_int16 = -123;
+void pilot_get_int16_handler(struct sbus_request *dbus_req,
+                             void *instance_data,
+                             int16_t *int16)
+{
+    getter_body(pilot_int16, int16);
+}
+
+static const uint16_t pilot_uint16 = 123;
+void pilot_get_uint16_handler(struct sbus_request *dbus_req,
+                              void *instance_data,
+                              uint16_t *uint16)
+{
+    getter_body(pilot_uint16, uint16);
+}
+
+static const int32_t pilot_int32 = -456;
+void pilot_get_int32_handler(struct sbus_request *dbus_req,
+                             void *instance_data,
+                             int32_t *int32)
+{
+    getter_body(pilot_int32, int32);
+}
+
+static const uint32_t pilot_uint32 = 456;
+void pilot_get_uint32_handler(struct sbus_request *dbus_req,
+                              void *instance_data,
+                              uint32_t *uint32)
+{
+    getter_body(pilot_uint32, uint32);
+}
+
+static const int64_t pilot_int64 = -456;
+void pilot_get_int64_handler(struct sbus_request *dbus_req,
+                             void *instance_data,
+                             int64_t *int64)
+{
+    getter_body(pilot_int64, int64);
+}
+
+static const uint64_t pilot_uint64 = 456;
+void pilot_get_uint64_handler(struct sbus_request *dbus_req,
+                              void *instance_data,
+                              uint64_t *uint64)
+{
+    getter_body(pilot_uint64, uint64);
+}
+
+static const double pilot_double = 3.14;
+void pilot_get_double_handler(struct sbus_request *dbus_req,
+                              void *instance_data,
+                              double *double_val)
+{
+    getter_body(pilot_double, double_val);
+}
+
+static const char *pilot_string = "leela";
+void pilot_get_string_handler(struct sbus_request *dbus_req,
+                              void *instance_data,
+                              const char **string_val)
+{
+    *string_val = pilot_string;
+}
+
+static const char *pilot_path = "/path/leela";
+void pilot_get_objpath_handler(struct sbus_request *dbus_req,
+                              void *instance_data,
+                              const char **path_val)
+{
+    *path_val = pilot_path;
+}
+
+#define array_getter_body(in, out, outlen) do {     \
+    ck_assert(dbus_req != NULL);                    \
+    ck_assert(out != NULL);                         \
+    ck_assert(outlen != NULL);                      \
+    *out = in;                                      \
+    *outlen = N_ELEMENTS(in);                       \
+} while(0);
+
+static uint8_t pilot_byte_array[] = { 42, 0 };
+void pilot_get_byte_array_handler(struct sbus_request *dbus_req,
+                                  void *instance_data,
+                                  uint8_t **arr_out, int *arr_len)
+{
+    array_getter_body(pilot_byte_array, arr_out, arr_len);
+}
+
+static int16_t pilot_int16_array[] = { -123, 0 };
+void pilot_get_int16_array_handler(struct sbus_request *dbus_req,
+                                  void *instance_data,
+                                  int16_t **arr_out, int *arr_len)
+{
+    array_getter_body(pilot_int16_array, arr_out, arr_len);
+}
+
+static uint16_t pilot_uint16_array[] = { 123, 0 };
+void pilot_get_uint16_array_handler(struct sbus_request *dbus_req,
+                                  void *instance_data,
+                                  uint16_t **arr_out, int *arr_len)
+{
+    array_getter_body(pilot_uint16_array, arr_out, arr_len);
+}
+
+static int32_t pilot_int32_array[] = { -456, 0 };
+void pilot_get_int32_array_handler(struct sbus_request *dbus_req,
+                                  void *instance_data,
+                                  int32_t **arr_out, int *arr_len)
+{
+    array_getter_body(pilot_int32_array, arr_out, arr_len);
+}
+
+static uint32_t pilot_uint32_array[] = { 456, 0 };
+void pilot_get_uint32_array_handler(struct sbus_request *dbus_req,
+                                  void *instance_data,
+                                  uint32_t **arr_out, int *arr_len)
+{
+    array_getter_body(pilot_uint32_array, arr_out, arr_len);
+}
+
+static int64_t pilot_int64_array[] = { -789, 0 };
+void pilot_get_int64_array_handler(struct sbus_request *dbus_req,
+                                  void *instance_data,
+                                  int64_t **arr_out, int *arr_len)
+{
+    array_getter_body(pilot_int64_array, arr_out, arr_len);
+}
+
+static uint64_t pilot_uint64_array[] = { 789, 0 };
+void pilot_get_uint64_array_handler(struct sbus_request *dbus_req,
+                                  void *instance_data,
+                                  uint64_t **arr_out, int *arr_len)
+{
+    array_getter_body(pilot_uint64_array, arr_out, arr_len);
+}
+
+static double pilot_double_array[] = { 3.14, 0 };
+void pilot_get_double_array_handler(struct sbus_request *dbus_req,
+                                    void *instance_data,
+                                    double **arr_out, int *arr_len)
+{
+    array_getter_body(pilot_double_array, arr_out, arr_len);
+}
+
+static const char *pilot_string_array[] = { "Turanga", "Leela" };
+void pilot_get_string_array_handler(struct sbus_request *dbus_req,
+                                    void *data,
+                                    const char ***arr_out,
+                                    int *arr_len)
+{
+    array_getter_body(pilot_string_array, arr_out, arr_len);
+}
+
+static const char *pilot_path_array[] = { "/some/path", "/another/path" };
+void pilot_get_path_array_handler(struct sbus_request *dbus_req,
+                                    void *data,
+                                    const char ***arr_out,
+                                    int *arr_len)
+{
+    array_getter_body(pilot_path_array, arr_out, arr_len);
+}
+
+struct test_pilot pilot_iface = {
     { &test_pilot_meta, 0 },
     .Eject = eject_handler,
+
+    .test_pilot_get_FullName = pilot_get_full_name_handler,
+    .test_pilot_get_byte = pilot_get_byte_handler,
+    .test_pilot_get_boolean = pilot_get_boolean_handler,
+    .test_pilot_get_int16 = pilot_get_int16_handler,
+    .test_pilot_get_uint16 = pilot_get_uint16_handler,
+    .test_pilot_get_int32 = pilot_get_int32_handler,
+    .test_pilot_get_uint32 = pilot_get_uint32_handler,
+    .test_pilot_get_int64 = pilot_get_int64_handler,
+    .test_pilot_get_uint64 = pilot_get_uint64_handler,
+    .test_pilot_get_double = pilot_get_double_handler,
+    .test_pilot_get_string = pilot_get_string_handler,
+    .test_pilot_get_object_path = pilot_get_objpath_handler,
+
+    .test_pilot_get_byte_array = pilot_get_byte_array_handler,
+    .test_pilot_get_int16_array = pilot_get_int16_array_handler,
+    .test_pilot_get_uint16_array = pilot_get_uint16_array_handler,
+    .test_pilot_get_int32_array = pilot_get_int32_array_handler,
+    .test_pilot_get_uint32_array = pilot_get_uint32_array_handler,
+    .test_pilot_get_int64_array = pilot_get_int64_array_handler,
+    .test_pilot_get_uint64_array = pilot_get_uint64_array_handler,
+    .test_pilot_get_double_array = pilot_get_double_array_handler,
+    .test_pilot_get_string_array = pilot_get_string_array_handler,
+    .test_pilot_get_object_path_array = pilot_get_path_array_handler,
 };
 
 static int pilot_test_server_init(struct sbus_connection *server, void *unused)
@@ -343,7 +559,7 @@ static int pilot_test_server_init(struct sbus_connection *server, void *unused)
 
     ret = sbus_conn_add_interface(server,
                                   sbus_new_interface(server, "/test/leela",
-                                                     &pilot_methods.vtable,
+                                                     &pilot_iface.vtable,
                                                      "Crash into the billboard"));
     ck_assert_int_eq(ret, EOK);
 
@@ -526,11 +742,277 @@ START_TEST(test_marshal_basic_types)
 }
 END_TEST
 
+static void parse_get_reply(DBusMessage *reply, const int type, void *val)
+{
+    DBusMessageIter iter;
+    DBusMessageIter variter;
+    dbus_bool_t dbret;
+
+    dbret = dbus_message_iter_init(reply, &iter);
+    ck_assert(dbret == TRUE);
+    ck_assert_int_eq(dbus_message_iter_get_arg_type(&iter), DBUS_TYPE_VARIANT);
+    dbus_message_iter_recurse(&iter, &variter);
+    ck_assert_int_eq(dbus_message_iter_get_arg_type(&variter), type);
+    dbus_message_iter_get_basic(&variter, val);
+}
+
+static void call_get(DBusConnection *client,
+                     const char *object_path,
+                     const char *iface,
+                     const char *prop,
+                     int type,
+                     void *val)
+{
+    DBusMessage *reply;
+    DBusError error = DBUS_ERROR_INIT;
+
+    reply = test_dbus_call_sync(client,
+                                object_path,
+                                DBUS_PROPERTIES_INTERFACE,
+                                "Get",
+                                &error,
+                                DBUS_TYPE_STRING, &iface,
+                                DBUS_TYPE_STRING, &prop,
+                                DBUS_TYPE_INVALID);
+    ck_assert(reply != NULL);
+    parse_get_reply(reply, type, val);
+}
+
+START_TEST(test_get_basic_types)
+{
+    TALLOC_CTX *ctx;
+    DBusConnection *client;
+    dbus_bool_t bool_val;
+    const char *string_val;
+    const char *path_val;
+    uint8_t byte_val;
+    int16_t int16_val;
+    uint16_t uint16_val;
+    int32_t int32_val;
+    uint32_t uint32_val;
+    int64_t int64_val;
+    uint64_t uint64_val;
+    double double_val;
+
+    ctx = talloc_new(NULL);
+    client = test_dbus_setup_mock(ctx, NULL, pilot_test_server_init, NULL);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "boolean",
+             DBUS_TYPE_BOOLEAN, &bool_val);
+    ck_assert(bool_val == pilot_bool);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "FullName",
+             DBUS_TYPE_STRING, &string_val);
+    ck_assert_str_eq(string_val, pilot_full_name);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "byte",
+             DBUS_TYPE_BYTE, &byte_val);
+    ck_assert_int_eq(byte_val, pilot_byte);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "int16",
+             DBUS_TYPE_INT16, &int16_val);
+    ck_assert_int_eq(int16_val, pilot_int16);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "uint16",
+             DBUS_TYPE_UINT16, &uint16_val);
+    ck_assert_int_eq(uint16_val, pilot_uint16);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "int32",
+             DBUS_TYPE_INT32, &int32_val);
+    ck_assert_int_eq(int32_val, pilot_int32);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "uint32",
+             DBUS_TYPE_UINT32, &uint32_val);
+    ck_assert_int_eq(uint32_val, pilot_uint32);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "int64",
+             DBUS_TYPE_INT64, &int64_val);
+    ck_assert_int_eq(int64_val, pilot_int64);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "uint64",
+             DBUS_TYPE_UINT64, &uint64_val);
+    ck_assert_int_eq(uint64_val, pilot_uint64);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "double",
+             DBUS_TYPE_DOUBLE, &double_val);
+    ck_assert_int_eq(double_val, pilot_double);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "string",
+             DBUS_TYPE_STRING, &string_val);
+    ck_assert_str_eq(string_val, pilot_string);
+
+    call_get(client, "/test/leela", test_pilot_meta.name, "object_path",
+             DBUS_TYPE_OBJECT_PATH, &path_val);
+    ck_assert_str_eq(path_val, pilot_path);
+}
+END_TEST
+
+static void parse_get_array_reply(DBusMessage *reply, const int type,
+                                  void **values, int *nels)
+{
+    DBusMessageIter iter;
+    DBusMessageIter variter;
+    DBusMessageIter arriter;
+    dbus_bool_t dbret;
+
+    dbret = dbus_message_iter_init(reply, &iter);
+    ck_assert(dbret == TRUE);
+    ck_assert_int_eq(dbus_message_iter_get_arg_type(&iter), DBUS_TYPE_VARIANT);
+    dbus_message_iter_recurse(&iter, &variter);
+    ck_assert_int_eq(dbus_message_iter_get_arg_type(&variter), DBUS_TYPE_ARRAY);
+    ck_assert_int_eq(dbus_message_iter_get_element_type(&variter), type);
+    dbus_message_iter_recurse(&variter, &arriter);
+    if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+        int n = 0, i = 0;;
+        const char **strings;
+        const char *s;
+
+        do {
+            n++;
+        } while (dbus_message_iter_next(&arriter));
+
+        /* Allocating on NULL is bad, but this is unit test */
+        strings = talloc_array(NULL, const char *, n);
+        ck_assert(strings != NULL);
+
+        dbus_message_iter_recurse(&variter, &arriter);
+        do {
+            dbus_message_iter_get_basic(&arriter, &s);
+            strings[i] = talloc_strdup(strings, s);
+            ck_assert(strings[i] != NULL);
+            i++;
+        } while (dbus_message_iter_next(&arriter));
+
+        *nels = n;
+        *values = strings;
+    } else {
+        /* Fixed types are easy */
+        dbus_message_iter_get_fixed_array(&arriter, values, nels);
+    }
+}
+
+static void call_get_array(DBusConnection *client,
+                           const char *object_path,
+                           const char *iface,
+                           const char *prop,
+                           int type,
+                           void **values,
+                           int *nels)
+{
+    DBusMessage *reply;
+    DBusError error = DBUS_ERROR_INIT;
+
+    reply = test_dbus_call_sync(client,
+                                object_path,
+                                DBUS_PROPERTIES_INTERFACE,
+                                "Get",
+                                &error,
+                                DBUS_TYPE_STRING, &iface,
+                                DBUS_TYPE_STRING, &prop,
+                                DBUS_TYPE_INVALID);
+    ck_assert(reply != NULL);
+    parse_get_array_reply(reply, type, values, nels);
+}
+
+#define _check_array(reply, len, known, fn) do {    \
+    fn(len, N_ELEMENTS(known));                     \
+    fn(reply[0], known[0]);                         \
+    fn(reply[1], known[1]);                         \
+} while(0);                                         \
+
+#define check_int_array(reply, len, known) \
+    _check_array(reply, len, known, ck_assert_int_eq)
+#define check_uint_array(reply, len, known) \
+    _check_array(reply, len, known, ck_assert_uint_eq)
+
+START_TEST(test_get_basic_array_types)
+{
+    TALLOC_CTX *ctx;
+    DBusConnection *client;
+    const char **string_arr_val;
+    int string_arr_len;
+    const char **path_arr_val;
+    int path_arr_len;
+    uint8_t *byte_arr_val;
+    int byte_arr_len;
+    int16_t *int16_arr_val;
+    int int16_arr_len;
+    uint16_t *uint16_arr_val;
+    int uint16_arr_len;
+    int32_t *int32_arr_val;
+    int int32_arr_len;
+    uint32_t *uint32_arr_val;
+    int uint32_arr_len;
+    int64_t *int64_arr_val;
+    int int64_arr_len;
+    uint64_t *uint64_arr_val;
+    int uint64_arr_len;
+    double *double_arr_val;
+    int double_arr_len;
+
+    ctx = talloc_new(NULL);
+    client = test_dbus_setup_mock(ctx, NULL, pilot_test_server_init, NULL);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "byte_array",
+                   DBUS_TYPE_BYTE, (void **) &byte_arr_val, &byte_arr_len);
+    check_uint_array(byte_arr_val, byte_arr_len, pilot_byte_array);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "int16_array",
+                   DBUS_TYPE_INT16, (void **) &int16_arr_val, &int16_arr_len);
+    check_int_array(int16_arr_val, int16_arr_len, pilot_int16_array);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "uint16_array",
+                   DBUS_TYPE_UINT16, (void **) &uint16_arr_val, &uint16_arr_len);
+    check_uint_array(uint16_arr_val, uint16_arr_len, pilot_uint16_array);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "int32_array",
+                   DBUS_TYPE_INT32, (void **) &int32_arr_val, &int32_arr_len);
+    check_int_array(int32_arr_val, int32_arr_len, pilot_int32_array);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "uint32_array",
+                   DBUS_TYPE_UINT32, (void **) &uint32_arr_val, &uint32_arr_len);
+    check_uint_array(uint32_arr_val, uint32_arr_len, pilot_uint32_array);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "int64_array",
+                   DBUS_TYPE_INT64, (void **) &int64_arr_val, &int64_arr_len);
+    check_int_array(int64_arr_val, int64_arr_len, pilot_int64_array);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "uint64_array",
+                   DBUS_TYPE_UINT64, (void **) &uint64_arr_val, &uint64_arr_len);
+    check_uint_array(uint64_arr_val, uint64_arr_len, pilot_uint64_array);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "double_array",
+                   DBUS_TYPE_DOUBLE, (void **) &double_arr_val, &double_arr_len);
+    check_int_array(double_arr_val, double_arr_len, pilot_double_array);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "string_array",
+                   DBUS_TYPE_STRING, (void **) &string_arr_val, &string_arr_len);
+    ck_assert_int_eq(string_arr_len, 2);
+    ck_assert_str_eq(string_arr_val[0], pilot_string_array[0]);
+    ck_assert_str_eq(string_arr_val[1], pilot_string_array[1]);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "string_array",
+                   DBUS_TYPE_STRING, (void **) &string_arr_val, &string_arr_len);
+    ck_assert_int_eq(string_arr_len, 2);
+    ck_assert_str_eq(string_arr_val[0], pilot_string_array[0]);
+    ck_assert_str_eq(string_arr_val[1], pilot_string_array[1]);
+
+    call_get_array(client, "/test/leela", test_pilot_meta.name, "object_path_array",
+                   DBUS_TYPE_OBJECT_PATH, (void **) &path_arr_val, &path_arr_len);
+    ck_assert_int_eq(path_arr_len, 2);
+    ck_assert_str_eq(path_arr_val[0], pilot_path_array[0]);
+    ck_assert_str_eq(path_arr_val[1], pilot_path_array[1]);
+
+}
+END_TEST
+
 TCase *create_handler_tests(void)
 {
     TCase *tc = tcase_create("handler");
 
     tcase_add_test(tc, test_marshal_basic_types);
+    tcase_add_test(tc, test_get_basic_types);
+    tcase_add_test(tc, test_get_basic_array_types);
 
     return tc;
 }
diff --git a/src/tests/sbus_codegen_tests.xml b/src/tests/sbus_codegen_tests.xml
index 83807b9c287f633ad59398519a7520e667003f00..74037cc4002198f8c8d4b6a8c12edbd34fe338ef 100755
--- a/src/tests/sbus_codegen_tests.xml
+++ b/src/tests/sbus_codegen_tests.xml
@@ -107,6 +107,32 @@
             <arg name="object_path_array" type="ao" direction="out"/>
         </method>
 
+        <!-- Properties with every type of basic argument, so far read only -->
+        <property name="byte" type="y" access="read"/>
+        <property name="boolean" type="b" access="read"/>
+        <property name="int16" type="n" access="read"/>
+        <property name="uint16" type="q" access="read"/>
+        <property name="int32" type="i" access="read"/>
+        <property name="uint32" type="u" access="read"/>
+        <property name="int64" type="x" access="read"/>
+        <property name="uint64" type="t" access="read"/>
+        <property name="double" type="d" access="read"/>
+        <property name="string" type="s" access="read"/>
+        <property name="object_path" type="o" access="read"/>
+
+        <!-- Property arrays with every type of basic argument except boolean
+             which we can't do (yet) -->
+        <property name="byte_array" type="ay" access="read"/>
+        <property name="int16_array" type="an" access="read"/>
+        <property name="uint16_array" type="aq" access="read"/>
+        <property name="int32_array" type="ai" access="read"/>
+        <property name="uint32_array" type="au" access="read"/>
+        <property name="int64_array" type="ax" access="read"/>
+        <property name="uint64_array" type="at" access="read"/>
+        <property name="double_array" type="ad" access="read"/>
+        <property name="string_array" type="as" access="read"/>
+        <property name="object_path_array" type="ao" access="read"/>
+
     </interface>
 
 </node>
diff --git a/src/tests/sbus_codegen_tests_generated.c b/src/tests/sbus_codegen_tests_generated.c
index 40d40cdc471b90f6b0d3fb52033f7ec9817c1772..4ace0365044ac1edec3c8774da2231c82e054017 100644
--- a/src/tests/sbus_codegen_tests_generated.c
+++ b/src/tests/sbus_codegen_tests_generated.c
@@ -16,6 +16,27 @@ static int invoke_u_method(struct sbus_request *dbus_req, void *function_ptr);
 
 /* invokes a handler with a 'ybnqiuxtdsoayanaqaiauaxatadasao' DBus signature */
 static int invoke_ybnqiuxtdsoayanaqaiauaxatadasao_method(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_s(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_y(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_b(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_n(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_q(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_i(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_u(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_x(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_t(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_d(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_o(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_ay(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_an(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_aq(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_ai(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_au(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_ax(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_at(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_ad(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_as(struct sbus_request *dbus_req, void *function_ptr);
+static int invoke_get_ao(struct sbus_request *dbus_req, void *function_ptr);
 
 /* arguments for com.planetexpress.Ship.MoveUniverse */
 const struct sbus_arg_meta com_planetexpress_Ship_MoveUniverse__in[] = {
@@ -96,6 +117,10 @@ const struct sbus_property_meta com_planetexpress_Ship__properties[] = {
         "Color", /* name */
         "s", /* type */
         SBUS_PROPERTY_READABLE,
+        offsetof(struct com_planetexpress_Ship, com_planetexpress_Ship_get_Color),
+        invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
     },
     { NULL, }
 };
@@ -233,6 +258,199 @@ const struct sbus_property_meta test_pilot__properties[] = {
         "FullName", /* name */
         "s", /* type */
         SBUS_PROPERTY_READABLE | SBUS_PROPERTY_WRITABLE,
+        offsetof(struct test_pilot, test_pilot_get_FullName),
+        invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "byte", /* name */
+        "y", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_byte),
+        invoke_get_y,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "boolean", /* name */
+        "b", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_boolean),
+        invoke_get_b,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "int16", /* name */
+        "n", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_int16),
+        invoke_get_n,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "uint16", /* name */
+        "q", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_uint16),
+        invoke_get_q,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "int32", /* name */
+        "i", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_int32),
+        invoke_get_i,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "uint32", /* name */
+        "u", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_uint32),
+        invoke_get_u,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "int64", /* name */
+        "x", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_int64),
+        invoke_get_x,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "uint64", /* name */
+        "t", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_uint64),
+        invoke_get_t,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "double", /* name */
+        "d", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_double),
+        invoke_get_d,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "string", /* name */
+        "s", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_string),
+        invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "object_path", /* name */
+        "o", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_object_path),
+        invoke_get_o,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "byte_array", /* name */
+        "ay", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_byte_array),
+        invoke_get_ay,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "int16_array", /* name */
+        "an", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_int16_array),
+        invoke_get_an,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "uint16_array", /* name */
+        "aq", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_uint16_array),
+        invoke_get_aq,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "int32_array", /* name */
+        "ai", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_int32_array),
+        invoke_get_ai,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "uint32_array", /* name */
+        "au", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_uint32_array),
+        invoke_get_au,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "int64_array", /* name */
+        "ax", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_int64_array),
+        invoke_get_ax,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "uint64_array", /* name */
+        "at", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_uint64_array),
+        invoke_get_at,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "double_array", /* name */
+        "ad", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_double_array),
+        invoke_get_ad,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "string_array", /* name */
+        "as", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_string_array),
+        invoke_get_as,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "object_path_array", /* name */
+        "ao", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct test_pilot, test_pilot_get_object_path_array),
+        invoke_get_ao,
+        0, /* not writable */
+        NULL, /* no invoker */
     },
     { NULL, }
 };
@@ -391,3 +609,307 @@ static int invoke_ybnqiuxtdsoayanaqaiauaxatadasao_method(struct sbus_request *db
                      arg_20,
                      len_20);
 }
+
+/* invokes a getter with an array of 'uint16_t' DBus type */
+static int invoke_get_aq(struct sbus_request *dbus_req, void *function_ptr)
+{
+    uint16_t *prop_val;
+    int prop_len;
+    uint16_t *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, uint16_t * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_UINT16, (uint8_t*)out_val, prop_len, sizeof(uint16_t));
+}
+
+/* invokes a getter with a 'dbus_bool_t' DBus type */
+static int invoke_get_b(struct sbus_request *dbus_req, void *function_ptr)
+{
+    bool prop_val;
+    dbus_bool_t out_val;
+
+    void (*handler)(struct sbus_request *, void *data, bool *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_BOOLEAN, &out_val);
+}
+
+/* invokes a getter with a 'double' DBus type */
+static int invoke_get_d(struct sbus_request *dbus_req, void *function_ptr)
+{
+    double prop_val;
+    double out_val;
+
+    void (*handler)(struct sbus_request *, void *data, double *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_DOUBLE, &out_val);
+}
+
+/* invokes a getter with an array of 'const char *' DBus type */
+static int invoke_get_ao(struct sbus_request *dbus_req, void *function_ptr)
+{
+    const char * *prop_val;
+    int prop_len;
+    const char * *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, const char * * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_OBJECT_PATH, (uint8_t*)out_val, prop_len, sizeof(const char *));
+}
+
+/* invokes a getter with a 'int32_t' DBus type */
+static int invoke_get_i(struct sbus_request *dbus_req, void *function_ptr)
+{
+    int32_t prop_val;
+    int32_t out_val;
+
+    void (*handler)(struct sbus_request *, void *data, int32_t *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_INT32, &out_val);
+}
+
+/* invokes a getter with an array of 'const char *' DBus type */
+static int invoke_get_as(struct sbus_request *dbus_req, void *function_ptr)
+{
+    const char * *prop_val;
+    int prop_len;
+    const char * *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, const char * * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_STRING, (uint8_t*)out_val, prop_len, sizeof(const char *));
+}
+
+/* invokes a getter with a 'const char *' DBus type */
+static int invoke_get_o(struct sbus_request *dbus_req, void *function_ptr)
+{
+    const char * prop_val;
+    const char * out_val;
+
+    void (*handler)(struct sbus_request *, void *data, const char * *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_OBJECT_PATH, &out_val);
+}
+
+/* invokes a getter with a 'int16_t' DBus type */
+static int invoke_get_n(struct sbus_request *dbus_req, void *function_ptr)
+{
+    int16_t prop_val;
+    int16_t out_val;
+
+    void (*handler)(struct sbus_request *, void *data, int16_t *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_INT16, &out_val);
+}
+
+/* invokes a getter with a 'uint16_t' DBus type */
+static int invoke_get_q(struct sbus_request *dbus_req, void *function_ptr)
+{
+    uint16_t prop_val;
+    uint16_t out_val;
+
+    void (*handler)(struct sbus_request *, void *data, uint16_t *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_UINT16, &out_val);
+}
+
+/* invokes a getter with an array of 'uint8_t' DBus type */
+static int invoke_get_ay(struct sbus_request *dbus_req, void *function_ptr)
+{
+    uint8_t *prop_val;
+    int prop_len;
+    uint8_t *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, uint8_t * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_BYTE, (uint8_t*)out_val, prop_len, sizeof(uint8_t));
+}
+
+/* invokes a getter with a 'const char *' DBus type */
+static int invoke_get_s(struct sbus_request *dbus_req, void *function_ptr)
+{
+    const char * prop_val;
+    const char * out_val;
+
+    void (*handler)(struct sbus_request *, void *data, const char * *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_STRING, &out_val);
+}
+
+/* invokes a getter with a 'uint32_t' DBus type */
+static int invoke_get_u(struct sbus_request *dbus_req, void *function_ptr)
+{
+    uint32_t prop_val;
+    uint32_t out_val;
+
+    void (*handler)(struct sbus_request *, void *data, uint32_t *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_UINT32, &out_val);
+}
+
+/* invokes a getter with a 'uint64_t' DBus type */
+static int invoke_get_t(struct sbus_request *dbus_req, void *function_ptr)
+{
+    uint64_t prop_val;
+    uint64_t out_val;
+
+    void (*handler)(struct sbus_request *, void *data, uint64_t *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_UINT64, &out_val);
+}
+
+/* invokes a getter with an array of 'int64_t' DBus type */
+static int invoke_get_ax(struct sbus_request *dbus_req, void *function_ptr)
+{
+    int64_t *prop_val;
+    int prop_len;
+    int64_t *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, int64_t * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_INT64, (uint8_t*)out_val, prop_len, sizeof(int64_t));
+}
+
+/* invokes a getter with a 'uint8_t' DBus type */
+static int invoke_get_y(struct sbus_request *dbus_req, void *function_ptr)
+{
+    uint8_t prop_val;
+    uint8_t out_val;
+
+    void (*handler)(struct sbus_request *, void *data, uint8_t *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_BYTE, &out_val);
+}
+
+/* invokes a getter with a 'int64_t' DBus type */
+static int invoke_get_x(struct sbus_request *dbus_req, void *function_ptr)
+{
+    int64_t prop_val;
+    int64_t out_val;
+
+    void (*handler)(struct sbus_request *, void *data, int64_t *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
+
+    out_val = prop_val;
+    return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_INT64, &out_val);
+}
+
+/* invokes a getter with an array of 'uint32_t' DBus type */
+static int invoke_get_au(struct sbus_request *dbus_req, void *function_ptr)
+{
+    uint32_t *prop_val;
+    int prop_len;
+    uint32_t *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, uint32_t * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_UINT32, (uint8_t*)out_val, prop_len, sizeof(uint32_t));
+}
+
+/* invokes a getter with an array of 'int16_t' DBus type */
+static int invoke_get_an(struct sbus_request *dbus_req, void *function_ptr)
+{
+    int16_t *prop_val;
+    int prop_len;
+    int16_t *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, int16_t * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_INT16, (uint8_t*)out_val, prop_len, sizeof(int16_t));
+}
+
+/* invokes a getter with an array of 'double' DBus type */
+static int invoke_get_ad(struct sbus_request *dbus_req, void *function_ptr)
+{
+    double *prop_val;
+    int prop_len;
+    double *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, double * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_DOUBLE, (uint8_t*)out_val, prop_len, sizeof(double));
+}
+
+/* invokes a getter with an array of 'int32_t' DBus type */
+static int invoke_get_ai(struct sbus_request *dbus_req, void *function_ptr)
+{
+    int32_t *prop_val;
+    int prop_len;
+    int32_t *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, int32_t * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_INT32, (uint8_t*)out_val, prop_len, sizeof(int32_t));
+}
+
+/* invokes a getter with an array of 'uint64_t' DBus type */
+static int invoke_get_at(struct sbus_request *dbus_req, void *function_ptr)
+{
+    uint64_t *prop_val;
+    int prop_len;
+    uint64_t *out_val;
+
+    void (*handler)(struct sbus_request *, void *data, uint64_t * *, int *) = function_ptr;
+
+    (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val, &prop_len);
+
+    out_val = prop_val;
+    return sbus_request_return_array_as_variant(dbus_req, DBUS_TYPE_UINT64, (uint8_t*)out_val, prop_len, sizeof(uint64_t));
+}
diff --git a/src/tests/sbus_codegen_tests_generated.h b/src/tests/sbus_codegen_tests_generated.h
index 137dab8a08502b482b445f254c42095202047526..9599091dd0627c7af84fb211858d918d9128ecf0 100644
--- a/src/tests/sbus_codegen_tests_generated.h
+++ b/src/tests/sbus_codegen_tests_generated.h
@@ -24,6 +24,27 @@
 #define TEST_PILOT_BLINK "Blink"
 #define TEST_PILOT_EJECT "Eject"
 #define TEST_PILOT_FULLNAME "FullName"
+#define TEST_PILOT_BYTE "byte"
+#define TEST_PILOT_BOOLEAN "boolean"
+#define TEST_PILOT_INT16 "int16"
+#define TEST_PILOT_UINT16 "uint16"
+#define TEST_PILOT_INT32 "int32"
+#define TEST_PILOT_UINT32 "uint32"
+#define TEST_PILOT_INT64 "int64"
+#define TEST_PILOT_UINT64 "uint64"
+#define TEST_PILOT_DOUBLE "double"
+#define TEST_PILOT_STRING "string"
+#define TEST_PILOT_OBJECT_PATH "object_path"
+#define TEST_PILOT_BYTE_ARRAY "byte_array"
+#define TEST_PILOT_INT16_ARRAY "int16_array"
+#define TEST_PILOT_UINT16_ARRAY "uint16_array"
+#define TEST_PILOT_INT32_ARRAY "int32_array"
+#define TEST_PILOT_UINT32_ARRAY "uint32_array"
+#define TEST_PILOT_INT64_ARRAY "int64_array"
+#define TEST_PILOT_UINT64_ARRAY "uint64_array"
+#define TEST_PILOT_DOUBLE_ARRAY "double_array"
+#define TEST_PILOT_STRING_ARRAY "string_array"
+#define TEST_PILOT_OBJECT_PATH_ARRAY "object_path_array"
 
 /* ------------------------------------------------------------------------
  * DBus handlers
@@ -49,6 +70,7 @@ struct com_planetexpress_Ship {
     int (*MoveUniverse)(struct sbus_request *req, void *data, bool arg_smoothly, uint32_t arg_speed_factor);
     int (*crash_now)(struct sbus_request *req, void *data, const char *arg_where);
     sbus_msg_handler_fn Land;
+    void (*com_planetexpress_Ship_get_Color)(struct sbus_request *, void *data, const char * *);
 };
 
 /* finish function for MoveUniverse */
@@ -62,6 +84,28 @@ struct test_pilot {
     struct sbus_vtable vtable; /* derive from sbus_vtable */
     int (*Blink)(struct sbus_request *req, void *data, uint32_t arg_duration);
     int (*Eject)(struct sbus_request *req, void *data, uint8_t arg_byte, bool arg_boolean, int16_t arg_int16, uint16_t arg_uint16, int32_t arg_int32, uint32_t arg_uint32, int64_t arg_int64, uint64_t arg_uint64, double arg_double, const char *arg_string, const char *arg_object_path, uint8_t arg_byte_array[], int len_byte_array, int16_t arg_int16_array[], int len_int16_array, uint16_t arg_uint16_array[], int len_uint16_array, int32_t arg_int32_array[], int len_int32_array, uint32_t arg_uint32_array[], int len_uint32_array, int64_t arg_int64_array[], int len_int64_array, uint64_t arg_uint64_array[], int len_uint64_array, double arg_double_array[], int len_double_array, const char *arg_string_array[], int len_string_array, const char *arg_object_path_array[], int len_object_path_array);
+    void (*test_pilot_get_FullName)(struct sbus_request *, void *data, const char * *);
+    void (*test_pilot_get_byte)(struct sbus_request *, void *data, uint8_t *);
+    void (*test_pilot_get_boolean)(struct sbus_request *, void *data, bool *);
+    void (*test_pilot_get_int16)(struct sbus_request *, void *data, int16_t *);
+    void (*test_pilot_get_uint16)(struct sbus_request *, void *data, uint16_t *);
+    void (*test_pilot_get_int32)(struct sbus_request *, void *data, int32_t *);
+    void (*test_pilot_get_uint32)(struct sbus_request *, void *data, uint32_t *);
+    void (*test_pilot_get_int64)(struct sbus_request *, void *data, int64_t *);
+    void (*test_pilot_get_uint64)(struct sbus_request *, void *data, uint64_t *);
+    void (*test_pilot_get_double)(struct sbus_request *, void *data, double *);
+    void (*test_pilot_get_string)(struct sbus_request *, void *data, const char * *);
+    void (*test_pilot_get_object_path)(struct sbus_request *, void *data, const char * *);
+    void (*test_pilot_get_byte_array)(struct sbus_request *, void *data, uint8_t * *, int *);
+    void (*test_pilot_get_int16_array)(struct sbus_request *, void *data, int16_t * *, int *);
+    void (*test_pilot_get_uint16_array)(struct sbus_request *, void *data, uint16_t * *, int *);
+    void (*test_pilot_get_int32_array)(struct sbus_request *, void *data, int32_t * *, int *);
+    void (*test_pilot_get_uint32_array)(struct sbus_request *, void *data, uint32_t * *, int *);
+    void (*test_pilot_get_int64_array)(struct sbus_request *, void *data, int64_t * *, int *);
+    void (*test_pilot_get_uint64_array)(struct sbus_request *, void *data, uint64_t * *, int *);
+    void (*test_pilot_get_double_array)(struct sbus_request *, void *data, double * *, int *);
+    void (*test_pilot_get_string_array)(struct sbus_request *, void *data, const char * * *, int *);
+    void (*test_pilot_get_object_path_array)(struct sbus_request *, void *data, const char * * *, int *);
 };
 
 /* finish function for Blink */
-- 
1.9.0

-------------- next part --------------
>From 8c84bc4616107a683765cdb1d60105998d87baff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina at redhat.com>
Date: Mon, 12 May 2014 22:54:01 +0200
Subject: [PATCH 6/6] SBUS: Return empty string if a getter returns NULL

In line with getters never returning errors, a getter should return an
empty string instead of NULL in case a string-like property in SSSD is
not set.
---
 src/sbus/sbus_codegen                    | 5 ++++-
 src/tests/sbus_codegen_tests_generated.c | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/sbus/sbus_codegen b/src/sbus/sbus_codegen
index a8f91b784ffaa5a17620792df7285df4b508749f..b671e939fcc7c29a2ba424e785e967111d32323c 100755
--- a/src/sbus/sbus_codegen
+++ b/src/sbus/sbus_codegen
@@ -318,7 +318,10 @@ def source_getter_invoker(prop):
     out(");")
 
     out("")
-    out("    out_val = prop_val;")
+    if prop.type == "s":
+        out("    out_val = prop_val == NULL ? \"\" : prop_val;")
+    else:
+        out("    out_val = prop_val;")
     if prop.is_array:
         out("    return sbus_request_return_array_as_variant(dbus_req, %s, (uint8_t*)out_val, prop_len, sizeof(%s));", prop.dbus_constant, prop.sssd_type)
     else:
diff --git a/src/tests/sbus_codegen_tests_generated.c b/src/tests/sbus_codegen_tests_generated.c
index 4ace0365044ac1edec3c8774da2231c82e054017..0aee500c93665c16bf51365846520ce42fcb5a38 100644
--- a/src/tests/sbus_codegen_tests_generated.c
+++ b/src/tests/sbus_codegen_tests_generated.c
@@ -764,7 +764,7 @@ static int invoke_get_s(struct sbus_request *dbus_req, void *function_ptr)
 
     (handler)(dbus_req, dbus_req->intf->instance_data, &prop_val);
 
-    out_val = prop_val;
+    out_val = prop_val == NULL ? "" : prop_val;
     return sbus_request_return_as_variant(dbus_req, DBUS_TYPE_STRING, &out_val);
 }
 
-- 
1.9.0



More information about the sssd-devel mailing list