/* Authors: Pavel Březina Copyright (C) 2015 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 . */ #include #include #include #include "util/util.h" #include "sbus/sssd_dbus.h" #include "sbus/sssd_dbus_private.h" static int sbus_incoming_signal_destructor(struct sbus_incoming_signal *a_signal) { dbus_message_unref(a_signal->message); return 0; } static struct sbus_incoming_signal * sbus_new_incoming_signal(struct sbus_connection *conn, DBusMessage *message) { struct sbus_incoming_signal *a_signal; a_signal = talloc_zero(conn, struct sbus_incoming_signal); if (a_signal == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory allocating D-Bus signal\n"); return NULL; } a_signal->conn = conn; a_signal->message = dbus_message_ref(message); a_signal->interface = dbus_message_get_interface(message); a_signal->signal = dbus_message_get_member(message); a_signal->path = dbus_message_get_path(message); talloc_set_destructor(a_signal, sbus_incoming_signal_destructor); return a_signal; } struct sbus_incoming_signal_data { sbus_incoming_signal_fn handler_fn; void *handler_data; }; errno_t sbus_incoming_signal_hash_init(TALLOC_CTX *mem_ctx, hash_table_t **_table) { return sss_hash_create(mem_ctx, 10, _table); } static errno_t sbus_incoming_signal_hash_add(hash_table_t *table, const char *iface, const char *a_signal, sbus_incoming_signal_fn handler_fn, void *handler_data) { TALLOC_CTX *tmp_ctx; struct sbus_incoming_signal_data *data; hash_key_t key; hash_value_t value; errno_t ret; bool has_key; int hret; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { return ENOMEM; } key.type = HASH_KEY_STRING; key.str = talloc_asprintf(tmp_ctx, "%s.%s", iface, a_signal); if (key.str == NULL) { ret = ENOMEM; goto done; } has_key = hash_has_key(table, &key); if (has_key) { ret = EEXIST; goto done; } data = talloc_zero(tmp_ctx, struct sbus_incoming_signal_data); if (data == NULL) { ret = ENOMEM; goto done; } data->handler_data = handler_data; data->handler_fn = handler_fn; value.type = HASH_VALUE_PTR; value.ptr = data; hret = hash_enter(table, &key, &value); if (hret != HASH_SUCCESS) { ret = EIO; goto done; } talloc_steal(table, key.str); talloc_steal(table, data); ret = EOK; done: talloc_free(tmp_ctx); return ret; } static struct sbus_incoming_signal_data * sbus_incoming_signal_hash_lookup(hash_table_t *table, const char *iface, const char *a_signal) { struct sbus_incoming_signal_data *data; hash_key_t key; hash_value_t value; int hret; key.type = HASH_KEY_STRING; key.str = talloc_asprintf(NULL, "%s.%s", iface, a_signal); if (key.str == NULL) { return NULL; } hret = hash_lookup(table, &key, &value); if (hret == HASH_ERROR_KEY_NOT_FOUND) { data = NULL; goto done; } else if (hret != HASH_SUCCESS) { DEBUG(SSSDBG_OP_FAILURE, "Unable to search hash table: hret=%d\n", hret); data = NULL; goto done; } data = talloc_get_type(value.ptr, struct sbus_incoming_signal_data); done: talloc_free(key.str); return data; } errno_t sbus_signal_listen(struct sbus_connection *conn, const char *iface, const char *a_signal, sbus_incoming_signal_fn handler_fn, void *handler_data) { TALLOC_CTX *tmp_ctx; const char *rule; DBusError error; errno_t ret; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n"); return ENOMEM; } dbus_error_init(&error); ret = sbus_incoming_signal_hash_add(conn->incoming_signals, iface, a_signal, handler_fn, handler_data); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register signal handler " "[%d]: %s\n", ret, sss_strerror(ret)); goto done; } rule = talloc_asprintf(tmp_ctx, "type='signal',interface='%s',member='%s'", iface, a_signal); if (rule == NULL) { ret = ENOMEM; goto done; } dbus_bus_add_match(conn->dbus.conn, rule, &error); if (dbus_error_is_set(&error)) { DEBUG(SSSDBG_CRIT_FAILURE, "Cannot add D-Bus match rule, cause: %s\n", error.message); ret = EIO; goto done; } DEBUG(SSSDBG_TRACE_FUNC, "Listening to signal %s.%s\n", iface, a_signal); done: dbus_error_free(&error); talloc_free(tmp_ctx); return ret; } static void sbus_signal_handler_got_caller_id(struct tevent_req *req); DBusHandlerResult sbus_signal_handler(DBusConnection *dbus_conn, DBusMessage *message, void *handler_data) { struct tevent_req *req; struct sbus_connection *conn; struct sbus_incoming_signal *a_signal; const char *sender; int type; type = dbus_message_get_type(message); if (type != DBUS_MESSAGE_TYPE_SIGNAL) { /* We ignore other types here. */ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } conn = talloc_get_type(handler_data, struct sbus_connection); sender = dbus_message_get_sender(message); /* we have a valid handler, create D-Bus request */ a_signal = sbus_new_incoming_signal(conn, message); if (a_signal == NULL) { return DBUS_HANDLER_RESULT_NEED_MEMORY; } DEBUG(SSSDBG_TRACE_INTERNAL, "Received D-Bus signal %s.%s\n", a_signal->interface, a_signal->signal); /* now get the sender ID */ req = sbus_get_sender_id_send(a_signal, conn->ev, conn, sender); if (req == NULL) { talloc_free(a_signal); return DBUS_HANDLER_RESULT_NEED_MEMORY; } tevent_req_set_callback(req, sbus_signal_handler_got_caller_id, a_signal); return DBUS_HANDLER_RESULT_HANDLED; } static void sbus_signal_handler_got_caller_id(struct tevent_req *req) { struct sbus_incoming_signal_data *signal_data; struct sbus_incoming_signal *a_signal; errno_t ret; a_signal = tevent_req_callback_data(req, struct sbus_incoming_signal); ret = sbus_get_sender_id_recv(req, &a_signal->client); if (ret == ERR_SBUS_SENDER_BUS) { DEBUG(SSSDBG_TRACE_FUNC, "Got a signal from the bus..\n"); } else if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to resolve caller's ID: %s\n", sss_strerror(ret)); goto done; } signal_data = sbus_incoming_signal_hash_lookup( a_signal->conn->incoming_signals, a_signal->interface, a_signal->signal); if (signal_data == NULL) { DEBUG(SSSDBG_MINOR_FAILURE, "Received signal %s.%s that we are " "not listening to.\n", a_signal->interface, a_signal->signal); goto done; } signal_data->handler_fn(a_signal, signal_data->handler_data); done: talloc_free(a_signal); }