summaryrefslogtreecommitdiffstats
path: root/src/sbus/connection/sbus_send.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sbus/connection/sbus_send.c')
-rw-r--r--src/sbus/connection/sbus_send.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/src/sbus/connection/sbus_send.c b/src/sbus/connection/sbus_send.c
new file mode 100644
index 0000000..407ea0f
--- /dev/null
+++ b/src/sbus/connection/sbus_send.c
@@ -0,0 +1,245 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+ Stephen Gallagher <sgallagh@redhat.com>
+ Simo Sorce <ssorce@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 <tevent.h>
+#include <talloc.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sbus_private.h"
+
+static errno_t
+sbus_pending_call_create(DBusConnection *conn,
+ DBusMessage *msg,
+ DBusPendingCallNotifyFunction notify_fn,
+ void *notify_data,
+ int timeout_ms,
+ DBusPendingCall **_pending_call)
+{
+ DBusPendingCall *pending;
+ dbus_bool_t dbret;
+
+ dbret = dbus_connection_send_with_reply(conn, msg, &pending, timeout_ms);
+ if (!dbret) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to create pending call!\n");
+ return ENOMEM;
+ }
+
+ if (pending == NULL) {
+ /* Connection may have been disconnected. */
+ return ERR_OFFLINE;
+ }
+
+ dbret = dbus_pending_call_set_notify(pending, notify_fn, notify_data, NULL);
+ if (!dbret) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Unable to setup callback for pending reply!\n");
+ dbus_pending_call_cancel(pending);
+ dbus_pending_call_unref(pending);
+ return ENOMEM;
+ }
+
+ *_pending_call = pending;
+
+ return EOK;
+}
+
+static errno_t
+sbus_pending_call_process(DBusPendingCall *pending,
+ DBusMessage **_reply)
+{
+ DBusMessage *reply;
+ DBusError error;
+ errno_t ret;
+
+ dbus_error_init(&error);
+
+ reply = dbus_pending_call_steal_reply(pending);
+ if (reply == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Severe error. A reply callback was "
+ "called but no reply was received and no timeout occurred\n");
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = sbus_reply_check(reply);
+ if (ret != EOK) {
+ dbus_message_unref(reply);
+ goto done;
+ }
+
+ *_reply = reply;
+
+done:
+ dbus_pending_call_unref(pending);
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+struct sbus_message_state {
+ DBusPendingCall *pending;
+ DBusMessage *reply;
+};
+
+static int sbus_message_destructor(struct sbus_message_state *state)
+{
+ if (state->pending == NULL) {
+ return 0;
+ }
+
+ /* This request was interrupted before the pending call has finished. */
+ dbus_pending_call_cancel(state->pending);
+ dbus_pending_call_unref(state->pending);
+ state->pending = NULL;
+
+ return 0;
+}
+
+static void sbus_message_done(DBusPendingCall *pending,
+ void *pvt);
+
+struct tevent_req *
+sbus_message_send(TALLOC_CTX *mem_ctx,
+ struct sbus_connection *conn,
+ DBusMessage *msg,
+ int timeout_ms)
+{
+ struct sbus_message_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sbus_message_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ return NULL;
+ }
+
+ if (conn->disconnecting) {
+ ret = ERR_TERMINATED;
+ goto done;
+ }
+
+ ret = sbus_pending_call_create(conn->connection, msg, sbus_message_done,
+ req, timeout_ms, &state->pending);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ talloc_set_destructor(state, sbus_message_destructor);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, conn->ev);
+ }
+
+ return req;
+}
+
+static void
+sbus_message_done(DBusPendingCall *pending,
+ void *pvt)
+{
+ struct sbus_message_state *state;
+ struct tevent_req *req;
+ errno_t ret;
+
+ req = talloc_get_type(pvt, struct tevent_req);
+ state = tevent_req_data(req, struct sbus_message_state);
+
+ /* The pending call has finished. Remove the destructor. */
+ state->pending = NULL;
+ talloc_set_destructor(state, NULL);
+
+ ret = sbus_pending_call_process(pending, &state->reply);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+errno_t
+sbus_message_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ DBusMessage **_reply)
+{
+ struct sbus_message_state *state;
+ errno_t ret;
+
+ state = tevent_req_data(req, struct sbus_message_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ ret = sbus_message_bound(mem_ctx, state->reply);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ *_reply = state->reply;
+
+ return EOK;
+}
+
+void sbus_reply(struct sbus_connection *conn,
+ DBusMessage *reply)
+{
+ if (conn->disconnecting) {
+ dbus_message_unref(reply);
+ return;
+ }
+
+ dbus_connection_send(conn->connection, reply, NULL);
+ dbus_message_unref(reply);
+}
+
+void sbus_reply_error(struct sbus_connection *conn,
+ DBusMessage *reply_to,
+ const char *error_name,
+ const char *error_message)
+{
+ DBusMessage *reply;
+
+ /* We can|t really send reply to a signal. */
+ if (dbus_message_get_type(reply_to) == DBUS_MESSAGE_TYPE_SIGNAL) {
+ return;
+ }
+
+ reply = dbus_message_new_error(reply_to, error_name, error_message);
+ if (reply == NULL) {
+ /* There is really nothing we can do. */
+ return;
+ }
+
+ sbus_reply(conn, reply);
+}
+
+void sbus_emit_signal(struct sbus_connection *conn,
+ DBusMessage *msg)
+{
+ sbus_reply(conn, msg);
+}