summaryrefslogtreecommitdiffstats
path: root/src/sbus/request
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/sbus/request
parentInitial commit. (diff)
downloadsssd-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/request')
-rw-r--r--src/sbus/request/sbus_message.c583
-rw-r--r--src/sbus/request/sbus_request.c818
-rw-r--r--src/sbus/request/sbus_request_call.c160
-rw-r--r--src/sbus/request/sbus_request_hash.c325
-rw-r--r--src/sbus/request/sbus_request_sender.c340
-rw-r--r--src/sbus/request/sbus_request_util.c67
6 files changed, 2293 insertions, 0 deletions
diff --git a/src/sbus/request/sbus_message.c b/src/sbus/request/sbus_message.c
new file mode 100644
index 0000000..cd7d6da
--- /dev/null
+++ b/src/sbus/request/sbus_message.c
@@ -0,0 +1,583 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_errors.h"
+#include "sbus/sbus_message.h"
+#include "sbus/sbus_sync_private.h"
+#include "sbus/interface/sbus_iterator_writers.h"
+
+/* Data slot that is used for message data. The slot is shared for all
+ * messages, i.e. when a data slot is allocated all messages have the
+ * slot available. */
+dbus_int32_t global_data_slot = -1;
+
+struct sbus_talloc_msg {
+ DBusMessage *msg;
+ bool in_talloc_destructor;
+};
+
+static int sbus_talloc_msg_destructor(struct sbus_talloc_msg *talloc_msg)
+{
+ talloc_msg->in_talloc_destructor = true;
+
+ if (talloc_msg->msg == NULL) {
+ return 0;
+ }
+
+ /* There may exist more references to this message but this talloc
+ * context is no longer valid. We remove dbus message data to invoke
+ * dbus destructor now. */
+ dbus_message_set_data(talloc_msg->msg, global_data_slot, NULL, NULL);
+ dbus_message_unref(talloc_msg->msg);
+ return 0;
+}
+
+static void sbus_msg_data_destructor(void *ctx)
+{
+ struct sbus_talloc_msg *talloc_msg;
+
+ talloc_msg = talloc_get_type(ctx, struct sbus_talloc_msg);
+
+ /* Decrement ref counter on data slot. */
+ dbus_message_free_data_slot(&global_data_slot);
+
+ if (!talloc_msg->in_talloc_destructor) {
+ /* References to this message dropped to zero but through
+ * dbus_message_unref(), not by calling talloc_free(). We need to free
+ * the talloc context and avoid running talloc destructor. */
+ talloc_set_destructor(talloc_msg, NULL);
+ talloc_free(talloc_msg);
+ }
+}
+
+errno_t
+sbus_message_bound(TALLOC_CTX *mem_ctx, DBusMessage *msg)
+{
+ struct sbus_talloc_msg *talloc_msg;
+ DBusFreeFunction free_fn;
+ dbus_bool_t bret;
+
+ if (mem_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Warning: bounding to NULL context!\n");
+ return EINVAL;
+ }
+
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Message can not be NULL!\n");
+ return EINVAL;
+ }
+
+ /* Create a talloc context that will unreference this message when
+ * the parent context is freed. */
+ talloc_msg = talloc(mem_ctx, struct sbus_talloc_msg);
+ if (talloc_msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to bound D-Bus message with talloc context!\n");
+ return ENOMEM;
+ }
+
+ /* Allocate a dbus message data slot that will contain pointer to the
+ * talloc context so we can pick up cases when the dbus message is
+ * freed through dbus api. */
+
+ bret = dbus_message_allocate_data_slot(&global_data_slot);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate data slot!\n");
+ talloc_free(talloc_msg);
+ return ENOMEM;
+ }
+
+ free_fn = sbus_msg_data_destructor;
+ bret = dbus_message_set_data(msg, global_data_slot, talloc_msg, free_fn);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set message data!\n");
+ talloc_free(talloc_msg);
+ dbus_message_free_data_slot(&global_data_slot);
+ return ENOMEM;
+ }
+
+ talloc_msg->msg = msg;
+ talloc_msg->in_talloc_destructor = false;
+
+ talloc_set_destructor(talloc_msg, sbus_talloc_msg_destructor);
+
+ return EOK;
+}
+
+errno_t
+sbus_message_bound_steal(TALLOC_CTX *mem_ctx, DBusMessage *msg)
+{
+ struct sbus_talloc_msg *talloc_msg;
+ void *data;
+
+ if (mem_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Warning: bounding to NULL context!\n");
+ return EINVAL;
+ }
+
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Message can not be NULL!\n");
+ return EINVAL;
+ }
+
+ if (global_data_slot < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "This message is not talloc-bound! "
+ "(data slot < 0)\n");
+ return ERR_INTERNAL;
+ }
+
+ data = dbus_message_get_data(msg, global_data_slot);
+ if (data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "This message is not talloc-bound! "
+ "(returned data is NULL)\n");
+ return ERR_INTERNAL;
+ }
+
+ talloc_msg = talloc_get_type(data, struct sbus_talloc_msg);
+ if (talloc_msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "This message is not talloc-bound! "
+ "(invalid data)\n");
+ return ERR_INTERNAL;
+ }
+
+ talloc_steal(mem_ctx, talloc_msg);
+
+ return EOK;
+}
+
+DBusMessage *
+sbus_method_create_empty(TALLOC_CTX *mem_ctx,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method)
+{
+ DBusMessage *msg;
+ errno_t ret;
+
+ msg = dbus_message_new_method_call(bus, path, iface, method);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create message\n");
+ return NULL;
+ }
+
+ if (mem_ctx != NULL) {
+ ret = sbus_message_bound(mem_ctx, msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to bound message with talloc context!\n");
+ dbus_message_unref(msg);
+ return NULL;
+ }
+ }
+
+ return msg;
+}
+
+static DBusMessage *
+sbus_method_create_valist(TALLOC_CTX *mem_ctx,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ int first_arg_type,
+ va_list va)
+{
+ DBusMessage *msg;
+ dbus_bool_t bret;
+
+ msg = sbus_method_create_empty(mem_ctx, bus, path, iface, method);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ bret = dbus_message_append_args_valist(msg, first_arg_type, va);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+DBusMessage *
+_sbus_method_create(TALLOC_CTX *mem_ctx,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ int first_arg_type,
+ ...)
+{
+ DBusMessage *msg;
+ va_list va;
+
+ va_start(va, first_arg_type);
+ msg = sbus_method_create_valist(mem_ctx, bus, path, iface, method,
+ first_arg_type, va);
+ va_end(va);
+
+ return msg;
+}
+
+DBusMessage *
+sbus_signal_create_empty(TALLOC_CTX *mem_ctx,
+ const char *path,
+ const char *iface,
+ const char *signame)
+{
+ DBusMessage *msg;
+ errno_t ret;
+
+ msg = dbus_message_new_signal(path, iface, signame);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create message\n");
+ return NULL;
+ }
+
+ if (mem_ctx != NULL) {
+ ret = sbus_message_bound(mem_ctx, msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to bound message with talloc context!\n");
+ dbus_message_unref(msg);
+ return NULL;
+ }
+ }
+
+ return msg;
+}
+
+static DBusMessage *
+sbus_signal_create_valist(TALLOC_CTX *mem_ctx,
+ const char *path,
+ const char *iface,
+ const char *signame,
+ int first_arg_type,
+ va_list va)
+{
+ DBusMessage *msg;
+ dbus_bool_t bret;
+
+ msg = sbus_signal_create_empty(mem_ctx, path, iface, signame);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ bret = dbus_message_append_args_valist(msg, first_arg_type, va);
+ if (!bret) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+DBusMessage *
+_sbus_signal_create(TALLOC_CTX *mem_ctx,
+ const char *path,
+ const char *iface,
+ const char *method,
+ int first_arg_type,
+ ...)
+{
+ DBusMessage *msg;
+ va_list va;
+
+ va_start(va, first_arg_type);
+ msg = sbus_signal_create_valist(mem_ctx, path, iface, method,
+ first_arg_type, va);
+ va_end(va);
+
+ return msg;
+}
+
+static errno_t
+sbus_message_parse_valist(DBusMessage *msg,
+ int first_arg_type,
+ va_list va)
+{
+ DBusError error;
+ dbus_bool_t bret;
+ errno_t ret;
+
+ dbus_error_init(&error);
+
+ bret = dbus_message_get_args_valist(msg, &error, first_arg_type, va);
+ if (bret == false) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse D-Bus message\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_error_to_errno(&error);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse D-Bus message [%s]: %s\n",
+ error.name, error.message);
+ goto done;
+ }
+
+done:
+ dbus_error_free(&error);
+ return ret;
+}
+
+errno_t
+_sbus_reply_parse(DBusMessage *msg,
+ int first_arg_type,
+ ...)
+{
+ errno_t ret;
+ va_list va;
+
+ ret = sbus_reply_check(msg);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ va_start(va, first_arg_type);
+ ret = sbus_message_parse_valist(msg, first_arg_type, va);
+ va_end(va);
+
+ return ret;
+}
+
+errno_t
+sbus_reply_check(DBusMessage *reply)
+{
+ dbus_bool_t bret;
+ DBusError error;
+ errno_t ret;
+ int type;
+
+ dbus_error_init(&error);
+
+ type = dbus_message_get_type(reply);
+ switch (type) {
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ ret = EOK;
+ goto done;
+
+ case DBUS_MESSAGE_TYPE_ERROR:
+ bret = dbus_set_error_from_message(&error, reply);
+ if (bret == false) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read error from message\n");
+ ret = EIO;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "D-Bus error [%s]: %s\n", error.name,
+ (error.message == NULL ? "<no-message>" : error.message));
+ ret = sbus_error_to_errno(&error);
+ goto done;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected D-Bus message type [%d]\n",
+ type);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+done:
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+errno_t
+sbus_write_input(DBusMessage *msg,
+ sbus_invoker_writer_fn writer,
+ void *input)
+{
+ DBusMessageIter write_iterator;
+ errno_t ret;
+
+ if (writer == NULL) {
+ return EOK;
+ }
+
+ dbus_message_iter_init_append(msg, &write_iterator);
+
+ ret = writer(&write_iterator, input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write message data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+errno_t
+sbus_read_output(TALLOC_CTX *mem_ctx,
+ DBusMessage *msg,
+ sbus_invoker_reader_fn reader,
+ void *output)
+{
+ DBusMessageIter read_iterator;
+ errno_t ret;
+
+ if (reader == NULL) {
+ return EOK;
+ }
+
+ dbus_message_iter_init(msg, &read_iterator);
+
+ ret = reader(mem_ctx, &read_iterator, output);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read message data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+DBusMessage *
+sbus_create_method_call(TALLOC_CTX *mem_ctx,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ void *input)
+{
+ DBusMessage *msg;
+ errno_t ret;
+
+ if (raw_message != NULL) {
+ return raw_message;
+ }
+
+ msg = sbus_method_create_empty(mem_ctx, bus, path, iface, method);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ ret = sbus_write_input(msg, writer, input);
+ if (ret != EOK) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+DBusMessage *
+sbus_create_signal_call(TALLOC_CTX *mem_ctx,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *path,
+ const char *iface,
+ const char *signal_name,
+ void *input)
+{
+ DBusMessage *msg;
+ errno_t ret;
+
+ if (raw_message != NULL) {
+ return raw_message;
+ }
+
+ msg = sbus_signal_create_empty(mem_ctx, path, iface, signal_name);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ ret = sbus_write_input(msg, writer, input);
+ if (ret != EOK) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+DBusMessage *
+sbus_create_set_call(TALLOC_CTX *mem_ctx,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *property,
+ const char *type,
+ void *input)
+{
+ DBusMessageIter iter;
+ DBusMessageIter variant;
+ DBusMessage *msg;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ if (writer == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: writer cannot be NULL\n");
+ return NULL;
+ }
+
+ msg = sbus_method_create_empty(mem_ctx, bus, path,
+ DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ ret = sbus_iterator_write_s(&iter, iface);
+ if (ret != EOK) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ ret = sbus_iterator_write_s(&iter, property);
+ if (ret != EOK) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ dbret = dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ type, &variant);
+ if (!dbret) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ ret = writer(&variant, input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write message data [%d]: %s\n",
+ ret, sss_strerror(ret));
+ dbus_message_iter_abandon_container(&iter, &variant);
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ dbret = dbus_message_iter_close_container(&iter, &variant);
+ if (!dbret) {
+ dbus_message_iter_abandon_container(&iter, &variant);
+ dbus_message_unref(msg);
+ return NULL;
+ }
+
+ return msg;
+}
diff --git a/src/sbus/request/sbus_request.c b/src/sbus/request/sbus_request.c
new file mode 100644
index 0000000..8d49259
--- /dev/null
+++ b/src/sbus/request/sbus_request.c
@@ -0,0 +1,818 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+#include <dhash.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "util/sss_chain_id.h"
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_private.h"
+
+typedef errno_t
+(*sbus_request_messages_fn)(struct tevent_req *req,
+ TALLOC_CTX **_reply_mem_ctx,
+ DBusMessage **_client_message,
+ DBusMessage ***_reply);
+
+struct sbus_active_requests *
+sbus_active_requests_init(TALLOC_CTX *mem_ctx)
+{
+ struct sbus_active_requests *requests;
+
+ requests = talloc_zero(mem_ctx, struct sbus_active_requests);
+ if (requests == NULL) {
+ return NULL;
+ }
+
+ requests->incoming = sbus_requests_init(requests);
+ if (requests->incoming == NULL) {
+ goto fail;
+ }
+
+ requests->outgoing = sbus_requests_init(requests);
+ if (requests->outgoing == NULL) {
+ goto fail;
+ }
+
+ return requests;
+
+fail:
+ talloc_free(requests);
+ return NULL;
+}
+
+struct sbus_request *
+sbus_request_create(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *destination,
+ const char *interface,
+ const char *member,
+ const char *path)
+{
+ struct sbus_request *request;
+
+ request = talloc_zero(mem_ctx, struct sbus_request);
+ if (request == NULL) {
+ return NULL;
+ }
+
+ request->conn = conn;
+ request->type = type;
+ request->sender = NULL;
+
+ request->destination = talloc_strdup(request, destination);
+ if (destination != NULL && request->destination == NULL) {
+ goto fail;
+ }
+
+ request->interface = talloc_strdup(request, interface);
+ if (request->interface == NULL) {
+ goto fail;
+ }
+
+ request->member = talloc_strdup(request, member);
+ if (request->member == NULL) {
+ goto fail;
+ }
+
+ request->path = talloc_strdup(request, path);
+ if (request->path == NULL) {
+ goto fail;
+ }
+
+ return request;
+
+fail:
+ talloc_free(request);
+ return NULL;
+}
+
+static errno_t
+sbus_request_prepare_reply(TALLOC_CTX *mem_ctx,
+ enum sbus_request_type type,
+ DBusMessage *msg,
+ DBusMessage **_reply,
+ DBusMessageIter **_reply_iterator)
+{
+ DBusMessageIter *iterator;
+ DBusMessage *reply;
+ errno_t ret;
+
+ if (type == SBUS_REQUEST_SIGNAL) {
+ /* Signals don't send reply*/
+ *_reply = NULL;
+ *_reply_iterator = NULL;
+ return EOK;
+ }
+
+ iterator = talloc_zero(mem_ctx, DBusMessageIter);
+ if (iterator == NULL) {
+ return ENOMEM;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL) {
+ talloc_free(iterator);
+ return ENOMEM;
+ }
+
+ ret = sbus_message_bound(mem_ctx, reply);
+ if (ret != EOK) {
+ talloc_free(iterator);
+ dbus_message_unref(reply);
+ return ret;
+ }
+
+ dbus_message_iter_init_append(reply, iterator);
+
+ *_reply = reply;
+ *_reply_iterator = iterator;
+
+ return EOK;
+}
+
+static errno_t
+sbus_request_switch_reply(DBusMessage *reply,
+ struct tevent_req *req,
+ sbus_request_messages_fn messages_fn)
+{
+ DBusMessage *client_message;
+ DBusMessage **reply_pointer;
+ TALLOC_CTX *reply_mem_ctx;
+ const char *sender;
+ dbus_uint32_t serial;
+ dbus_bool_t dbret;
+ errno_t ret;
+
+ ret = messages_fn(req, &reply_mem_ctx, &client_message, &reply_pointer);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Copy reply to location in a state of this request. */
+
+ *reply_pointer = dbus_message_copy(reply);
+ if (*reply_pointer == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sbus_message_bound(reply_mem_ctx, *reply_pointer);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (client_message == NULL) {
+ ret = EOK;
+ goto done;
+ }
+
+ /* We set destination and serial in reply to point to the original
+ * client request. */
+
+ sender = dbus_message_get_sender(client_message);
+ serial = dbus_message_get_serial(client_message);
+
+ dbret = dbus_message_set_destination(*reply_pointer, sender);
+ if (dbret == false) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set reply sender!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ dbret = dbus_message_set_reply_serial(*reply_pointer, serial);
+ if (dbret == false) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set reply serial!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ dbus_message_unref(*reply_pointer);
+ *reply_pointer = NULL;
+ }
+
+ return ret;
+}
+
+static void
+sbus_request_notify_error(hash_table_t *table,
+ const char *key,
+ struct tevent_req *req,
+ errno_t error)
+{
+ struct sbus_request_list *mainreq = NULL;
+ struct sbus_request_list *list;
+ struct sbus_request_list *item;
+
+ list = sbus_requests_lookup(table, key);
+ if (list == NULL) {
+ /* This was the only request with no key generator available. */
+ tevent_req_error(req, error);
+ return;
+ }
+
+ /* First notify all chained D-Bus requests. */
+ DLIST_FOR_EACH(item, list) {
+ /* Remember the main request. */
+ if (item->req == req) {
+ mainreq = item;
+ continue;
+ }
+
+ /* We don't want to notify current, invalid or non D-Bus request. */
+ if (!item->is_dbus || item->is_invalid) {
+ continue;
+ }
+
+ sbus_requests_finish(item, error);
+ }
+
+ /* Now we finish the main request. */
+ sbus_requests_finish(mainreq, error);
+
+ /* And as last, we notify all await requests. */
+ DLIST_FOR_EACH(item, list) {
+ if (item->is_dbus) {
+ continue;
+ }
+
+ sbus_requests_finish(item, error);
+ }
+
+ sbus_requests_delete(list);
+}
+
+static void
+sbus_request_notify_success(hash_table_t *table,
+ const char *key,
+ struct tevent_req *req,
+ sbus_request_messages_fn messages_fn,
+ DBusMessage *reply)
+{
+ struct sbus_request_list *mainreq = NULL;
+ struct sbus_request_list *list;
+ struct sbus_request_list *item;
+ errno_t ret;
+
+ list = sbus_requests_lookup(table, key);
+ if (list == NULL) {
+ /* This was the only request with no key generator available. */
+ tevent_req_done(req);
+ return;
+ }
+
+ /* First notify all chained D-Bus requests so we can steal the reply. */
+ DLIST_FOR_EACH(item, list) {
+ /* Remember the main request. */
+ if (item->req == req) {
+ mainreq = item;
+ continue;
+ }
+
+ /* We don't want to notify current, invalid or non D-Bus request. */
+ if (!item->is_dbus || item->is_invalid) {
+ continue;
+ }
+
+ ret = sbus_request_switch_reply(reply, item->req, messages_fn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to switch reply for %p, "
+ "terminating this request!\n", item->req);
+ sbus_requests_finish(item, ret);
+ continue;
+ }
+
+ sbus_requests_finish(item, EOK);
+ }
+
+ /* Now we finish the main request. */
+ sbus_requests_finish(mainreq, EOK);
+
+ /* And as last, we notify all await requests. */
+ DLIST_FOR_EACH(item, list) {
+ if (item->is_dbus) {
+ continue;
+ }
+
+ sbus_requests_finish(item, EOK);
+ }
+
+ sbus_requests_delete(list);
+}
+
+struct sbus_incoming_request_state {
+ struct tevent_context *ev;
+ const struct sbus_invoker *invoker;
+ const struct sbus_handler *handler;
+ struct sbus_connection *conn;
+ struct sbus_request *request;
+ DBusMessageIter *read_iter;
+ DBusMessage *reply;
+ DBusMessage *msg;
+ const char *key;
+};
+
+static errno_t
+sbus_request_messages(struct tevent_req *req,
+ TALLOC_CTX **_reply_mem_ctx,
+ DBusMessage **_client_message,
+ DBusMessage ***_reply)
+{
+ struct sbus_incoming_request_state *state;
+
+ state = tevent_req_data(req, struct sbus_incoming_request_state);
+ if (state == NULL) {
+ return ERR_INTERNAL;
+ }
+
+ *_reply_mem_ctx = state;
+ *_client_message = state->msg;
+ *_reply = &state->reply;
+
+ return EOK;
+}
+
+static void sbus_incoming_request_sender_done(struct tevent_req *subreq);
+static void sbus_incoming_request_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_incoming_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_connection *conn,
+ struct sbus_request *request,
+ const struct sbus_invoker *invoker,
+ const struct sbus_handler *handler,
+ const char *sender_name,
+ DBusMessageIter *read_iter,
+ DBusMessage *msg)
+{
+ struct sbus_incoming_request_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_incoming_request_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ if (invoker->issue == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "There is no invoker set!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ switch (handler->type) {
+ case SBUS_HANDLER_SYNC:
+ if (handler->sync == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "There is no handler set!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ break;
+ case SBUS_HANDLER_ASYNC:
+ if (handler->async_send == NULL || handler->async_recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "There is no handler set!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ break;
+ }
+
+ state->ev = ev;
+ state->msg = msg;
+ state->conn = conn;
+ state->request = request;
+ state->invoker = invoker;
+ state->handler = handler;
+ state->read_iter = read_iter;
+ state->reply = NULL;
+
+ subreq = sbus_sender_resolve_send(mem_ctx, ev, conn, request->type,
+ request->destination, request->path,
+ request->interface, request->member,
+ sender_name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_incoming_request_sender_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_incoming_request_sender_done(struct tevent_req *subreq)
+{
+ struct sbus_incoming_request_state *state;
+ DBusMessageIter *write_iter = NULL;
+ struct sbus_sender *sender;
+ struct tevent_req *req;
+ bool key_exists;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_incoming_request_state);
+
+ ret = sbus_sender_resolve_recv(state, subreq, &sender);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->request->sender = talloc_steal(state->request, sender);
+
+ ret = sbus_check_access(state->conn, state->request);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sbus_request_prepare_reply(state, state->request->type, state->msg,
+ &state->reply, &write_iter);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /**
+ * Invoke a read invoker. This function will read method arguments
+ * from message. Then it will use these arguments to create a key
+ * describing this method and at last, it will schedule method handler
+ * to be issued in next loop.
+ */
+ subreq = state->invoker->issue(state, state->ev, state->request,
+ state->invoker->keygen, state->handler,
+ state->read_iter, write_iter,
+ &state->key);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ /**
+ * Now when we have the key, we will search table to see if the same
+ * request is not already in progress. If it is, we cancel this one
+ * and register ourselves for notification when it is finished.
+ *
+ * Otherwise we add ourselves as the first request of this type and
+ * set a tevent callback that is triggered when the method handler is done.
+ */
+ ret = sbus_requests_add(state->conn->requests->incoming, state->key,
+ state->conn, req, true, &key_exists);
+ if (ret != EOK || key_exists) {
+ /* Cancel the sub request. Since there was either an error or the
+ * sub request was chained. */
+ talloc_zfree(subreq);
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_incoming_request_done, req);
+ return;
+
+done:
+ talloc_zfree(write_iter);
+
+ if (state->reply != NULL) {
+ dbus_message_unref(state->reply);
+ state->reply = NULL;
+ }
+
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ }
+}
+
+static void sbus_incoming_request_done(struct tevent_req *subreq)
+{
+ struct sbus_incoming_request_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_incoming_request_state);
+
+ /* state->reply is filled through iterator in the subrequest. */
+ ret = sbus_invoker_recv(subreq);
+ talloc_zfree(subreq);
+
+ if (ret != EOK) {
+ sbus_request_notify_error(state->conn->requests->incoming,
+ state->key, req, ret);
+ return;
+ }
+
+ sbus_request_notify_success(state->conn->requests->incoming,
+ state->key, req, sbus_request_messages,
+ state->reply);
+}
+
+errno_t
+sbus_incoming_request_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ struct sbus_incoming_request_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sbus_incoming_request_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ /* Signals have no reply so this is ok. */
+ if (state->reply == NULL) {
+ *_reply = NULL;
+ return EOK;
+ }
+
+ ret = sbus_message_bound_steal(mem_ctx, state->reply);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to steal message [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ *_reply = state->reply;
+
+ return EOK;
+}
+
+struct sbus_outgoing_request_state {
+ const char *key;
+ struct sbus_connection *conn;
+ DBusMessage *reply;
+ uint64_t chain_id;
+};
+
+static errno_t
+sbus_outgoing_request_messages(struct tevent_req *req,
+ TALLOC_CTX **_reply_mem_ctx,
+ DBusMessage **_client_message,
+ DBusMessage ***_reply)
+{
+ struct sbus_outgoing_request_state *state;
+
+ state = tevent_req_data(req, struct sbus_outgoing_request_state);
+ if (state == NULL) {
+ return ERR_INTERNAL;
+ }
+
+ *_reply_mem_ctx = state;
+ *_client_message = NULL;
+ *_reply = &state->reply;
+
+ return EOK;
+}
+
+static void sbus_outgoing_request_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_outgoing_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_connection *conn,
+ const char *key,
+ DBusMessage *msg)
+{
+ struct sbus_outgoing_request_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ bool key_exists;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_outgoing_request_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->conn = conn;
+
+ /*
+ * The message is sent over top level dbus tevent code. This means that
+ * the chain id information is lost and is not restored when we get reply
+ * from dbus. Therefore we need to remember it and restore it manually
+ * when this request is done.
+ */
+ state->chain_id = sss_chain_id_get();
+
+ if (key != NULL) {
+ state->key = talloc_strdup(state, key);
+ if (state->key == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ /**
+ * We will search table to see if the same request is not already
+ * in progress. If it is, we register ourselves for notification
+ * when it is finished.
+ *
+ * Otherwise we add ourselves as the first request of this type and
+ * set a tevent callback that is triggered when the method handler is done.
+ */
+ ret = sbus_requests_add(conn->requests->outgoing, key,
+ conn, req, true, &key_exists);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (key_exists) {
+ return req;
+ }
+
+ subreq = sbus_message_send(state, conn, msg, SBUS_MESSAGE_TIMEOUT);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_outgoing_request_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_outgoing_request_done(struct tevent_req *subreq)
+{
+ struct sbus_outgoing_request_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_outgoing_request_state);
+
+ sss_chain_id_set(state->chain_id);
+
+ ret = sbus_message_recv(state, subreq, &state->reply);
+ talloc_zfree(subreq);
+
+ if (ret != EOK) {
+ sbus_request_notify_error(state->conn->requests->outgoing,
+ state->key, req, ret);
+ return;
+ }
+
+ sbus_request_notify_success(state->conn->requests->outgoing,
+ state->key, req,
+ sbus_outgoing_request_messages,
+ state->reply);
+}
+
+errno_t
+sbus_outgoing_request_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ struct sbus_outgoing_request_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sbus_outgoing_request_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ ret = sbus_message_bound_steal(mem_ctx, state->reply);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to steal message [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ *_reply = state->reply;
+
+ return EOK;
+}
+
+struct sbus_request_await_state {
+ int dummy;
+};
+
+struct tevent_req *
+sbus_request_await_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *object_path,
+ const char *interface,
+ const char *member,
+ const char *additional_key)
+{
+ struct sbus_request_await_state *state;
+ struct sbus_request_list *list;
+ struct tevent_req *req;
+ const char *key;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_request_await_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ key = talloc_asprintf(state, "-:%u:%s.%s:%s%s%s",
+ type, interface, member, object_path,
+ additional_key == NULL ? "" : ":",
+ additional_key == NULL ? "" : additional_key);
+ if (key == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ list = sbus_requests_lookup(conn->requests->outgoing, key);
+ if (list == NULL) {
+ /* No active request with this key exists. */
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise attach to this request. */
+ ret = sbus_requests_add(conn->requests->outgoing, key, conn,
+ req, false, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to attach to the request list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, conn->ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, conn->ev);
+ }
+
+ return req;
+}
+
+errno_t sbus_request_await_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static errno_t sbus_unwanted_reply_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+void sbus_unwanted_reply(struct tevent_req *subreq)
+{
+ errno_t ret;
+
+ ret = sbus_unwanted_reply_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK && ret != ERR_SBUS_UNKNOWN_SERVICE) {
+ DEBUG(SSSDBG_OP_FAILURE, "Error sending sbus message [%d]: %s\n",
+ ret, sss_strerror(ret));
+ }
+}
diff --git a/src/sbus/request/sbus_request_call.c b/src/sbus/request/sbus_request_call.c
new file mode 100644
index 0000000..cf2a6e5
--- /dev/null
+++ b/src/sbus/request/sbus_request_call.c
@@ -0,0 +1,160 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <tevent.h>
+#include <talloc.h>
+
+#include "sbus/sbus_private.h"
+
+struct sbus_call_method_state {
+ DBusMessage *reply;
+};
+
+static void sbus_call_method_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_call_method_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_keygen keygen,
+ sbus_invoker_writer_fn writer,
+ const char *bus,
+ const char *path,
+ const char *iface,
+ const char *method,
+ void *input)
+{
+ struct sbus_call_method_state *state;
+ struct sbus_request *sbus_req;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ const char *key = NULL;
+ DBusMessage *msg;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_call_method_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ sbus_req = sbus_request_create(state, conn, SBUS_REQUEST_METHOD,
+ bus, iface, method, path);
+ if (sbus_req == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg = sbus_create_method_call(state, raw_message, writer, bus, path,
+ iface, method, input);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_request_key(state, keygen, sbus_req, input, &key);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ subreq = sbus_outgoing_request_send(state, conn->ev, conn, key, msg);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_call_method_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, conn->ev);
+ }
+
+ return req;
+}
+
+static void sbus_call_method_done(struct tevent_req *subreq)
+{
+ struct sbus_call_method_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_call_method_state);
+
+ ret = sbus_outgoing_request_recv(state, subreq, &state->reply);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+errno_t
+sbus_call_method_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ struct sbus_call_method_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sbus_call_method_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ ret = sbus_message_bound_steal(mem_ctx, state->reply);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to steal message [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ *_reply = state->reply;
+
+ return EOK;
+}
+
+void
+sbus_call_signal_send(struct sbus_connection *conn,
+ DBusMessage *raw_message,
+ sbus_invoker_writer_fn writer,
+ const char *path,
+ const char *iface,
+ const char *signal_name,
+ void *input)
+{
+ DBusMessage *msg;
+
+ msg = sbus_create_signal_call(NULL, raw_message, writer, path, iface,
+ signal_name, input);
+ if (msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create signal message!\n");
+ return;
+ }
+
+ sbus_emit_signal(conn, msg);
+}
diff --git a/src/sbus/request/sbus_request_hash.c b/src/sbus/request/sbus_request_hash.c
new file mode 100644
index 0000000..0ddad03
--- /dev/null
+++ b/src/sbus/request/sbus_request_hash.c
@@ -0,0 +1,325 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "util/dlinklist.h"
+#include "util/sss_ptr_hash.h"
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_private.h"
+
+static void
+sbus_requests_disable_spies(struct sbus_request_list *item);
+
+static void
+sbus_requests_validate(struct sbus_request_list *list);
+
+struct sbus_request_spy {
+ struct sbus_request_list *item;
+};
+
+static int
+sbus_requests_spy_destructor(struct sbus_request_spy *spy)
+{
+ struct sbus_request_list *item;
+
+ item = spy->item;
+
+ if (item->spy.conn == spy) {
+ item->spy.conn = NULL;
+ item->conn = NULL;
+ } else {
+ item->spy.req = NULL;
+ item->req = NULL;
+ }
+
+ sbus_requests_finish(item, ERR_TERMINATED);
+ sbus_requests_validate(item);
+
+ return 0;
+}
+
+static struct sbus_request_spy *
+sbus_requests_spy_create(TALLOC_CTX *mem_ctx,
+ struct sbus_request_list *item)
+{
+ struct sbus_request_spy *spy;
+
+ spy = talloc_zero(mem_ctx, struct sbus_request_spy);
+ if (spy == NULL) {
+ return NULL;
+ }
+
+ spy->item = item;
+
+ talloc_set_destructor(spy, sbus_requests_spy_destructor);
+
+ return spy;
+}
+
+static errno_t
+sbus_requests_attach_spies(struct sbus_request_list *item)
+{
+ item->spy.conn = sbus_requests_spy_create(item->conn, item);
+ if (item->spy.conn == NULL) {
+ return ENOMEM;
+ }
+
+ item->spy.req = sbus_requests_spy_create(item->req, item);
+ if (item->spy.req == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+}
+
+static void
+sbus_requests_disable_spies(struct sbus_request_list *item)
+{
+ if (item->spy.req != NULL) {
+ talloc_set_destructor(item->spy.req, NULL);
+ }
+
+ if (item->spy.conn != NULL) {
+ talloc_set_destructor(item->spy.conn, NULL);
+ }
+
+ talloc_zfree(item->spy.req);
+ talloc_zfree(item->spy.conn);
+}
+
+hash_table_t *
+sbus_requests_init(TALLOC_CTX *mem_ctx)
+{
+ return sss_ptr_hash_create(mem_ctx, NULL, NULL);
+}
+
+errno_t
+sbus_requests_add(hash_table_t *table,
+ const char *key,
+ struct sbus_connection *conn,
+ struct tevent_req *req,
+ bool is_dbus,
+ bool *_key_exists)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_request_list *list;
+ struct sbus_request_list *item;
+ bool key_exists = false;
+ errno_t ret;
+
+ if (key == NULL) {
+ /* This is ok, since not all request are supposed to be multicasted.
+ * The caller will continue as this was a new request.
+ * And it simplifies the code. */
+ *_key_exists = false;
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ item = talloc_zero(tmp_ctx, struct sbus_request_list);
+ if (item == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ item->req = req;
+ item->conn = conn;
+ item->is_dbus = is_dbus;
+
+ ret = sbus_requests_attach_spies(item);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* First, check if the key already exist. If yes, check if the list
+ * is valid and just append the item to the list if so. Otherwise,
+ * the list is internally deleted and we can create a new one. */
+ list = sss_ptr_hash_lookup(table, key, struct sbus_request_list);
+ if (list != NULL) {
+ key_exists = true;
+ DLIST_ADD_END(list, item, struct sbus_request_list *);
+ DEBUG(SSSDBG_TRACE_ALL, "Chaining request: %s\n", key);
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise create new hash entry and new list. */
+ list = item;
+ ret = sss_ptr_hash_add(table, key, list, struct sbus_request_list);
+
+done:
+ if (ret == EOK) {
+ if (_key_exists != NULL) {
+ *_key_exists = key_exists;
+ }
+
+ talloc_steal(table, item);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+struct sbus_request_list *
+sbus_requests_lookup(hash_table_t *table,
+ const char *key)
+{
+ if (key == NULL) {
+ /* This is ok, since not all request are supposed to be multicasted.
+ * The caller will have an empty list ot send notification to.
+ * And it simplifies the code. */
+ return NULL;
+ }
+
+ return sss_ptr_hash_lookup(table, key, struct sbus_request_list);
+}
+
+void
+sbus_requests_delete(struct sbus_request_list *list)
+{
+ struct sbus_request_list *current, *next;
+
+ if (list == NULL) {
+ return;
+ }
+
+ /* Find head of the list. */
+ while (list->prev != NULL) {
+ list = list->prev;
+ }
+
+ /* Freeing the first item will remove the list also from the table. */
+ DLIST_FOR_EACH_SAFE(current, next, list) {
+ sbus_requests_disable_spies(current);
+ talloc_zfree(current);
+ }
+}
+
+static void
+sbus_requests_validate(struct sbus_request_list *list)
+{
+ struct sbus_request_list *current, *next;
+
+ /* Find head of the list. */
+ while (list->prev != NULL) {
+ list = list->prev;
+ }
+
+ /* An item is invalid if either its request or associated connection
+ * is freed before this sbus request has finished.
+ *
+ * The list is invalid only if all items are invalid or if the first
+ * item that holds the actual request is invalid. If this is the case
+ * we will remove this list and report it to the caller.
+ *
+ * The sbus request is always associated with the first item. If it
+ * is invalid we must also terminate all other requests. */
+
+ if (list->is_invalid) {
+ DLIST_FOR_EACH_SAFE(current, next, list->next) {
+ if (current->is_invalid) {
+ continue;
+ }
+
+ sbus_requests_disable_spies(current);
+ tevent_req_error(current->req, ERR_TERMINATED);
+ }
+ } else {
+ DLIST_FOR_EACH_SAFE(current, next, list) {
+ if (!current->is_invalid) {
+ return;
+ }
+ }
+ }
+
+ sbus_requests_delete(list);
+}
+
+void
+sbus_requests_finish(struct sbus_request_list *item,
+ errno_t error)
+{
+ if (item == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Bug: item is NULL\n");
+ return;
+ }
+
+ if (item->is_invalid) {
+ return;
+ }
+
+ /* Make sure that spies are disabled and this item is not handled
+ * anymore. */
+ sbus_requests_disable_spies(item);
+ item->is_invalid = true;
+
+ if (item->req == NULL) {
+ return;
+ }
+
+ if (error != EOK) {
+ tevent_req_error(item->req, error);
+ return;
+ }
+
+ tevent_req_done(item->req);
+
+ item->req = NULL;
+}
+
+void
+sbus_requests_terminate_all(hash_table_t *table,
+ errno_t error)
+{
+ struct sbus_request_list *list;
+ struct sbus_request_list *item;
+ hash_value_t *values;
+ unsigned long int num;
+ unsigned long int i;
+ int hret;
+
+ hret = hash_values(table, &num, &values);
+ if (hret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get list of active requests "
+ "[%d]: %s\n", hret, hash_error_string(hret));
+ return;
+ }
+
+ for (i = 0; i < num; i++) {
+ list = sss_ptr_get_value(&values[i], struct sbus_request_list);
+
+ DLIST_FOR_EACH(item, list) {
+ sbus_requests_finish(item, error);
+ }
+
+ sbus_requests_delete(list);
+ }
+
+ talloc_free(values);
+}
diff --git a/src/sbus/request/sbus_request_sender.c b/src/sbus/request/sbus_request_sender.c
new file mode 100644
index 0000000..39cdec0
--- /dev/null
+++ b/src/sbus/request/sbus_request_sender.c
@@ -0,0 +1,340 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <dhash.h>
+#include <stdint.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "util/sss_ptr_hash.h"
+#include "sbus/sbus_private.h"
+#include "sbus/interface_dbus/sbus_dbus_client_async.h"
+
+struct sbus_sender *
+sbus_sender_create(TALLOC_CTX *mem_ctx,
+ const char *name,
+ int64_t uid)
+{
+ struct sbus_sender sender = {.name = name, .uid = uid};
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ return sbus_sender_copy(mem_ctx, &sender);
+}
+
+struct sbus_sender *
+sbus_sender_copy(TALLOC_CTX *mem_ctx,
+ const struct sbus_sender *input)
+{
+ struct sbus_sender *copy;
+
+ copy = talloc_zero(mem_ctx, struct sbus_sender);
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ copy->name = talloc_strdup(copy, input->name);
+ if (copy->name == NULL) {
+ talloc_free(copy);
+ return NULL;
+ }
+
+ copy->uid = input->uid;
+
+ return copy;
+}
+
+hash_table_t *
+sbus_senders_init(TALLOC_CTX *mem_ctx)
+{
+ return sss_ptr_hash_create(mem_ctx, NULL, NULL);
+}
+
+static errno_t
+sbus_senders_add(hash_table_t *table,
+ struct sbus_sender *sender)
+{
+ struct sbus_sender *copy;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Inserting identity of sender [%s]: %"PRIi64"\n",
+ sender->name, sender->uid);
+
+ copy = sbus_sender_copy(table, sender);
+ if (copy == NULL) {
+ return ENOMEM;
+ }
+
+ return sss_ptr_hash_add(table, sender->name, copy, struct sbus_sender);
+}
+
+static struct sbus_sender *
+sbus_senders_lookup(hash_table_t *table,
+ const char *name)
+{
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Looking for identity of sender [%s]\n",
+ name);
+
+ return sss_ptr_hash_lookup(table, name, struct sbus_sender);
+}
+
+void
+sbus_senders_delete(hash_table_t *table,
+ const char *name)
+{
+ if (sss_ptr_hash_has_key(table, name)) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Removing identity of sender [%s]\n",
+ name);
+ sss_ptr_hash_delete(table, name, true);
+ }
+}
+
+errno_t
+sbus_sender_check_input(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *destination,
+ const char *object_path,
+ const char *interface,
+ const char *member,
+ const char *name,
+ struct sbus_sender **_sender)
+{
+ /* This is a server call. We do not need to resolve sender in this case. */
+ if (destination != NULL && strcmp(destination, DBUS_SERVICE_DBUS) == 0) {
+ return EOK;
+ }
+
+ /* Hello is a special method that is used by clients to register on the
+ * bus. Upon registration server assigns unique name to the clients.
+ * Therefore it is not actually possible to resolve a sender name
+ * prior this call. */
+ if (name == NULL && type == SBUS_REQUEST_METHOD
+ && strcmp(object_path, DBUS_PATH_DBUS) == 0
+ && strcmp(interface, DBUS_INTERFACE_DBUS) == 0
+ && strcmp(member, "Hello") == 0) {
+
+ *_sender = sbus_sender_create(mem_ctx, name, SBUS_SENDER_HELLO);
+ if (*_sender == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+ }
+
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Can not resolve empty name!\n");
+ return EINVAL;
+ }
+
+ /* Got signal from bus, this is OK. This name is not really resolvable. */
+ if (strcmp(name, DBUS_SERVICE_DBUS) == 0) {
+ *_sender = sbus_sender_create(mem_ctx, name, SBUS_SENDER_DBUS);
+ if (*_sender == NULL) {
+ return ENOMEM;
+ }
+
+ return EOK;
+ }
+
+ return EAGAIN;
+}
+
+struct sbus_sender_resolve_state {
+ struct sbus_connection *conn;
+ enum sbus_request_type type;
+ struct sbus_sender *sender;
+ const char *name;
+};
+
+static void sbus_sender_resolve_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sbus_sender_resolve_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sbus_connection *conn,
+ enum sbus_request_type type,
+ const char *destination,
+ const char *object_path,
+ const char *interface,
+ const char *member,
+ const char *name)
+{
+ struct sbus_sender_resolve_state *state;
+ struct sbus_sender *sender;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_sender_resolve_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ state->conn = conn;
+ state->type = type;
+ state->sender = NULL;
+
+ ret = sbus_sender_check_input(state, conn, type, destination, object_path,
+ interface, member, name, &state->sender);
+ if (ret != EAGAIN) {
+ goto done;
+ }
+
+ /* Check if the sender is already known. If yes, we must create a copy
+ * of it since it may be asynchronously deleted through NameOwnerChanged
+ * signal. */
+ sender = sbus_senders_lookup(conn->senders, name);
+ if (sender != NULL) {
+ state->sender = sbus_sender_copy(state, sender);
+ if (state->sender == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+ goto done;
+ }
+
+ state->name = talloc_strdup(state, name);
+ if (state->name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ subreq = sbus_call_DBus_GetConnectionUnixUser_send(state, conn,
+ DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, name);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, sbus_sender_resolve_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ tevent_req_post(req, ev);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void sbus_sender_resolve_done(struct tevent_req *subreq)
+{
+ struct sbus_sender_resolve_state *state;
+ struct sbus_sender *sender;
+ struct tevent_req *req;
+ uint32_t uid;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_sender_resolve_state);
+
+ ret = sbus_call_DBus_GetConnectionUnixUser_recv(subreq, &uid);
+ talloc_zfree(subreq);
+ if (ret == ERR_SBUS_UNKNOWN_OWNER && state->type == SBUS_REQUEST_SIGNAL) {
+ /* If the caller of the signal exits before we translate the name,
+ * it is possible that the name is no longer known on the bus.
+ * E.g. when the signal is sent via dbus-send. */
+ DEBUG(SSSDBG_MINOR_FAILURE, "Identity of signal sender "
+ "[%s] is not known. Continue without it.\n", state->name);
+
+ state->sender = sbus_sender_create(state, state->name,
+ SBUS_SENDER_SIGNAL);
+ if (state->sender == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ goto done;
+ }
+
+ /* We don't have request chaining on this level so it is possible that
+ * a concurrent lookup finished first. If this is this case, we return
+ * the previous lookup result and just finish.
+ *
+ * We must create a copy of the result since it may be asynchronously
+ * deleted through NameOwnerChanged signal. */
+ sender = sbus_senders_lookup(state->conn->senders, state->name);
+ if (sender != NULL) {
+ state->sender = sbus_sender_copy(state, sender);
+ if (state->sender == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+ goto done;
+ }
+
+ /* Otherwise we insert this result into the table. The add operation
+ * will create a copy of this structure so we can return state->sender
+ * directly in the result. */
+ state->sender = sbus_sender_create(state, state->name, uid);
+ if (state->sender == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sbus_senders_add(state->conn->senders, state->sender);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t
+sbus_sender_resolve_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ struct sbus_sender **_sender)
+{
+ struct sbus_sender_resolve_state *state;
+ state = tevent_req_data(req, struct sbus_sender_resolve_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_sender) {
+ *_sender = talloc_steal(mem_ctx, state->sender);
+ }
+
+ return EOK;
+}
diff --git a/src/sbus/request/sbus_request_util.c b/src/sbus/request/sbus_request_util.c
new file mode 100644
index 0000000..c9bddbc
--- /dev/null
+++ b/src/sbus/request/sbus_request_util.c
@@ -0,0 +1,67 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <tevent.h>
+#include <talloc.h>
+
+#include "sbus/sbus_request.h"
+#include "sbus/sbus_private.h"
+
+errno_t
+sbus_invoker_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+errno_t
+sbus_request_key(TALLOC_CTX *mem_ctx,
+ sbus_invoker_keygen keygen,
+ struct sbus_request *sbus_req,
+ void *input,
+ const char **_key)
+{
+ const char *(*args_fn)(TALLOC_CTX *, struct sbus_request *, void *);
+ const char *(*noargs_fn)(TALLOC_CTX *, struct sbus_request *);
+ const char *key;
+
+ if (keygen == NULL) {
+ *_key = NULL;
+ return EOK;
+ }
+
+ if (input == NULL) {
+ noargs_fn = keygen;
+ key = noargs_fn(mem_ctx, sbus_req);
+ } else {
+ args_fn = keygen;
+ key = args_fn(mem_ctx, sbus_req, input);
+ }
+
+ if (key == NULL) {
+ return ENOMEM;
+ }
+
+ *_key = key;
+
+ return EOK;
+
+}