diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/sbus/interface | |
parent | Initial commit. (diff) | |
download | sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip |
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/sbus/interface')
-rw-r--r-- | src/sbus/interface/sbus_interface.c | 464 | ||||
-rw-r--r-- | src/sbus/interface/sbus_introspection.c | 679 | ||||
-rw-r--r-- | src/sbus/interface/sbus_iterator_readers.c | 414 | ||||
-rw-r--r-- | src/sbus/interface/sbus_iterator_readers.h | 126 | ||||
-rw-r--r-- | src/sbus/interface/sbus_iterator_writers.c | 365 | ||||
-rw-r--r-- | src/sbus/interface/sbus_iterator_writers.h | 126 | ||||
-rw-r--r-- | src/sbus/interface/sbus_properties.c | 894 | ||||
-rw-r--r-- | src/sbus/interface/sbus_properties_parser.c | 198 | ||||
-rw-r--r-- | src/sbus/interface/sbus_std_signals.c | 65 |
9 files changed, 3331 insertions, 0 deletions
diff --git a/src/sbus/interface/sbus_interface.c b/src/sbus/interface/sbus_interface.c new file mode 100644 index 0000000..e7fc05d --- /dev/null +++ b/src/sbus/interface/sbus_interface.c @@ -0,0 +1,464 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <talloc.h> +#include <string.h> + +#include "sbus/sbus_annotations.h" +#include "sbus/sbus_interface_declarations.h" + +static struct sbus_handler +sbus_sync_handler(sbus_handler_sync_fn handler, + sbus_handler_data data) +{ + struct sbus_handler object = { + .type = SBUS_HANDLER_SYNC, + .sync = handler, + .data = data + }; + + return object; +} + +static struct sbus_handler +sbus_async_handler(sbus_handler_send_fn handler_send, + sbus_handler_recv_fn handler_recv, + sbus_handler_data data) +{ + struct sbus_handler object = { + .type = SBUS_HANDLER_ASYNC, + .async_send = handler_send, + .async_recv = handler_recv, + .data = data + }; + + return object; +} + +struct sbus_method +sbus_method_sync(const char *name, + const struct sbus_method_arguments *arguments, + const struct sbus_annotation *annotations, + sbus_invoker_issue invoker_issue, + sbus_invoker_keygen invoker_keygen, + sbus_handler_sync_fn handler, + sbus_handler_data data) +{ + struct sbus_method object = { + .name = name, + .annotations = annotations, + .invoker = {.issue = invoker_issue, .keygen = invoker_keygen}, + .handler = sbus_sync_handler(handler, data), + .arguments = arguments + }; + + return object; +} + +struct sbus_method +sbus_method_async(const char *name, + const struct sbus_method_arguments *arguments, + const struct sbus_annotation *annotations, + sbus_invoker_issue invoker_issue, + sbus_invoker_keygen invoker_keygen, + sbus_handler_send_fn handler_send, + sbus_handler_recv_fn handler_recv, + sbus_handler_data data) +{ + struct sbus_method object = { + .name = name, + .annotations = annotations, + .invoker = {.issue = invoker_issue, .keygen = invoker_keygen}, + .handler = sbus_async_handler(handler_send, handler_recv, data), + .arguments = arguments + }; + + return object; +} + +static struct sbus_method * +sbus_method_copy(TALLOC_CTX *mem_ctx, + const struct sbus_method *input) +{ + struct sbus_method *copy; + size_t count; + + for (count = 0; input[count].name != NULL; count++); + + copy = talloc_zero_array(mem_ctx, struct sbus_method, count + 1); + if (copy == NULL) { + return NULL; + } + + /* All data is either pointer to a static data or it is not a pointer. + * We can just copy it. */ + memcpy(copy, input, sizeof(struct sbus_method) * (count + 1)); + + return copy; +} + +struct sbus_signal +sbus_signal(const char *name, + const struct sbus_argument *arguments, + const struct sbus_annotation *annotations) +{ + struct sbus_signal object = { + .name = name, + .arguments = arguments, + .annotations = annotations + }; + + return object; +} + +static struct sbus_signal * +sbus_signal_copy(TALLOC_CTX *mem_ctx, + const struct sbus_signal *input) +{ + struct sbus_signal *copy; + size_t count; + + for (count = 0; input[count].name != NULL; count++); + + copy = talloc_zero_array(mem_ctx, struct sbus_signal, count + 1); + if (copy == NULL) { + return NULL; + } + + /* All data is either pointer to a static data or it is not a pointer. + * We can just copy it. */ + memcpy(copy, input, sizeof(struct sbus_signal) * (count + 1)); + + return copy; +} + +struct sbus_property +sbus_property_sync(const char *name, + const char *type, + enum sbus_property_access access, + const struct sbus_annotation *annotations, + sbus_invoker_issue invoker_issue, + sbus_handler_sync_fn handler, + sbus_handler_data data) +{ + struct sbus_property object = { + .name = name, + .type = type, + .access = access, + .annotations = annotations, + .invoker = {.issue = invoker_issue, .keygen = NULL}, + .handler = sbus_sync_handler(handler, data) + }; + + return object; +} + +struct sbus_property +sbus_property_async(const char *name, + const char *type, + enum sbus_property_access access, + const struct sbus_annotation *annotations, + sbus_invoker_issue invoker_issue, + sbus_handler_send_fn handler_send, + sbus_handler_recv_fn handler_recv, + sbus_handler_data data) +{ + struct sbus_property object = { + .name = name, + .type = type, + .access = access, + .annotations = annotations, + .invoker = {.issue = invoker_issue, .keygen = NULL}, + .handler = sbus_async_handler(handler_send, handler_recv, data) + }; + + return object; +} + +static struct sbus_property * +sbus_property_copy(TALLOC_CTX *mem_ctx, + const struct sbus_property *input) +{ + struct sbus_property *copy; + size_t count; + + for (count = 0; input[count].name != NULL; count++); + + copy = talloc_zero_array(mem_ctx, struct sbus_property, count + 1); + if (copy == NULL) { + return NULL; + } + + /* All data is either pointer to a static data or it is not a pointer. + * We can just copy it. */ + memcpy(copy, input, sizeof(struct sbus_property) * (count + 1)); + + return copy; +} + +struct sbus_interface +sbus_interface(const char *name, + const struct sbus_annotation *annotations, + const struct sbus_method *methods, + const struct sbus_signal *signals, + const struct sbus_property *properties) +{ + struct sbus_interface object = { + .name = name, + .annotations = annotations, + .methods = methods, + .signals = signals, + .properties = properties + }; + + return object; +} + +struct sbus_interface * +sbus_interface_copy(TALLOC_CTX *mem_ctx, + const struct sbus_interface *input) +{ + struct sbus_interface *copy; + + copy = talloc_zero(mem_ctx, struct sbus_interface); + if (copy == NULL) { + return NULL; + } + + /* Name and annotations are pointer to static data, no need to copy them. */ + copy->name = input->name; + copy->annotations = input->annotations; + + copy->methods = sbus_method_copy(copy, input->methods); + copy->signals = sbus_signal_copy(copy, input->signals); + copy->properties = sbus_property_copy(copy, input->properties); + + if (copy->methods == NULL || copy->signals == NULL + || copy->properties == NULL) { + talloc_free(copy); + return NULL; + } + + return copy; +} + +const struct sbus_method * +sbus_interface_find_method(struct sbus_interface *iface, + const char *method_name) +{ + unsigned int i; + + for (i = 0; iface->methods[i].name != NULL; i++) { + if (strcmp(iface->methods[i].name, method_name) == 0) { + return &iface->methods[i]; + } + } + + return NULL; +} + +const struct sbus_property * +sbus_interface_find_property(struct sbus_interface *iface, + enum sbus_property_access access, + const char *property_name) +{ + unsigned int i; + + for (i = 0; iface->properties[i].name != NULL; i++) { + if (iface->properties[i].access != access) { + continue; + } + + if (strcmp(iface->properties[i].name, property_name) == 0) { + return &iface->properties[i]; + } + } + + return NULL; +} + +struct sbus_listener +sbus_listener_sync(const char *interface, + const char *signal_name, + const char *object_path, + sbus_invoker_issue invoker_issue, + sbus_invoker_keygen invoker_keygen, + sbus_handler_sync_fn handler, + sbus_handler_data data) +{ + struct sbus_listener object = { + .interface = interface, + .signal_name = signal_name, + .object_path = object_path, + .invoker = {.issue = invoker_issue, .keygen = invoker_keygen}, + .handler = sbus_sync_handler(handler, data) + }; + + return object; +} + +struct sbus_listener +sbus_listener_async(const char *interface, + const char *signal_name, + const char *object_path, + sbus_invoker_issue invoker_issue, + sbus_invoker_keygen invoker_keygen, + sbus_handler_send_fn handler_send, + sbus_handler_recv_fn handler_recv, + sbus_handler_data data) +{ + struct sbus_listener object = { + .interface = interface, + .signal_name = signal_name, + .object_path = object_path, + .invoker = {.issue = invoker_issue, .keygen = invoker_keygen}, + .handler = sbus_async_handler(handler_send, handler_recv, data) + }; + + return object; +} + +struct sbus_listener * +sbus_listener_copy(TALLOC_CTX *mem_ctx, + const struct sbus_listener *input) +{ + /* All data is either pointer to a static data or it is not a pointer. + * We can just copy it. */ + return talloc_memdup(mem_ctx, input, sizeof(struct sbus_listener)); +} + +struct sbus_node +sbus_node_sync(const char *path, + sbus_handler_sync_fn factory, + sbus_handler_data data) +{ + struct sbus_node object = { + .path = path, + .factory = sbus_sync_handler(factory, data) + }; + + return object; +} + +struct sbus_node +sbus_node_async(const char *path, + sbus_handler_send_fn factory_send, + sbus_handler_recv_fn factory_recv, + sbus_handler_data data) +{ + struct sbus_node object = { + .path = path, + .factory = sbus_async_handler(factory_send, factory_recv, data) + }; + + return object; +} + +struct sbus_node * +sbus_node_copy(TALLOC_CTX *mem_ctx, + struct sbus_node *input) +{ + struct sbus_node *copy; + + copy = talloc_zero(mem_ctx, struct sbus_node); + if (copy == NULL) { + return NULL; + } + + copy->path = talloc_strdup(copy, input->path); + if (copy->path == NULL) { + talloc_free(copy); + return NULL; + } + + copy->factory = input->factory; + + return copy; +} + +const char * +sbus_annotation_find(const struct sbus_annotation *annotations, + const char *name) +{ + int i; + + if (annotations == NULL) { + return NULL; + } + + for (i = 0; annotations[i].name != NULL; i++) { + if (strcmp(annotations[i].name, name) == 0) { + return annotations[i].value; + } + } + + return NULL; +} + +bool +sbus_annotation_find_as_bool(const struct sbus_annotation *annotations, + const char *name) +{ + const char *value; + + value = sbus_annotation_find(annotations, name); + + if (value != NULL && strcasecmp(value, "true") == 0) { + return true; + } + + return false; +} + +static void +sbus_warn_deprecated(const struct sbus_annotation *annotations, + const char *iface_name, + const char *method_name) +{ + const char *by; + const char *member; + const char *dot; + + if (annotations == NULL) { + return; + } + + if (sbus_annotation_find_as_bool(annotations, SBUS_ANNOTATION_DEPRECATED)) { + member = method_name == NULL ? "" : method_name; + dot = method_name == NULL ? "" : "."; + + by = sbus_annotation_find(annotations, SBUS_ANNOTATION_DEPRECATED_BY); + if (by != NULL) { + DEBUG(SSSDBG_IMPORTANT_INFO, "%s%s%s is deprecated by %s\n", + iface_name, dot, member, by); + } else { + DEBUG(SSSDBG_IMPORTANT_INFO, "%s%s%s is deprecated\n", + iface_name, dot, member); + } + } +} + +void +sbus_annotation_warn(const struct sbus_interface *iface, + const struct sbus_method *method) +{ + sbus_warn_deprecated(iface->annotations, iface->name, NULL); + sbus_warn_deprecated(method->annotations, iface->name, method->name); +} diff --git a/src/sbus/interface/sbus_introspection.c b/src/sbus/interface/sbus_introspection.c new file mode 100644 index 0000000..8633837 --- /dev/null +++ b/src/sbus/interface/sbus_introspection.c @@ -0,0 +1,679 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> +#include <stdio.h> + +#include "util/util.h" +#include "util/dlinklist.h" +#include "sbus/sbus_request.h" +#include "sbus/sbus_private.h" +#include "sbus/sbus_interface.h" +#include "sbus/interface_dbus/sbus_dbus_server.h" + +#define FMT_DOCTYPE \ + "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \ + " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" + +#define FMT_NODE "<node name=\"%s\">\n" +#define FMT_IFACE " <interface name=\"%s\">\n" +#define FMT_ANNOTATION " %s<annotation name=\"%s\" value=\"%s\" />\n" +#define FMT_METHOD_EMPTY " <method name=\"%s\" />\n" +#define FMT_METHOD_OPEN " <method name=\"%s\">\n" +#define FMT_METHOD_ARG " <arg type=\"%s\" name=\"%s\" direction=\"%s\" />\n" +#define FMT_METHOD_CLOSE " </method>\n" +#define FMT_SIGNAL_EMPTY " <signal name=\"%s\" />\n" +#define FMT_SIGNAL_OPEN " <signal name=\"%s\">\n" +#define FMT_SIGNAL_ARG " <arg type=\"%s\" name=\"%s\" />\n" +#define FMT_SIGNAL_CLOSE " </signal>\n" +#define FMT_PROPERTY_EMPTY " <property name=\"%s\" type=\"%s\" access=\"%s\" />\n" +#define FMT_PROPERTY_OPEN " <property name=\"%s\" type=\"%s\" access=\"%s\">\n" +#define FMT_PROPERTY_CLOSE " </property>\n" +#define FMT_IFACE_CLOSE " </interface>\n" +#define FMT_CHILD_NODE " <node name=\"%s\" />\n" +#define FMT_NODE_CLOSE "</node>\n" + +#define WRITE_OR_FAIL(file, ret, label, fmt, ...) do { \ + ret = fprintf(file, fmt, ##__VA_ARGS__); \ + if (ret < 0) { \ + ret = EIO; \ + goto label; \ + } \ +} while (0) + +#define EMPTY(field) ((field) == NULL || (field)[0].name == NULL) + +enum sbus_arg_type { + SBUS_ARG_IN, + SBUS_ARG_OUT, + SBUS_ARG_SIGNAL +}; + +static errno_t +sbus_introspect_annotations(FILE *file, + bool inside, + const struct sbus_annotation *annotations) +{ + errno_t ret; + const char *indent = inside ? " " : ""; + int i; + + if (annotations == NULL) { + return EOK; + } + + for (i = 0; annotations[i].name != NULL; i++) { + WRITE_OR_FAIL(file, ret, done, FMT_ANNOTATION, indent, + annotations[i].name, annotations[i].value); + } + + ret = EOK; + +done: + return ret; +} + +static errno_t +sbus_introspect_args(FILE *file, + enum sbus_arg_type type, + const struct sbus_argument *args) +{ + errno_t ret; + int i; + + if (args == NULL) { + return EOK; + } + + for (i = 0; args[i].name != NULL; i++) { + switch (type) { + case SBUS_ARG_SIGNAL: + WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_ARG, + args[i].type, args[i].name); + break; + case SBUS_ARG_IN: + WRITE_OR_FAIL(file, ret, done, FMT_METHOD_ARG, + args[i].type, args[i].name, "in"); + break; + case SBUS_ARG_OUT: + WRITE_OR_FAIL(file, ret, done, FMT_METHOD_ARG, + args[i].type, args[i].name, "out"); + break; + } + } + + ret = EOK; + +done: + return ret; +} + +static errno_t +sbus_introspect_methods(FILE *file, + const struct sbus_method *methods) +{ + errno_t ret; + int i; + + if (methods == NULL) { + return EOK; + } + + for (i = 0; methods[i].name != NULL; i++) { + if (EMPTY(methods[i].annotations) + && EMPTY(methods[i].arguments->input) + && EMPTY(methods[i].arguments->output)) { + WRITE_OR_FAIL(file, ret, done, FMT_METHOD_EMPTY, methods[i].name); + continue; + } + + WRITE_OR_FAIL(file, ret, done, FMT_METHOD_OPEN, methods[i].name); + + ret = sbus_introspect_annotations(file, true, methods[i].annotations); + if (ret != EOK) { + goto done; + } + + ret = sbus_introspect_args(file, SBUS_ARG_IN, + methods[i].arguments->input); + if (ret != EOK) { + goto done; + } + + ret = sbus_introspect_args(file, SBUS_ARG_OUT, + methods[i].arguments->output); + if (ret != EOK) { + goto done; + } + + WRITE_OR_FAIL(file, ret, done, FMT_METHOD_CLOSE); + } + + ret = EOK; + +done: + return ret; +} + +static errno_t +sbus_introspect_signals(FILE *file, + const struct sbus_signal *signals) +{ + errno_t ret; + int i; + + if (signals == NULL) { + return EOK; + } + + for (i = 0; signals[i].name != NULL; i++) { + if (EMPTY(signals[i].annotations) && EMPTY(signals[i].arguments)) { + WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_EMPTY, signals[i].name); + continue; + } + + WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_OPEN, signals[i].name); + + ret = sbus_introspect_annotations(file, true, signals[i].annotations); + if (ret != EOK) { + goto done; + } + + ret = sbus_introspect_args(file, SBUS_ARG_SIGNAL, signals[i].arguments); + if (ret != EOK) { + goto done; + } + + WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_CLOSE); + } + + ret = EOK; + +done: + return ret; +} + +struct sbus_introspect_property { + const char *name; + const char *type; + const struct sbus_annotation *annotations; + enum sbus_property_access access; +}; + +static void +sbus_introspect_property_set(struct sbus_introspect_property *properties, + const struct sbus_property *property) +{ + int i; + + for (i = 0; properties[i].name != NULL; i++) { + if (strcmp(properties[i].name, property->name) == 0) { + break; + } + } + + /* Name, type and annotation is the same for both getter and setter. + * We just need to update access mode. */ + properties[i].name = property->name; + properties[i].type = property->type; + properties[i].annotations = property->annotations; + properties[i].access |= property->access; +} + +static const char * +sbus_introspect_property_mode(struct sbus_introspect_property *property) +{ + switch (property->access) { + case SBUS_PROPERTY_READABLE: + return "read"; + case SBUS_PROPERTY_WRITABLE: + return "write"; + default: + return "readwrite"; + } +} + +static errno_t +sbus_introspect_properties(FILE *file, + const struct sbus_property *properties) +{ + struct sbus_introspect_property *props; + const char *mode; + errno_t ret; + int len; + int i; + + if (properties == NULL) { + return EOK; + } + + for (len = 0; properties[len].name != NULL ; len++); + + props = talloc_zero_array(NULL, struct sbus_introspect_property, len + 1); + if (props == NULL) { + return ENOMEM; + } + + for (i = 0; properties[i].name != NULL; i++) { + sbus_introspect_property_set(props, &properties[i]); + } + + for (i = 0; props[i].name != NULL; i++) { + mode = sbus_introspect_property_mode(&props[i]); + + if (EMPTY(props[i].annotations)) { + WRITE_OR_FAIL(file, ret, done, FMT_PROPERTY_EMPTY, + props[i].name, props[i].type, mode); + continue; + } + + WRITE_OR_FAIL(file, ret, done, FMT_PROPERTY_OPEN, + props[i].name, props[i].type, mode); + + ret = sbus_introspect_annotations(file, true, props[i].annotations); + if (ret != EOK) { + goto done; + } + + WRITE_OR_FAIL(file, ret, done, FMT_PROPERTY_CLOSE); + } + + ret = EOK; + +done: + talloc_free(props); + return ret; +} + +static int +sbus_introspect_iface(FILE *file, struct sbus_interface *iface) +{ + errno_t ret; + + WRITE_OR_FAIL(file, ret, done, FMT_IFACE, iface->name); + + ret = sbus_introspect_annotations(file, false, iface->annotations); + if (ret != EOK) { + goto done; + } + + ret = sbus_introspect_methods(file, iface->methods); + if (ret != EOK) { + goto done; + } + + ret = sbus_introspect_signals(file, iface->signals); + if (ret != EOK) { + goto done; + } + + ret = sbus_introspect_properties(file, iface->properties); + if (ret != EOK) { + goto done; + } + + WRITE_OR_FAIL(file, ret, done, FMT_IFACE_CLOSE); + + ret = EOK; + +done: + return ret; +} + +static int +sbus_introspect_nodes(FILE *file, const char **nodes) +{ + errno_t ret; + int i; + + if (nodes == NULL) { + return EOK; + } + + for (i = 0; nodes[i] != NULL; i++) { + WRITE_OR_FAIL(file, ret, done, FMT_CHILD_NODE, nodes[i]); + } + + ret = EOK; + +done: + return ret; +} + +static char * +sbus_introspect(TALLOC_CTX *mem_ctx, + const char *node, + const char **nodes, + struct sbus_interface_list *list) +{ + struct sbus_interface_list *item; + char *introspection = NULL; + FILE *memstream; + char *buffer; + size_t size; + errno_t ret; + + memstream = open_memstream(&buffer, &size); + if (memstream == NULL) { + goto done; + } + + WRITE_OR_FAIL(memstream, ret, done, FMT_DOCTYPE); + WRITE_OR_FAIL(memstream, ret, done, FMT_NODE, node); + + DLIST_FOR_EACH(item, list) { + ret = sbus_introspect_iface(memstream, item->interface); + if (ret != EOK) { + goto done; + } + } + + ret = sbus_introspect_nodes(memstream, nodes); + if (ret != EOK) { + goto done; + } + + WRITE_OR_FAIL(memstream, ret, done, FMT_NODE_CLOSE); + + fflush(memstream); + introspection = talloc_memdup(mem_ctx, buffer, size + 1); + +done: + if (memstream != NULL) { + fclose(memstream); + free(buffer); + } + + return introspection; +} + +typedef errno_t +(*sbus_node_factory_sync)(TALLOC_CTX *, const char *, void *, const char ***); + +typedef struct tevent_req * +(*sbus_node_factory_send)(TALLOC_CTX *, struct tevent_context *, + const char *, void *); + +typedef errno_t +(*sbus_node_factory_recv)(TALLOC_CTX *, struct tevent_req *, const char ***); + +struct sbus_acquire_nodes_state { + const char **nodes; + struct sbus_handler *handler; +}; + +static void sbus_acquire_nodes_done(struct tevent_req *subreq); + +static struct tevent_req * +sbus_acquire_nodes_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_router *router, + const char *path) +{ + struct sbus_acquire_nodes_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + struct sbus_node *node; + sbus_node_factory_sync handler_sync; + sbus_node_factory_send handler_send; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sbus_acquire_nodes_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + node = sbus_router_nodes_lookup(router->nodes, path); + if (node == NULL) { + /* If there is no node factory registered and it is a root path, + * we return all known paths to the router. */ + if (strcmp(path, "/") == 0) { + state->nodes = sbus_router_paths_nodes(state, router->paths); + } else { + state->nodes = NULL; + } + ret = EOK; + goto done; + } + + state->handler = &node->factory; + + switch (node->factory.type) { + case SBUS_HANDLER_SYNC: + handler_sync = node->factory.sync; + if (handler_sync == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n"); + ret = ERR_INTERNAL; + goto done; + } + + ret = handler_sync(state, path, node->factory.data, &state->nodes); + goto done; + case SBUS_HANDLER_ASYNC: + handler_send = node->factory.async_send; + if (handler_send == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n"); + ret = ERR_INTERNAL; + goto done; + } + + subreq = handler_send(state, ev, path, node->factory.data); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sbus_acquire_nodes_done, req); + break; + } + + ret = EAGAIN; + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void sbus_acquire_nodes_done(struct tevent_req *subreq) +{ + struct sbus_acquire_nodes_state *state; + sbus_node_factory_recv handler_recv; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sbus_acquire_nodes_state); + + handler_recv = state->handler->async_recv; + if (handler_recv == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n"); + tevent_req_error(req, ERR_INTERNAL); + return; + } + + ret = handler_recv(state, subreq, &state->nodes); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +static errno_t +sbus_acquire_nodes_recv(struct tevent_req *req, + const char ***_nodes) +{ + struct sbus_acquire_nodes_state *state; + state = tevent_req_data(req, struct sbus_acquire_nodes_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + /* We keep the nodes allocated on this request state, so we do not have + * to expect that state->nodes is a talloc context. This way, it may + * be static array. */ + + *_nodes = state->nodes; + + return EOK; +} + +struct sbus_introspection_state { + struct sbus_interface_list *list; + const char *introspection; + const char *path; +}; + +static void sbus_introspection_done(struct tevent_req *subreq); + +static struct tevent_req * +sbus_introspection_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct sbus_router *router) +{ + struct sbus_introspection_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sbus_introspection_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->path = sbus_req->path; + state->introspection = NULL; + + ret = sbus_router_paths_supported(state, router->paths, + sbus_req->path, &state->list); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to acquire interface list " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + subreq = sbus_acquire_nodes_send(mem_ctx, ev, router, sbus_req->path); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sbus_introspection_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void sbus_introspection_done(struct tevent_req *subreq) +{ + struct sbus_introspection_state *state; + struct tevent_req *req; + const char **nodes; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sbus_introspection_state); + + /* We keep the nodes allocated on subrequest state, so we do not have + * to expect that it is a talloc context and allow it also as a static + * array. Therefore we must free subreq later. */ + + ret = sbus_acquire_nodes_recv(subreq, &nodes); + if (ret != EOK) { + goto done; + } + + state->introspection = sbus_introspect(state, state->path, + nodes, state->list); + if (state->introspection == NULL) { + ret = ENOMEM; + goto done; + } + +done: + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; +} + +static errno_t +sbus_introspection_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + const char **_introspection) +{ + struct sbus_introspection_state *state; + state = tevent_req_data(req, struct sbus_introspection_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_introspection = talloc_steal(mem_ctx, state->introspection); + + return EOK; +} + +errno_t +sbus_register_introspection(struct sbus_router *router) +{ + + SBUS_INTERFACE(iface, + org_freedesktop_DBus_Introspectable, + SBUS_METHODS( + SBUS_ASYNC(METHOD, org_freedesktop_DBus_Introspectable, Introspect, + sbus_introspection_send, sbus_introspection_recv, + router) + ), + SBUS_WITHOUT_SIGNALS, + SBUS_WITHOUT_PROPERTIES + ); + + struct sbus_path paths[] = { + {"/", &iface}, + {"/*", &iface}, + {NULL, NULL} + }; + + return sbus_router_add_path_map(router, paths); +} diff --git a/src/sbus/interface/sbus_iterator_readers.c b/src/sbus/interface/sbus_iterator_readers.c new file mode 100644 index 0000000..6113f6d --- /dev/null +++ b/src/sbus/interface/sbus_iterator_readers.c @@ -0,0 +1,414 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <errno.h> +#include <dbus/dbus.h> + +#include "util/util.h" +#include "sbus/interface/sbus_iterator_readers.h" + +static errno_t +sbus_iterator_read_basic(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + int dbus_type, + void *_value_ptr) +{ + int arg_type; + char **strptr; + char *str; + + arg_type = dbus_message_iter_get_arg_type(iterator); + if (arg_type != dbus_type) { + return ERR_SBUS_INVALID_TYPE; + } + + dbus_message_iter_get_basic(iterator, _value_ptr); + dbus_message_iter_next(iterator); + + switch (dbus_type) { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + strptr = (char**)_value_ptr; + str = talloc_strdup(mem_ctx, *strptr); + if (str == NULL) { + return ENOMEM; + } + *strptr = str; + break; + default: + break; + } + + return EOK; +} + +static errno_t +_sbus_iterator_read_basic_array(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + int dbus_type, + int element_size, + void **_value_ptr) +{ + DBusMessageIter subiter; + uint8_t *arrayptr; + void *array = NULL; + int arg_type; + int count; + errno_t ret; + int i; + + arg_type = dbus_message_iter_get_arg_type(iterator); + if (arg_type != DBUS_TYPE_ARRAY) { + ret = ERR_SBUS_INVALID_TYPE; + goto done; + } + + count = dbus_message_iter_get_element_count(iterator); + dbus_message_iter_recurse(iterator, &subiter); + + /* NULL-terminated array for pointer types */ + switch (dbus_type) { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + array = talloc_zero_size(mem_ctx, (size_t)(count + 1) * element_size); + if (array == NULL) { + ret = ENOMEM; + goto done; + } + + if (count == 0) { + array = NULL; + ret = EOK; + goto done; + } + break; + default: + if (count == 0) { + array = NULL; + ret = EOK; + goto done; + } + + array = talloc_zero_size(mem_ctx, (size_t)count * element_size); + if (array == NULL) { + ret = ENOMEM; + goto done; + } + break; + } + + arrayptr = array; + for (i = 0; i < count; i++) { + ret = sbus_iterator_read_basic(array, &subiter, dbus_type, arrayptr); + if (ret != EOK) { + talloc_free(array); + goto done; + } + + arrayptr += element_size; + } + + ret = EOK; + +done: + /* Always step past the array. */ + dbus_message_iter_next(iterator); + + if (ret != EOK) { + return ret; + } + + *_value_ptr = array; + + return ret; +} + +#define sbus_iterator_read_basic_array(mem_ctx, iterator, dbus_type, c_type, dest) \ + _sbus_iterator_read_basic_array((mem_ctx), (iterator), (dbus_type), \ + sizeof(c_type), (void**)(dest)) + +errno_t sbus_iterator_read_y(DBusMessageIter *iterator, + uint8_t *_value) +{ + return sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_BYTE, _value); +} + +errno_t sbus_iterator_read_b(DBusMessageIter *iterator, + bool *_value) +{ + dbus_bool_t dbus_value; + errno_t ret; + + ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_BOOLEAN, &dbus_value); + if (ret != EOK) { + return ret; + } + + *_value = dbus_value; + + return EOK; +} + +errno_t sbus_iterator_read_n(DBusMessageIter *iterator, + int16_t *_value) +{ + dbus_int16_t dbus_value; + errno_t ret; + + ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_INT16, &dbus_value); + if (ret != EOK) { + return ret; + } + + *_value = dbus_value; + + return EOK; +} + +errno_t sbus_iterator_read_q(DBusMessageIter *iterator, + uint16_t *_value) +{ + dbus_uint16_t dbus_value; + errno_t ret; + + ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_UINT16, &dbus_value); + if (ret != EOK) { + return ret; + } + + *_value = dbus_value; + + return EOK; +} + +errno_t sbus_iterator_read_i(DBusMessageIter *iterator, + int32_t *_value) +{ + dbus_int32_t dbus_value; + errno_t ret; + + ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_INT32, &dbus_value); + if (ret != EOK) { + return ret; + } + + *_value = dbus_value; + + return EOK; +} + +errno_t sbus_iterator_read_u(DBusMessageIter *iterator, + uint32_t *_value) +{ + dbus_uint32_t dbus_value; + errno_t ret; + + ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_UINT32, &dbus_value); + if (ret != EOK) { + return ret; + } + + *_value = dbus_value; + + return EOK; +} + +errno_t sbus_iterator_read_x(DBusMessageIter *iterator, + int64_t *_value) +{ + dbus_int64_t dbus_value; + errno_t ret; + + ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_INT64, &dbus_value); + if (ret != EOK) { + return ret; + } + + *_value = dbus_value; + + return EOK; +} + +errno_t sbus_iterator_read_t(DBusMessageIter *iterator, + uint64_t *_value) +{ + dbus_uint64_t dbus_value; + errno_t ret; + + ret = sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_UINT64, &dbus_value); + if (ret != EOK) { + return ret; + } + + *_value = dbus_value; + + return EOK; +} + +errno_t sbus_iterator_read_d(DBusMessageIter *iterator, + double *_value) +{ + return sbus_iterator_read_basic(NULL, iterator, DBUS_TYPE_DOUBLE, _value); +} + +errno_t sbus_iterator_read_s(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + const char **_value) +{ + return sbus_iterator_read_basic(mem_ctx, iterator, DBUS_TYPE_STRING, _value); +} + +errno_t sbus_iterator_read_S(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + char **_value) +{ + return sbus_iterator_read_basic(mem_ctx, iterator, DBUS_TYPE_STRING, _value); +} + +errno_t sbus_iterator_read_o(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + const char **_value) +{ + return sbus_iterator_read_basic(mem_ctx, iterator, DBUS_TYPE_OBJECT_PATH, _value); +} + +errno_t sbus_iterator_read_O(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + char **_value) +{ + return sbus_iterator_read_basic(mem_ctx, iterator, DBUS_TYPE_OBJECT_PATH, _value); +} + +errno_t sbus_iterator_read_ay(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + uint8_t **_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_BYTE, + uint8_t, _value); +} + +errno_t sbus_iterator_read_ab(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + bool **_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_BOOLEAN, + uint8_t, _value); +} + +errno_t sbus_iterator_read_an(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + int16_t **_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_INT16, + int16_t, _value); +} + +errno_t sbus_iterator_read_aq(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + uint16_t **_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_UINT16, + uint16_t, _value); +} + +errno_t sbus_iterator_read_ai(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + int32_t **_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_INT32, + int32_t, _value); +} + +errno_t sbus_iterator_read_au(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + uint32_t **_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_UINT32, + uint32_t, _value); +} + +errno_t sbus_iterator_read_ax(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + int64_t **_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_INT64, + int64_t, _value); +} + +errno_t sbus_iterator_read_at(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + uint64_t **_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_UINT64, + uint64_t, _value); +} + +errno_t sbus_iterator_read_ad(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + double **_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_DOUBLE, + double, _value); +} + +errno_t sbus_iterator_read_as(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + const char ***_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_STRING, + const char *, _value); +} + +errno_t sbus_iterator_read_aS(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + char ***_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_STRING, + char *, _value); +} + +errno_t sbus_iterator_read_ao(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + const char ***_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_OBJECT_PATH, + const char *, _value); +} + +errno_t sbus_iterator_read_aO(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + char ***_value) +{ + return sbus_iterator_read_basic_array(mem_ctx, iterator, + DBUS_TYPE_OBJECT_PATH, + char *, _value); +} diff --git a/src/sbus/interface/sbus_iterator_readers.h b/src/sbus/interface/sbus_iterator_readers.h new file mode 100644 index 0000000..6785243 --- /dev/null +++ b/src/sbus/interface/sbus_iterator_readers.h @@ -0,0 +1,126 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _SBUS_ITERATOR_READERS_H_ +#define _SBUS_ITERATOR_READERS_H_ + +#include <stdint.h> +#include <stdbool.h> +#include <talloc.h> +#include <dbus/dbus.h> + +#include "util/util.h" + +errno_t sbus_iterator_read_y(DBusMessageIter *iterator, + uint8_t *_value); + +errno_t sbus_iterator_read_b(DBusMessageIter *iterator, + bool *_value); + +errno_t sbus_iterator_read_n(DBusMessageIter *iterator, + int16_t *_value); + +errno_t sbus_iterator_read_q(DBusMessageIter *iterator, + uint16_t *_value); + +errno_t sbus_iterator_read_i(DBusMessageIter *iterator, + int32_t *_value); + +errno_t sbus_iterator_read_u(DBusMessageIter *iterator, + uint32_t *_value); + +errno_t sbus_iterator_read_x(DBusMessageIter *iterator, + int64_t *_value); + +errno_t sbus_iterator_read_t(DBusMessageIter *iterator, + uint64_t *_value); + +errno_t sbus_iterator_read_d(DBusMessageIter *iterator, + double *_value); + +errno_t sbus_iterator_read_s(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + const char **_value); + +errno_t sbus_iterator_read_S(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + char **_value); + +errno_t sbus_iterator_read_o(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + const char **_value); + +errno_t sbus_iterator_read_O(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + char **_value); + +errno_t sbus_iterator_read_ay(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + uint8_t **_value); + +errno_t sbus_iterator_read_ab(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + bool **_value); + +errno_t sbus_iterator_read_an(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + int16_t **_value); + +errno_t sbus_iterator_read_aq(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + uint16_t **_value); + +errno_t sbus_iterator_read_ai(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + int32_t **_value); + +errno_t sbus_iterator_read_au(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + uint32_t **_value); + +errno_t sbus_iterator_read_ax(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + int64_t **_value); + +errno_t sbus_iterator_read_at(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + uint64_t **_value); + +errno_t sbus_iterator_read_ad(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + double **_value); + +errno_t sbus_iterator_read_as(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + const char ***_value); + +errno_t sbus_iterator_read_aS(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + char ***_value); + +errno_t sbus_iterator_read_ao(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + const char ***_value); + +errno_t sbus_iterator_read_aO(TALLOC_CTX *mem_ctx, + DBusMessageIter *iterator, + char ***_value); + +#endif /* _SBUS_ITERATOR_READERS_H_ */ diff --git a/src/sbus/interface/sbus_iterator_writers.c b/src/sbus/interface/sbus_iterator_writers.c new file mode 100644 index 0000000..3becc8f --- /dev/null +++ b/src/sbus/interface/sbus_iterator_writers.c @@ -0,0 +1,365 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <talloc.h> +#include <dbus/dbus.h> + +#include "util/util.h" +#include "util/sss_utf8.h" +#include "sbus/interface/sbus_iterator_writers.h" + +static errno_t +sbus_iterator_write_basic(DBusMessageIter *iterator, + int dbus_type, + void *value_ptr) +{ + dbus_bool_t ret; + + ret = dbus_message_iter_append_basic(iterator, dbus_type, value_ptr); + + return ret ? EOK : EIO; +} + +static errno_t +sbus_iterator_write_string(DBusMessageIter *iterator, + int dbus_type, + const char *value, + const char *default_value) +{ + dbus_bool_t ret; + bool is_valid; + + /* If the value is not set, we will provide a correct default value. */ + value = value == NULL ? default_value : value; + + /* D-Bus is not capable of sending NULL string. If even the default value + * was not set, we return an error. */ + if (value == NULL) { + return ERR_SBUS_EMPTY_STRING; + } + + /* D-Bus can send only correct UTF-8 strings. */ + is_valid = sss_utf8_check((const uint8_t *)value, strlen(value)); + if (!is_valid) { + DEBUG(SSSDBG_CRIT_FAILURE, "String with non-utf8 characters was " + "given [%s]\n", value); + return ERR_SBUS_INVALID_STRING; + } + + ret = dbus_message_iter_append_basic(iterator, dbus_type, &value); + + return ret ? EOK : EIO; +} + +static errno_t +sbus_iterator_write_string_elements(DBusMessageIter *iterator, + int dbus_type, + const char **values) +{ + errno_t ret; + int i; + + if (values == NULL) { + return EOK; + } + + /* String arrays are NULL-terminated. */ + for (i = 0; values[i] != NULL; i++) { + ret = sbus_iterator_write_string(iterator, dbus_type, values[i], NULL); + if (ret != EOK) { + return ret; + } + } + + return EOK; +} + +static errno_t +sbus_iterator_write_fixed_elements(DBusMessageIter *iterator, + int dbus_type, + int element_size, + int array_length, + void *value_ptr) +{ + errno_t ret; + uint8_t *element_ptr; + int count; + int i; + + element_ptr = value_ptr; + if (array_length < 0) { + count = talloc_get_size(value_ptr) / element_size; + } else { + count = array_length; + } + + + for (i = 0; i < count; i++) { + ret = sbus_iterator_write_basic(iterator, dbus_type, element_ptr); + if (ret != EOK) { + return ret; + } + + element_ptr += element_size; + } + + return EOK; +} + +errno_t +_sbus_iterator_write_basic_array(DBusMessageIter *iterator, + int dbus_type, + int element_size, + int array_length, + void *value_ptr) +{ + const char array_type[2] = {dbus_type, '\0'}; + DBusMessageIter arrayiter; + dbus_bool_t dbret; + errno_t ret; + + dbret = dbus_message_iter_open_container(iterator, DBUS_TYPE_ARRAY, + array_type, &arrayiter); + if (!dbret) { + return EIO; + } + + switch (dbus_type) { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + ret = sbus_iterator_write_string_elements(&arrayiter, dbus_type, + (const char **)value_ptr); + if (ret != EOK) { + goto done; + } + break; + default: + ret = sbus_iterator_write_fixed_elements(&arrayiter, dbus_type, + element_size, array_length, + value_ptr); + if (ret != EOK) { + goto done; + } + break; + } + + dbret = dbus_message_iter_close_container(iterator, &arrayiter); + if (!dbret) { + ret = EIO; + goto done; + } + + ret = EOK; + +done: + if (ret != EOK) { + dbus_message_iter_abandon_container(iterator, &arrayiter); + } + + return ret; +} + +errno_t sbus_iterator_write_y(DBusMessageIter *iterator, + uint8_t value) +{ + return sbus_iterator_write_basic(iterator, DBUS_TYPE_BYTE, &value); +} + +errno_t sbus_iterator_write_b(DBusMessageIter *iterator, + bool value) +{ + dbus_bool_t dbus_value = value; + + return sbus_iterator_write_basic(iterator, DBUS_TYPE_BOOLEAN, &dbus_value); +} + +errno_t sbus_iterator_write_n(DBusMessageIter *iterator, + int16_t value) +{ + dbus_int16_t dbus_value = value; + + return sbus_iterator_write_basic(iterator, DBUS_TYPE_INT16, &dbus_value); +} + +errno_t sbus_iterator_write_q(DBusMessageIter *iterator, + uint16_t value) +{ + dbus_uint16_t dbus_value = value; + + return sbus_iterator_write_basic(iterator, DBUS_TYPE_UINT16, &dbus_value); +} + +errno_t sbus_iterator_write_i(DBusMessageIter *iterator, + int32_t value) +{ + dbus_int32_t dbus_value = value; + + return sbus_iterator_write_basic(iterator, DBUS_TYPE_INT32, &dbus_value); +} + +errno_t sbus_iterator_write_u(DBusMessageIter *iterator, + uint32_t value) +{ + dbus_uint32_t dbus_value = value; + + return sbus_iterator_write_basic(iterator, DBUS_TYPE_UINT32, &dbus_value); +} + +errno_t sbus_iterator_write_x(DBusMessageIter *iterator, + int64_t value) +{ + dbus_int64_t dbus_value = value; + + return sbus_iterator_write_basic(iterator, DBUS_TYPE_INT64, &dbus_value); +} + +errno_t sbus_iterator_write_t(DBusMessageIter *iterator, + uint64_t value) +{ + dbus_uint64_t dbus_value = value; + + return sbus_iterator_write_basic(iterator, DBUS_TYPE_UINT64, &dbus_value); +} + +errno_t sbus_iterator_write_d(DBusMessageIter *iterator, + double value) +{ + return sbus_iterator_write_basic(iterator, DBUS_TYPE_DOUBLE, &value); +} + +errno_t sbus_iterator_write_s(DBusMessageIter *iterator, + const char *value) +{ + return sbus_iterator_write_string(iterator, DBUS_TYPE_STRING, value, ""); +} + +errno_t sbus_iterator_write_S(DBusMessageIter *iterator, + char *value) +{ + return sbus_iterator_write_string(iterator, DBUS_TYPE_STRING, value, ""); +} + +errno_t sbus_iterator_write_o(DBusMessageIter *iterator, + const char *value) +{ + return sbus_iterator_write_string(iterator, DBUS_TYPE_OBJECT_PATH, + value, "/"); +} + +errno_t sbus_iterator_write_O(DBusMessageIter *iterator, + char *value) +{ + return sbus_iterator_write_string(iterator, DBUS_TYPE_OBJECT_PATH, + value, "/"); +} + +errno_t sbus_iterator_write_ay(DBusMessageIter *iterator, + uint8_t *value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_BYTE, + uint8_t, value); +} + +errno_t sbus_iterator_write_ab(DBusMessageIter *iterator, + bool *value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_BOOLEAN, + bool, value); +} + +errno_t sbus_iterator_write_an(DBusMessageIter *iterator, + int16_t *value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_INT16, + int16_t, value); +} + +errno_t sbus_iterator_write_aq(DBusMessageIter *iterator, + uint16_t *value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_UINT16, + uint16_t, value); +} + +errno_t sbus_iterator_write_ai(DBusMessageIter *iterator, + int32_t *value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_INT32, + int32_t, value); +} + +errno_t sbus_iterator_write_au(DBusMessageIter *iterator, + uint32_t *value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_UINT32, + uint32_t, value); +} + +errno_t sbus_iterator_write_ax(DBusMessageIter *iterator, + int64_t *value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_INT64, + int64_t, value); +} + +errno_t sbus_iterator_write_at(DBusMessageIter *iterator, + uint64_t *value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_UINT64, + uint64_t, value); +} + +errno_t sbus_iterator_write_ad(DBusMessageIter *iterator, + double *value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_DOUBLE, + double, value); +} + +errno_t sbus_iterator_write_as(DBusMessageIter *iterator, + const char **value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_STRING, + const char *, value); +} + +errno_t sbus_iterator_write_aS(DBusMessageIter *iterator, + char **value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_STRING, + char *, value); +} + +errno_t sbus_iterator_write_ao(DBusMessageIter *iterator, + const char **value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_OBJECT_PATH, + const char *, value); +} + +errno_t sbus_iterator_write_aO(DBusMessageIter *iterator, + char **value) +{ + return sbus_iterator_write_basic_array(iterator, DBUS_TYPE_OBJECT_PATH, + char *, value); +} diff --git a/src/sbus/interface/sbus_iterator_writers.h b/src/sbus/interface/sbus_iterator_writers.h new file mode 100644 index 0000000..ec0662e --- /dev/null +++ b/src/sbus/interface/sbus_iterator_writers.h @@ -0,0 +1,126 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _SBUS_ITERATOR_WRITERS_H_ +#define _SBUS_ITERATOR_WRITERS_H_ + +#include <stdint.h> +#include <dbus/dbus.h> + +#include "util/util.h" + +/* Generic writers to be used in custom type handlers. */ + +errno_t +_sbus_iterator_write_basic_array(DBusMessageIter *iterator, + int dbus_type, + int element_size, + int array_length, + void *value_ptr); + +#define sbus_iterator_write_basic_array(iterator, dbus_type, c_type, source) \ + _sbus_iterator_write_basic_array((iterator), (dbus_type), \ + sizeof(c_type), -1, (void*)(source)) + +#define sbus_iterator_write_basic_array_len(iterator, dbus_type, c_type, source, length) \ + _sbus_iterator_write_basic_array((iterator), (dbus_type), \ + sizeof(c_type), (length), (void*)(source)) + +/* Basic types. */ + +errno_t sbus_iterator_write_y(DBusMessageIter *iterator, + uint8_t value); + +errno_t sbus_iterator_write_b(DBusMessageIter *iterator, + bool value); + +errno_t sbus_iterator_write_n(DBusMessageIter *iterator, + int16_t value); + +errno_t sbus_iterator_write_q(DBusMessageIter *iterator, + uint16_t value); + +errno_t sbus_iterator_write_i(DBusMessageIter *iterator, + int32_t value); + +errno_t sbus_iterator_write_u(DBusMessageIter *iterator, + uint32_t value); + +errno_t sbus_iterator_write_x(DBusMessageIter *iterator, + int64_t value); + +errno_t sbus_iterator_write_t(DBusMessageIter *iterator, + uint64_t value); + +errno_t sbus_iterator_write_d(DBusMessageIter *iterator, + double value); + +errno_t sbus_iterator_write_s(DBusMessageIter *iterator, + const char *value); + +errno_t sbus_iterator_write_S(DBusMessageIter *iterator, + char *value); + +errno_t sbus_iterator_write_o(DBusMessageIter *iterator, + const char *value); + +errno_t sbus_iterator_write_O(DBusMessageIter *iterator, + char *value); + +errno_t sbus_iterator_write_ay(DBusMessageIter *iterator, + uint8_t *value); + +errno_t sbus_iterator_write_ab(DBusMessageIter *iterator, + bool *value); + +errno_t sbus_iterator_write_an(DBusMessageIter *iterator, + int16_t *value); + +errno_t sbus_iterator_write_aq(DBusMessageIter *iterator, + uint16_t *value); + +errno_t sbus_iterator_write_ai(DBusMessageIter *iterator, + int32_t *value); + +errno_t sbus_iterator_write_au(DBusMessageIter *iterator, + uint32_t *value); + +errno_t sbus_iterator_write_ax(DBusMessageIter *iterator, + int64_t *value); + +errno_t sbus_iterator_write_at(DBusMessageIter *iterator, + uint64_t *value); + +errno_t sbus_iterator_write_ad(DBusMessageIter *iterator, + double *value); + +errno_t sbus_iterator_write_as(DBusMessageIter *iterator, + const char **value); + +errno_t sbus_iterator_write_aS(DBusMessageIter *iterator, + char **value); + +errno_t sbus_iterator_write_ao(DBusMessageIter *iterator, + const char **value); + +errno_t sbus_iterator_write_aO(DBusMessageIter *iterator, + char **value); + +#endif /* _SBUS_ITERATOR_WRITERS_H_ */ diff --git a/src/sbus/interface/sbus_properties.c b/src/sbus/interface/sbus_properties.c new file mode 100644 index 0000000..8be933c --- /dev/null +++ b/src/sbus/interface/sbus_properties.c @@ -0,0 +1,894 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <errno.h> +#include <string.h> +#include <dbus/dbus.h> + +#include "util/util.h" +#include "sbus/sbus_request.h" +#include "sbus/sbus_private.h" +#include "sbus/interface/sbus_iterator_readers.h" +#include "sbus/interface/sbus_iterator_writers.h" +#include "sbus/interface_dbus/sbus_dbus_server.h" + +static errno_t +sbus_open_variant(DBusMessageIter *parent, + DBusMessageIter *sub, + const char *type) +{ + dbus_bool_t dbret; + + dbret = dbus_message_iter_open_container(parent, DBUS_TYPE_VARIANT, + type, sub); + if (!dbret) { + return ENOMEM; + } + + return EOK; +} + +static errno_t +sbus_open_dict(DBusMessageIter *parent, + DBusMessageIter *sub) +{ + dbus_bool_t dbret; + + dbret = dbus_message_iter_open_container(parent, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + sub); + if (!dbret) { + return ENOMEM; + } + + return EOK; +} + +static errno_t +sbus_open_dict_entry(DBusMessageIter *parent, + DBusMessageIter *sub) +{ + dbus_bool_t dbret; + + dbret = dbus_message_iter_open_container(parent, DBUS_TYPE_DICT_ENTRY, + NULL, sub); + if (!dbret) { + return ENOMEM; + } + + return EOK; +} + +static errno_t +sbus_close_iterator(DBusMessageIter *parent, + DBusMessageIter *sub) +{ + dbus_bool_t dbret; + + dbret = dbus_message_iter_close_container(parent, sub); + if (!dbret) { + return EIO; + } + + return EOK; +} + +static errno_t +sbus_create_dummy_message(TALLOC_CTX *mem_ctx, + DBusMessage **_msg, + DBusMessageIter *_write_iter) +{ + DBusMessage *msg; + errno_t ret; + + msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL); + if (msg == NULL) { + return ENOMEM; + } + + /* Set fake serial number for reply. */ + dbus_message_set_serial(msg, 1); + + ret = sbus_message_bound(mem_ctx, msg); + if (ret != EOK) { + dbus_message_unref(msg); + return ret; + } + + dbus_message_iter_init_append(msg, _write_iter); + + *_msg = msg; + + return EOK; +} + +static errno_t +sbus_copy_iterator_value(DBusMessageIter *from, + DBusMessageIter *to); + +static errno_t +sbus_copy_iterator_fixed_array(DBusMessageIter *from, + DBusMessageIter *to, + int type) +{ + DBusMessageIter from_sub; + DBusMessageIter to_sub; + dbus_bool_t dbret; + const char *typestr; + void *fixed; + int count; + + typestr = dbus_message_type_to_string(type); + if (typestr == NULL) { + return ERR_INTERNAL; + } + + dbret = dbus_message_iter_open_container(to, DBUS_TYPE_ARRAY, + typestr, &to_sub); + if (!dbret) { + return EIO; + } + + dbus_message_iter_recurse(from, &from_sub); + dbus_message_iter_get_fixed_array(&from_sub, &fixed, &count); + + dbret = dbus_message_iter_append_fixed_array(&to_sub, type, &fixed, count); + if (!dbret) { + goto fail; + } + + dbret = dbus_message_iter_close_container(to, &to_sub); + if (!dbret) { + goto fail; + } + + return EOK; + +fail: + dbus_message_iter_abandon_container(to, &to_sub); + return EIO; +} + +static errno_t +sbus_copy_iterator_container(DBusMessageIter *from, + DBusMessageIter *to, + int type) +{ + DBusMessageIter from_sub; + DBusMessageIter to_sub; + const char *signature; + dbus_bool_t dbret; + errno_t ret; + + dbus_message_iter_recurse(from, &from_sub); + + if (type == DBUS_TYPE_DICT_ENTRY) { + /* This is a special case. Dictionary entries do not have any specific + * signature when we open their container. */ + signature = NULL; + } else { + signature = dbus_message_iter_get_signature(&from_sub); + if (signature == NULL) { + ret = ENOMEM; + goto done; + } + } + + dbret = dbus_message_iter_open_container(to, type, signature, &to_sub); + if (!dbret) { + return EIO; + } + + ret = sbus_copy_iterator_value(&from_sub, &to_sub); + if (ret != EOK) { + ret = EIO; + goto done; + } + + dbret = dbus_message_iter_close_container(to, &to_sub); + if (!dbret) { + ret = EIO; + goto done; + } + + ret = EOK; + +done: + if (ret != EOK) { + dbus_message_iter_abandon_container(to, &to_sub); + } + + return ret; +} + +static errno_t +sbus_copy_iterator_value(DBusMessageIter *from, + DBusMessageIter *to) +{ + void *basic; + dbus_bool_t dbret; + int element_type; + int type; + errno_t ret; + + do { + type = dbus_message_iter_get_arg_type(from); + + if (type == DBUS_TYPE_INVALID) { + /* We have reached the end of the message. */ + return EOK; + } + + /* If this is a basic type, we just write it to its destination. */ + if (dbus_type_is_basic(type)) { + dbus_message_iter_get_basic(from, &basic); + dbret = dbus_message_iter_append_basic(to, type, &basic); + if (!dbret) { + return EIO; + } + + continue; + } + + if (type == DBUS_TYPE_ARRAY) { + element_type = dbus_message_iter_get_element_type(from); + + /* Fixed types can be copied at once. Otherwise we treat it + * as any other container. */ + if (dbus_type_is_fixed(element_type)) { + ret = sbus_copy_iterator_fixed_array(from, to, element_type); + if (ret != EOK) { + return ret; + } + + continue; + } + } + + /* If this is a container, we need to descend into it and open + * this container in the destination iterator. */ + if (dbus_type_is_container(type)) { + ret = sbus_copy_iterator_container(from, to, type); + if (ret != EOK) { + return ret; + } + + continue; + } + + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type [%d]\n", type); + return ERR_INTERNAL; + } while (dbus_message_iter_next(from)); + + return EOK; +} + +static errno_t +sbus_copy_message_to_dictionary(const char *name, + DBusMessage *msg, + DBusMessageIter *to) +{ + DBusMessageIter entry; + DBusMessageIter from; + dbus_bool_t dbret; + errno_t ret; + + /* Open dictionary entry iterator. */ + ret = sbus_open_dict_entry(to, &entry); + if (ret != EOK) { + return ret; + } + + /* Append property name as key. */ + ret = sbus_iterator_write_s(&entry, name); + if (ret != EOK) { + goto done; + } + + /* Open message iterator for reading. */ + dbret = dbus_message_iter_init(msg, &from); + if (!dbret) { + ret = ENOMEM; + goto done; + } + + ret = sbus_copy_iterator_value(&from, &entry); + if (ret != EOK) { + goto done; + } + + ret = sbus_close_iterator(to, &entry); + +done: + if (ret != EOK) { + dbus_message_iter_abandon_container(to, &entry); + } + + return ret; +} + +static errno_t +sbus_request_property(TALLOC_CTX *mem_ctx, + struct sbus_connection *conn, + struct sbus_router *router, + const struct sbus_sender *sender, + enum sbus_property_access access, + const char *destination, + const char *path, + const char *interface_name, + const char *property_name, + struct sbus_request **_sbus_req, + const struct sbus_property **_property) +{ + const struct sbus_property *property; + struct sbus_request *sbus_req; + struct sbus_interface *iface; + enum sbus_request_type type; + + iface = sbus_router_paths_lookup(router->paths, path, interface_name); + if (iface == NULL) { + return ERR_SBUS_UNKNOWN_INTERFACE; + } + + property = sbus_interface_find_property(iface, access, property_name); + if (property == NULL) { + return ERR_SBUS_UNKNOWN_PROPERTY; + } + + switch (access) { + case SBUS_PROPERTY_READABLE: + type = SBUS_REQUEST_PROPERTY_GET; + break; + case SBUS_PROPERTY_WRITABLE: + type = SBUS_REQUEST_PROPERTY_SET; + break; + default: + return EINVAL; + } + + sbus_req = sbus_request_create(mem_ctx, conn, type, destination, + interface_name, property_name, path); + if (sbus_req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create request data!\n"); + return ENOMEM; + } + + sbus_req->sender = sbus_sender_copy(sbus_req, sender); + if (sbus_req->sender == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to copy sender data!\n"); + talloc_free(sbus_req); + return ENOMEM; + } + + *_sbus_req = sbus_req; + *_property = property; + + return EOK; +} + +struct sbus_properties_get_state { + struct { + DBusMessageIter *root; + DBusMessageIter variant; + } iter; +}; + +static void sbus_properties_get_done(struct tevent_req *subreq); + +static struct tevent_req * +sbus_properties_get_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct sbus_router *router, + const char *interface_name, + const char *property_name, + DBusMessageIter *write_iterator) +{ + struct sbus_properties_get_state *state; + const struct sbus_property *property; + struct sbus_request *property_req; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + DEBUG(SSSDBG_TRACE_ALL, "Requesting property: %s.%s of %s\n", + interface_name, property_name, sbus_req->path); + + req = tevent_req_create(mem_ctx, &state, struct sbus_properties_get_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + ret = sbus_request_property(state, sbus_req->conn, router, sbus_req->sender, + SBUS_PROPERTY_READABLE, sbus_req->destination, + sbus_req->path, interface_name, property_name, + &property_req, &property); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot request property %s.%s [%d]: %s\n", + interface_name, property_name, ret, sss_strerror(ret)); + goto done; + } + + ret = sbus_check_access(router->conn, property_req); + if (ret != EOK) { + goto done; + } + + state->iter.root = write_iterator; + ret = sbus_open_variant(state->iter.root, &state->iter.variant, + property->type); + if (ret != EOK) { + goto done; + } + + subreq = property->invoker.issue(state, ev, property_req, + NULL, /* no keygen */ + &property->handler, + NULL, /* no read iterator*/ + &state->iter.variant, + NULL /* no key */); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sbus_properties_get_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void +sbus_properties_get_done(struct tevent_req *subreq) +{ + struct sbus_properties_get_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sbus_properties_get_state); + + ret = sbus_invoker_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + dbus_message_iter_abandon_container(state->iter.root, + &state->iter.variant); + goto done; + } + + ret = sbus_close_iterator(state->iter.root, &state->iter.variant); + +done: + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t +sbus_properties_get_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +struct sbus_properties_getall_state { + struct tevent_context *ev; + struct sbus_router *router; + struct sbus_request *sbus_req; + const char *interface_name; + + struct { + DBusMessageIter *root; + DBusMessageIter dict; + DBusMessageIter entry; + } iter; + + struct { + DBusMessage *msg; + DBusMessageIter write_iter; + } dummy; + + const struct sbus_property *properties; + struct { + const struct sbus_property *current; + size_t index; + } property; +}; + +static errno_t sdap_properties_getall_next(struct tevent_req *req); +static void sbus_properties_getall_done(struct tevent_req *subreq); + +static struct tevent_req * +sbus_properties_getall_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct sbus_router *router, + const char *interface_name, + DBusMessageIter *write_iterator) +{ + struct sbus_properties_getall_state *state; + struct sbus_interface *iface; + struct tevent_req *req; + errno_t ret; + + DEBUG(SSSDBG_TRACE_ALL, "Requesting all properties: %s of %s\n", + interface_name, sbus_req->path); + + req = tevent_req_create(mem_ctx, &state, + struct sbus_properties_getall_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + iface = sbus_router_paths_lookup(router->paths, sbus_req->path, + interface_name); + if (iface == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Unknown interface: %s\n", interface_name); + ret = ERR_SBUS_UNKNOWN_INTERFACE; + goto done; + } + + state->ev = ev; + state->router = router; + state->sbus_req = sbus_req; + state->interface_name = interface_name; + state->properties = iface->properties; + state->iter.root = write_iterator; + + /* Open array of <key, value> pairs. */ + ret = sbus_open_dict(state->iter.root, &state->iter.dict); + if (ret != EOK) { + goto done; + } + + ret = sdap_properties_getall_next(req); + if (ret == EOK) { + /* There were no properties to return, we must close the container + * so an empty result is sent back to the caller. */ + ret = sbus_close_iterator(state->iter.root, &state->iter.dict); + goto done; + } else if (ret != EAGAIN) { + dbus_message_iter_abandon_container(state->iter.root, + &state->iter.dict); + } + + ret = EAGAIN; + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static errno_t +sdap_properties_getall_next(struct tevent_req *req) +{ + struct sbus_properties_getall_state *state; + const struct sbus_property *property; + struct tevent_req *subreq; + errno_t ret; + + state = tevent_req_data(req, struct sbus_properties_getall_state); + + /* There are no properties available. */ + if (state->properties == NULL) { + return EOK; + } + + do { + property = &state->properties[state->property.index]; + state->property.current = property; + state->property.index++; + + /* There are no more properties available. */ + if (property->name == NULL) { + return EOK; + } + + /* We are interested only in readable properties. */ + } while (property->access != SBUS_PROPERTY_READABLE); + + /* Create new message that we will use to fake an Get method request. + * We will then copy its reply to the GetAll dictionary. */ + ret = sbus_create_dummy_message(state, &state->dummy.msg, + &state->dummy.write_iter); + if (ret != EOK) { + return ret; + } + + subreq = sbus_properties_get_send(state, state->ev, state->sbus_req, + state->router, state->interface_name, + property->name, &state->dummy.write_iter); + if (subreq == NULL) { + return ENOMEM; + } + + tevent_req_set_callback(subreq, sbus_properties_getall_done, req); + + return EAGAIN; +} + +static void sbus_properties_getall_done(struct tevent_req *subreq) +{ + struct sbus_properties_getall_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sbus_properties_getall_state); + + ret = sbus_properties_get_recv(state, subreq); + talloc_zfree(subreq); + switch (ret) { + case EOK: + ret = sbus_copy_message_to_dictionary(state->property.current->name, + state->dummy.msg, + &state->iter.dict); + if (ret != EOK) { + goto done; + } + break; + case ENOENT: + case EACCES: + case EPERM: + /* These errors are not fatal. We will just skip this property. */ + DEBUG(SSSDBG_TRACE_FUNC, "Unable to get property %s.%s [%d]: %s\n", + state->interface_name, state->property.current->name, + ret, sss_strerror(ret)); + break; + default: + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to get property %s.%s [%d]: %s\n", + state->interface_name, state->property.current->name, + ret, sss_strerror(ret)); + goto done; + } + + dbus_message_unref(state->dummy.msg); + ret = sdap_properties_getall_next(req); + if (ret == EAGAIN) { + /* Continue with next property. */ + return; + } else if (ret != EOK) { + goto done; + } + + ret = sbus_close_iterator(state->iter.root, &state->iter.dict); + if (ret != EOK) { + goto done; + } + +done: + if (ret != EOK) { + dbus_message_iter_abandon_container(state->iter.root, + &state->iter.dict); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t +sbus_properties_getall_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +static errno_t +sbus_properties_set_parse(TALLOC_CTX *mem_ctx, + DBusMessageIter *read_iter, + const char **_interface_name, + const char **_property_name) +{ + const char *interface_name; + const char *property_name; + errno_t ret; + + ret = sbus_iterator_read_s(mem_ctx, read_iter, &interface_name); + if (ret != EOK) { + return ret; + } + + ret = sbus_iterator_read_s(mem_ctx, read_iter, &property_name); + if (ret != EOK) { + return ret; + } + + *_interface_name = interface_name; + *_property_name = property_name; + + return EOK; +} + +struct sbus_properties_set_state { + DBusMessageIter variant_iterator; +}; + +static void sbus_properties_set_done(struct tevent_req *subreq); + +static struct tevent_req * +sbus_properties_set_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sbus_request *sbus_req, + struct sbus_router *router, + DBusMessageIter *read_iterator) +{ + struct sbus_properties_set_state *state; + const struct sbus_property *property; + struct sbus_request *property_req; + struct tevent_req *subreq; + struct tevent_req *req; + const char *interface_name; + const char *property_name; + char *signature; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct sbus_properties_set_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + ret = sbus_properties_set_parse(state, read_iterator, &interface_name, + &property_name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Unable to parse input message [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + + DEBUG(SSSDBG_TRACE_ALL, "Setting property: %s.%s of %s\n", + interface_name, property_name, sbus_req->path); + + ret = sbus_request_property(state, sbus_req->conn, router, sbus_req->sender, + SBUS_PROPERTY_WRITABLE, sbus_req->destination, + sbus_req->path, interface_name, property_name, + &property_req, &property); + if (ret != EOK) { + goto done; + } + + ret = sbus_check_access(router->conn, property_req); + if (ret != EOK) { + goto done; + } + + if (dbus_message_iter_get_arg_type(read_iterator) != DBUS_TYPE_VARIANT) { + DEBUG(SSSDBG_CRIT_FAILURE, "Setter argument is not inside variant!\n"); + ret = ERR_SBUS_INVALID_TYPE; + goto done; + } + + /* Recurse into variant to get iterator for new property value. */ + dbus_message_iter_recurse(read_iterator, &state->variant_iterator); + signature = dbus_message_iter_get_signature(&state->variant_iterator); + if (strcmp(property->type, signature) != 0) { + ret = EINVAL; + goto done; + } + + subreq = property->invoker.issue(state, ev, property_req, + NULL, /* no keygen */ + &property->handler, + &state->variant_iterator, + NULL, /* no write iterator*/ + NULL /* no key */); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, sbus_properties_set_done, req); + + ret = EAGAIN; + +done: + if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void +sbus_properties_set_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + + ret = sbus_invoker_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t +sbus_properties_set_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +errno_t +sbus_register_properties(struct sbus_router *router) +{ + + SBUS_INTERFACE(iface, + org_freedesktop_DBus_Properties, + SBUS_METHODS( + SBUS_ASYNC(METHOD, org_freedesktop_DBus_Properties, Get, + sbus_properties_get_send, sbus_properties_get_recv, + router), + SBUS_ASYNC(METHOD, org_freedesktop_DBus_Properties, Set, + sbus_properties_set_send, sbus_properties_set_recv, + router), + SBUS_ASYNC(METHOD, org_freedesktop_DBus_Properties, GetAll, + sbus_properties_getall_send, sbus_properties_getall_recv, + router) + ), + SBUS_WITHOUT_SIGNALS, + SBUS_WITHOUT_PROPERTIES + ); + + struct sbus_path paths[] = { + {"/", &iface}, + {"/*", &iface}, + {NULL, NULL} + }; + + return sbus_router_add_path_map(router, paths); +} diff --git a/src/sbus/interface/sbus_properties_parser.c b/src/sbus/interface/sbus_properties_parser.c new file mode 100644 index 0000000..2a94493 --- /dev/null +++ b/src/sbus/interface/sbus_properties_parser.c @@ -0,0 +1,198 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> +#include <talloc.h> + +#include "sbus/sbus_private.h" +#include "sbus/interface/sbus_iterator_readers.h" +#include "sbus/interface/sbus_iterator_writers.h" + +static errno_t +sbus_parse_get_value(TALLOC_CTX *mem_ctx, + sbus_value_reader_fn reader, + sbus_value_reader_talloc_fn reader_talloc, + DBusMessageIter *iter, + void *_value_ptr) +{ + DBusMessageIter variant; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) { + return ERR_SBUS_INVALID_TYPE; + } + + dbus_message_iter_recurse(iter, &variant); + + if (reader != NULL) { + return reader(&variant, _value_ptr); + } + + return reader_talloc(mem_ctx, &variant, _value_ptr); +} + +errno_t +sbus_parse_get_message(TALLOC_CTX *mem_ctx, + sbus_value_reader_fn reader, + sbus_value_reader_talloc_fn reader_talloc, + DBusMessage *msg, + void *_value_ptr) +{ + DBusMessageIter iterator; + + dbus_message_iter_init(msg, &iterator); + + return sbus_parse_get_value(mem_ctx, reader, reader_talloc, + &iterator, _value_ptr); +} + +static errno_t +sbus_parse_getall_name(struct sbus_parse_getall_table *table, + DBusMessageIter *dict_iter, + struct sbus_parse_getall_table **_property) +{ + const char *name; + int type; + int i; + + type = dbus_message_iter_get_arg_type(dict_iter); + if (type != DBUS_TYPE_STRING) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type [%d]\n", type); + return ERR_SBUS_INVALID_TYPE; + } + + dbus_message_iter_get_basic(dict_iter, &name); + + for (i = 0; table[i].name != NULL; i++) { + if (strcmp(table[i].name, name) == 0) { + *_property = &table[i]; + return EOK; + } + } + + DEBUG(SSSDBG_MINOR_FAILURE, "Unknown property [%s], skipping...\n", name); + *_property = NULL; + + return EOK; +} + +static errno_t +sbus_parse_getall_dict_entry(TALLOC_CTX *mem_ctx, + struct sbus_parse_getall_table *table, + DBusMessageIter *dict_iter) +{ + struct sbus_parse_getall_table *property; + dbus_bool_t dbret; + errno_t ret; + + ret = sbus_parse_getall_name(table, dict_iter, &property); + if (ret != EOK) { + return ret; + } + + dbret = dbus_message_iter_next(dict_iter); + if (!dbret) { + return ERR_SBUS_INVALID_TYPE; + } + + if (property == NULL) { + return EOK; + } + + ret = sbus_parse_get_value(mem_ctx, property->reader, + property->reader_talloc, dict_iter, + property->destination); + if (ret != EOK) { + return ret; + } + + *(property->is_set) = true; + + return EOK; +} + +static errno_t +sbus_parse_getall_array(TALLOC_CTX *mem_ctx, + struct sbus_parse_getall_table *table, + DBusMessageIter *array_iter) +{ + DBusMessageIter dict_iter; + errno_t ret; + int type; + + do { + type = dbus_message_iter_get_arg_type(array_iter); + + switch (type) { + case DBUS_TYPE_INVALID: + /* We have reached the end of the array. */ + return EOK; + case DBUS_TYPE_DICT_ENTRY: + dbus_message_iter_recurse(array_iter, &dict_iter); + ret = sbus_parse_getall_dict_entry(mem_ctx, table, &dict_iter); + if (ret != EOK) { + return ret; + } + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type [%d]\n", type); + return ERR_SBUS_INVALID_TYPE; + } + } while (dbus_message_iter_next(array_iter)); + + return EOK; +} + +errno_t +sbus_parse_getall_message(TALLOC_CTX *mem_ctx, + struct sbus_parse_getall_table *table, + DBusMessage *msg) +{ + DBusMessageIter array_iter; + DBusMessageIter iter; + errno_t ret; + int type; + + dbus_message_iter_init(msg, &iter); + + type = dbus_message_iter_get_arg_type(&iter); + + switch (type) { + case DBUS_TYPE_INVALID: + /* Empty message. */ + return EOK; + case DBUS_TYPE_ARRAY: + dbus_message_iter_recurse(&iter, &array_iter); + ret = sbus_parse_getall_array(mem_ctx, table, &array_iter); + if (ret != EOK) { + return ret; + } + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type [%d]\n", type); + return ERR_SBUS_INVALID_TYPE; + } + + if (dbus_message_iter_has_next(&iter)) { + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid GetAll reply\n"); + return ERR_SBUS_INVALID_TYPE; + } + + return EOK; +} diff --git a/src/sbus/interface/sbus_std_signals.c b/src/sbus/interface/sbus_std_signals.c new file mode 100644 index 0000000..c9afe44 --- /dev/null +++ b/src/sbus/interface/sbus_std_signals.c @@ -0,0 +1,65 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2017 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "util/util.h" +#include "sbus/sbus_request.h" +#include "sbus/sbus_private.h" +#include "sbus/interface_dbus/sbus_dbus_server.h" + +static errno_t +sbus_name_owner_changed(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct sbus_connection *conn, + const char *name, + const char *new_owner, + const char *old_owner) +{ + DEBUG(SSSDBG_TRACE_ALL, "Name of owner %s has changed from " + "[%s] to [%s]\n", name, old_owner, new_owner); + + /* Delete any existing sender information since it is now obsolete. */ + sbus_senders_delete(conn->senders, name); + + return EOK; +} + +static errno_t +sbus_name_acquired(TALLOC_CTX *mem_ctx, + struct sbus_request *sbus_req, + struct sbus_connection *conn, + const char *name) +{ + DEBUG(SSSDBG_TRACE_FUNC, "D-Bus name acquired: %s\n", name); + + return EOK; +} + +errno_t +sbus_register_standard_signals(struct sbus_connection *conn) +{ + struct sbus_listener listeners[] = SBUS_LISTENERS( + SBUS_LISTEN_SYNC(org_freedesktop_DBus, NameOwnerChanged, + DBUS_PATH_DBUS, sbus_name_owner_changed, conn), + SBUS_LISTEN_SYNC(org_freedesktop_DBus, NameAcquired, + DBUS_PATH_DBUS, sbus_name_acquired, conn) + ); + + return sbus_router_listen_map(conn, listeners); +} |