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/request | |
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/request')
-rw-r--r-- | src/sbus/request/sbus_message.c | 583 | ||||
-rw-r--r-- | src/sbus/request/sbus_request.c | 818 | ||||
-rw-r--r-- | src/sbus/request/sbus_request_call.c | 160 | ||||
-rw-r--r-- | src/sbus/request/sbus_request_hash.c | 325 | ||||
-rw-r--r-- | src/sbus/request/sbus_request_sender.c | 340 | ||||
-rw-r--r-- | src/sbus/request/sbus_request_util.c | 67 |
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; + +} |