From 847968f52740f692805b1fff9b1874e75ad9ad73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Sun, 14 Dec 2014 19:02:49 +0100
Subject: [PATCH 9/9] sbus: support org.freedesktop.DBus.Properties

Bring back org.freedesktop.DBus.Properties with support of
multiple interfaces on single object path.
---
 src/sbus/sssd_dbus_interface.c  |  13 +-
 src/sbus/sssd_dbus_private.h    |   6 +
 src/sbus/sssd_dbus_properties.c | 521 +++++++++++++++++++++++-----------------
 3 files changed, 320 insertions(+), 220 deletions(-)

diff --git a/src/sbus/sssd_dbus_interface.c b/src/sbus/sssd_dbus_interface.c
index cbabd022358814f2c117c36367f6e5b754a7ffe7..16919c967b2d3e11036a78f9328b358da0503db7 100644
--- a/src/sbus/sssd_dbus_interface.c
+++ b/src/sbus/sssd_dbus_interface.c
@@ -304,7 +304,7 @@ sbus_opath_hash_has_path(hash_table_t *table,
  * in the path hierarchy and try to lookup the parent node. This continues
  * until the root is reached.
  */
-static struct sbus_interface *
+struct sbus_interface *
 sbus_opath_hash_lookup_iface(hash_table_t *table,
                              const char *object_path,
                              const char *iface_name)
@@ -547,8 +547,19 @@ sbus_conn_register_iface(struct sbus_connection *conn,
     }
 
     /* register standard interfaces with this object path as well */
+    ret = sbus_conn_register_iface(conn, sbus_properties_vtable(),
+                                   object_path, conn);
+    if (ret != EOK) {
+        return ret;
+    }
+
     ret = sbus_conn_register_iface(conn, sbus_introspect_vtable(),
                                    object_path, conn);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    ret = EOK;
 
     return ret;
 }
diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h
index 62c2d6f9e71e40601830ff797533b0aba647944d..24b6bec18cf8331942480312505b2dce77d2c2ac 100644
--- a/src/sbus/sssd_dbus_private.h
+++ b/src/sbus/sssd_dbus_private.h
@@ -69,6 +69,7 @@ struct sbus_connection {
 /* =Standard=interfaces=================================================== */
 
 struct sbus_vtable *sbus_introspect_vtable(void);
+struct sbus_vtable *sbus_properties_vtable(void);
 
 /* =Watches=============================================================== */
 
@@ -117,6 +118,11 @@ sbus_opath_hash_init(TALLOC_CTX *mem_ctx,
                      struct sbus_connection *conn,
                      hash_table_t **_table);
 
+struct sbus_interface *
+sbus_opath_hash_lookup_iface(hash_table_t *table,
+                             const char *object_path,
+                             const char *iface_name);
+
 errno_t
 sbus_opath_hash_lookup_supported(TALLOC_CTX *mem_ctx,
                                  hash_table_t *table,
diff --git a/src/sbus/sssd_dbus_properties.c b/src/sbus/sssd_dbus_properties.c
index c6bdffda72b5a02f352cdeb27ac75b30bf07d6ba..aee7ef6a014daf2e007ee4f51eef3608249c462b 100644
--- a/src/sbus/sssd_dbus_properties.c
+++ b/src/sbus/sssd_dbus_properties.c
@@ -1,9 +1,12 @@
 /*
     Authors:
         Stef Walter <stefw@redhat.com>
+        Pavel Březina <pbrezina@redhat.com>
 
     Copyright (C) 2014 Red Hat
 
+    SBUS: Interface introspection
+
     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
@@ -18,11 +21,310 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include "config.h"
+
+#include <stdio.h>
+
 #include "util/util.h"
 #include "sbus/sssd_dbus.h"
 #include "sbus/sssd_dbus_meta.h"
 #include "sbus/sssd_dbus_private.h"
 
+#define CHECK_SIGNATURE(req, error, label, exp) do { \
+    const char *__sig; \
+    __sig = dbus_message_get_signature(req->message); \
+    if (strcmp(__sig, exp) != 0) { \
+        error = sbus_error_new(req, DBUS_ERROR_INVALID_ARGS, \
+               "Invalid arguments: expected \"%s\", got \"%s\"", exp, __sig); \
+        goto label; \
+    } \
+} while (0)
+
+struct iface_introspectable {
+    struct sbus_vtable vtable; /* derive from sbus_vtable */
+    int (*Introspect)(struct sbus_request *req, void *data);
+};
+
+struct iface_properties {
+    struct sbus_vtable vtable; /* derive from sbus_vtable */
+    sbus_msg_handler_fn Get;
+    sbus_msg_handler_fn Set;
+    sbus_msg_handler_fn GetAll;
+};
+
+static int sbus_properties_get(struct sbus_request *sbus_req, void *pvt);
+static int sbus_properties_set(struct sbus_request *sbus_req, void *pvt);
+static int sbus_properties_get_all(struct sbus_request *sbus_req, void *pvt);
+
+struct sbus_vtable *
+sbus_properties_vtable(void)
+{
+    /* Properties.Get */
+    static const struct sbus_arg_meta get_args_in[] = {
+        { "interface_name", "s" },
+        { "property_name", "s" },
+        { NULL, }
+    };
+
+    static const struct sbus_arg_meta get_args_out[] = {
+        { "value", "v" },
+        { NULL, }
+    };
+
+    /* Properties.Set */
+    static const struct sbus_arg_meta set_args_in[] = {
+        { "interface_name", "s" },
+        { "property_name", "s" },
+        { "value", "v" },
+        { NULL, }
+    };
+
+    /* Properties.GetAll */
+    static const struct sbus_arg_meta getall_args_in[] = {
+        { "interface_name", "s" },
+        { NULL, }
+    };
+
+    static const struct sbus_arg_meta getall_args_out[] = {
+        { "props", "a{sv}" },
+        { NULL, }
+    };
+
+    static const struct sbus_method_meta iface_methods[] = {
+        {
+            "Get", /* name */
+            get_args_in,
+            get_args_out,
+            offsetof(struct iface_properties, Get),
+            NULL, /* no invoker */
+        },
+        {
+            "Set", /* name */
+            set_args_in,
+            NULL, /* no out_args */
+            offsetof(struct iface_properties, Set),
+            NULL, /* no invoker */
+        },
+        {
+            "GetAll", /* name */
+            getall_args_in,
+            getall_args_out,
+            offsetof(struct iface_properties, GetAll),
+            NULL, /* no invoker */
+        },
+        { NULL, }
+    };
+
+    static const struct sbus_interface_meta iface_meta = {
+        "org.freedesktop.DBus.Properties", /* name */
+        iface_methods,
+        NULL, /* no signals */
+        NULL, /* no properties */
+        NULL, /* no GetAll invoker */
+    };
+
+    static struct iface_properties iface = {
+        { &iface_meta, 0 },
+        .Get = sbus_properties_get,
+        .Set = sbus_properties_set,
+        .GetAll = sbus_properties_get_all,
+    };
+
+    return &iface.vtable;
+}
+
+static int sbus_properties_invoke(TALLOC_CTX *mem_ctx,
+                                  struct sbus_request *sbus_req,
+                                  struct sbus_interface *iface,
+                                  sbus_msg_handler_fn handler_fn,
+                                  void *handler_data,
+                                  sbus_method_invoker_fn invoker_fn)
+{
+    struct sbus_request *sbus_subreq;
+
+    /* Create new sbus_request to so it contain given interface. The
+     * old sbus_request talloc context will be attached to this new one
+     * so it is freed together. */
+    sbus_subreq = sbus_new_request(mem_ctx, iface, sbus_req->message);
+    if (sbus_subreq == NULL) {
+        return ENOMEM;
+    }
+
+    talloc_steal(sbus_subreq, sbus_req);
+
+    sbus_request_invoke_or_finish(sbus_subreq, handler_fn, handler_data,
+                                  invoker_fn);
+
+    return EOK;
+}
+
+static int sbus_properties_get(struct sbus_request *sbus_req, void *pvt)
+{
+    DBusError *error;
+    struct sbus_connection *conn;
+    struct sbus_interface *iface;
+    const struct sbus_property_meta *prop;
+    sbus_msg_handler_fn handler_fn;
+    const char *interface_name;
+    const char *property_name;
+    bool bret;
+
+    conn = talloc_get_type(pvt, struct sbus_connection);
+
+    CHECK_SIGNATURE(sbus_req, error, fail, "ss");
+
+    bret = sbus_request_parse_or_finish(sbus_req,
+                                        DBUS_TYPE_STRING, &interface_name,
+                                        DBUS_TYPE_STRING, &property_name,
+                                        DBUS_TYPE_INVALID);
+    if (!bret) {
+        /* request was handled */
+        return EOK;
+    }
+
+    /* find interface */
+    iface = sbus_opath_hash_lookup_iface(conn->managed_paths, sbus_req->path,
+                                         interface_name);
+    if (iface == NULL) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_INTERFACE,
+                               "Unknown interface");
+        goto fail;
+    }
+
+    /* find property handler */
+    prop = sbus_meta_find_property(iface->vtable->meta, property_name);
+    if (prop == NULL) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_PROPERTY,
+                               "Unknown property");
+        goto fail;
+    }
+
+    if (!(prop->flags & SBUS_PROPERTY_READABLE)) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_ACCESS_DENIED,
+                               "Property is not readable");
+        goto fail;
+    }
+
+    handler_fn = VTABLE_FUNC(iface->vtable, prop->vtable_offset_get);
+    if (handler_fn == NULL) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_NOT_SUPPORTED,
+                               "Getter is not implemented");
+        goto fail;
+    }
+
+    return sbus_properties_invoke(conn, sbus_req, iface, handler_fn,
+                                  iface->instance_data, prop->invoker_get);
+
+fail:
+    return sbus_request_fail_and_finish(sbus_req, error);
+}
+
+static int sbus_properties_set(struct sbus_request *sbus_req, void *pvt)
+{
+    DBusError *error;
+    DBusMessageIter iter;
+    DBusMessageIter iter_variant;
+    struct sbus_connection *conn;
+    struct sbus_interface *iface;
+    const struct sbus_property_meta *prop;
+    const char *interface_name;
+    const char *property_name;
+    const char *variant_sig;
+    sbus_msg_handler_fn handler_fn;
+
+    conn = talloc_get_type(pvt, struct sbus_connection);
+
+    CHECK_SIGNATURE(sbus_req, error, fail, "ssv");
+
+    /* get interface and property */
+    dbus_message_iter_init(sbus_req->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);
+
+    /* find interface */
+    iface = sbus_opath_hash_lookup_iface(conn->managed_paths, sbus_req->path,
+                                         interface_name);
+    if (iface == NULL) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_INTERFACE,
+                               "Unknown interface");
+        goto fail;
+    }
+
+    /* find property handler */
+    prop = sbus_meta_find_property(iface->vtable->meta, property_name);
+    if (prop == NULL) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_PROPERTY,
+                               "Unknown property");
+        goto fail;
+    }
+
+    if (!(prop->flags & SBUS_PROPERTY_WRITABLE)) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_ACCESS_DENIED,
+                               "Property is not writable");
+        goto fail;
+    }
+
+    handler_fn = VTABLE_FUNC(iface->vtable, prop->vtable_offset_set);
+    if (handler_fn == NULL) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_NOT_SUPPORTED,
+                               "Setter is not implemented");
+        goto fail;
+    }
+
+    /* check variant type */
+    dbus_message_iter_recurse(&iter, &iter_variant);
+    variant_sig = dbus_message_iter_get_signature(&iter_variant);
+    if (strcmp(prop->type, variant_sig) != 0) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_INVALID_ARGS,
+                               "Invalid data type for property");
+        goto fail;
+    }
+
+    return sbus_properties_invoke(conn, sbus_req, iface, handler_fn,
+                                  iface->instance_data, prop->invoker_set);
+
+fail:
+    return sbus_request_fail_and_finish(sbus_req, error);
+}
+
+static int sbus_properties_get_all(struct sbus_request *sbus_req, void *pvt)
+{
+    DBusError *error;
+    struct sbus_connection *conn;
+    struct sbus_interface *iface;
+    const char *interface_name;
+    bool bret;
+
+    conn = talloc_get_type(pvt, struct sbus_connection);
+
+    CHECK_SIGNATURE(sbus_req, error, fail, "s");
+
+    bret = sbus_request_parse_or_finish(sbus_req,
+                                        DBUS_TYPE_STRING, &interface_name,
+                                        DBUS_TYPE_INVALID);
+    if (!bret) {
+        /* request was handled */
+        return EOK;
+    }
+
+    /* find interface */
+    iface = sbus_opath_hash_lookup_iface(conn->managed_paths, sbus_req->path,
+                                         interface_name);
+    if (iface == NULL) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_UNKNOWN_INTERFACE,
+                               "Unknown interface");
+        goto fail;
+    }
+
+    return sbus_properties_invoke(conn, sbus_req, iface, NULL, NULL,
+                                  iface->vtable->meta->invoker_get_all);
+
+fail:
+    return sbus_request_fail_and_finish(sbus_req, error);
+}
+
 static char *
 type_to_string(char type, char *str)
 {
@@ -166,222 +468,3 @@ int sbus_add_array_as_variant_to_dict(DBusMessageIter *iter_dict,
 
     return EOK;
 }
-
-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;
-}
-
-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;
-}
-
-static int
-dispatch_properties_get_all(struct sbus_connection *conn,
-                            struct sbus_interface *intf,
-                            DBusMessage *message)
-{
-    struct sbus_request *req;
-    const char *signature;
-    const struct sbus_interface_meta *meta;
-    const char *interface_name;
-    DBusMessageIter iter;
-
-    req = sbus_new_request(conn, intf, message);
-    if (req == NULL) {
-        return ENOMEM;
-    }
-
-    meta = intf->vtable->meta;
-
-    signature = dbus_message_get_signature(message);
-    /* Interface name */
-    if (strcmp(signature, "s") != 0) {
-        return sbus_request_fail_and_finish(req,
-                    sbus_error_new(req,
-                                   DBUS_ERROR_INVALID_ARGS,
-                                   "Invalid argument types passed " \
-                                   "to GetAll method"));
-    }
-
-    dbus_message_iter_init(message, &iter);
-    dbus_message_iter_get_basic(&iter, &interface_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"));
-    }
-
-    sbus_request_invoke_or_finish(req, NULL, NULL, meta->invoker_get_all);
-    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);
-    } else if (strcmp (member, "Get") == 0) {
-        return dispatch_properties_get(dbus_req->conn,
-                                       dbus_req->intf,
-                                       dbus_req->message);
-    } else if (strcmp (member, "GetAll") == 0) {
-        return dispatch_properties_get_all(dbus_req->conn,
-                                            dbus_req->intf,
-                                            dbus_req->message);
-    }
-
-    return ERR_SBUS_NOSUP;
-}
-- 
1.7.11.7

