diff options
Diffstat (limited to 'ipc/kdbus/notify.c')
-rw-r--r-- | ipc/kdbus/notify.c | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/ipc/kdbus/notify.c b/ipc/kdbus/notify.c new file mode 100644 index 000000000..375758c48 --- /dev/null +++ b/ipc/kdbus/notify.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2013-2015 Kay Sievers + * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> + * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> + * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> + * Copyright (C) 2013-2015 Linux Foundation + * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> + * + * kdbus is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + */ + +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include "bus.h" +#include "connection.h" +#include "domain.h" +#include "endpoint.h" +#include "item.h" +#include "message.h" +#include "notify.h" + +static inline void kdbus_notify_add_tail(struct kdbus_staging *staging, + struct kdbus_bus *bus) +{ + spin_lock(&bus->notify_lock); + list_add_tail(&staging->notify_entry, &bus->notify_list); + spin_unlock(&bus->notify_lock); +} + +static int kdbus_notify_reply(struct kdbus_bus *bus, u64 id, + u64 cookie, u64 msg_type) +{ + struct kdbus_staging *s; + + s = kdbus_staging_new_kernel(bus, id, cookie, 0, msg_type); + if (IS_ERR(s)) + return PTR_ERR(s); + + kdbus_notify_add_tail(s, bus); + return 0; +} + +/** + * kdbus_notify_reply_timeout() - queue a timeout reply + * @bus: Bus which queues the messages + * @id: The destination's connection ID + * @cookie: The cookie to set in the reply. + * + * Queues a message that has a KDBUS_ITEM_REPLY_TIMEOUT item attached. + * + * Return: 0 on success, negative errno on failure. + */ +int kdbus_notify_reply_timeout(struct kdbus_bus *bus, u64 id, u64 cookie) +{ + return kdbus_notify_reply(bus, id, cookie, KDBUS_ITEM_REPLY_TIMEOUT); +} + +/** + * kdbus_notify_reply_dead() - queue a 'dead' reply + * @bus: Bus which queues the messages + * @id: The destination's connection ID + * @cookie: The cookie to set in the reply. + * + * Queues a message that has a KDBUS_ITEM_REPLY_DEAD item attached. + * + * Return: 0 on success, negative errno on failure. + */ +int kdbus_notify_reply_dead(struct kdbus_bus *bus, u64 id, u64 cookie) +{ + return kdbus_notify_reply(bus, id, cookie, KDBUS_ITEM_REPLY_DEAD); +} + +/** + * kdbus_notify_name_change() - queue a notification about a name owner change + * @bus: Bus which queues the messages + * @type: The type if the notification; KDBUS_ITEM_NAME_ADD, + * KDBUS_ITEM_NAME_CHANGE or KDBUS_ITEM_NAME_REMOVE + * @old_id: The id of the connection that used to own the name + * @new_id: The id of the new owner connection + * @old_flags: The flags to pass in the KDBUS_ITEM flags field for + * the old owner + * @new_flags: The flags to pass in the KDBUS_ITEM flags field for + * the new owner + * @name: The name that was removed or assigned to a new owner + * + * Return: 0 on success, negative errno on failure. + */ +int kdbus_notify_name_change(struct kdbus_bus *bus, u64 type, + u64 old_id, u64 new_id, + u64 old_flags, u64 new_flags, + const char *name) +{ + size_t name_len, extra_size; + struct kdbus_staging *s; + + name_len = strlen(name) + 1; + extra_size = sizeof(struct kdbus_notify_name_change) + name_len; + + s = kdbus_staging_new_kernel(bus, KDBUS_DST_ID_BROADCAST, 0, + extra_size, type); + if (IS_ERR(s)) + return PTR_ERR(s); + + s->notify->name_change.old_id.id = old_id; + s->notify->name_change.old_id.flags = old_flags; + s->notify->name_change.new_id.id = new_id; + s->notify->name_change.new_id.flags = new_flags; + memcpy(s->notify->name_change.name, name, name_len); + + kdbus_notify_add_tail(s, bus); + return 0; +} + +/** + * kdbus_notify_id_change() - queue a notification about a unique ID change + * @bus: Bus which queues the messages + * @type: The type if the notification; KDBUS_ITEM_ID_ADD or + * KDBUS_ITEM_ID_REMOVE + * @id: The id of the connection that was added or removed + * @flags: The flags to pass in the KDBUS_ITEM flags field + * + * Return: 0 on success, negative errno on failure. + */ +int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags) +{ + struct kdbus_staging *s; + size_t extra_size; + + extra_size = sizeof(struct kdbus_notify_id_change); + s = kdbus_staging_new_kernel(bus, KDBUS_DST_ID_BROADCAST, 0, + extra_size, type); + if (IS_ERR(s)) + return PTR_ERR(s); + + s->notify->id_change.id = id; + s->notify->id_change.flags = flags; + + kdbus_notify_add_tail(s, bus); + return 0; +} + +/** + * kdbus_notify_flush() - send a list of collected messages + * @bus: Bus which queues the messages + * + * The list is empty after sending the messages. + */ +void kdbus_notify_flush(struct kdbus_bus *bus) +{ + LIST_HEAD(notify_list); + struct kdbus_staging *s, *tmp; + + mutex_lock(&bus->notify_flush_lock); + down_read(&bus->name_registry->rwlock); + + spin_lock(&bus->notify_lock); + list_splice_init(&bus->notify_list, ¬ify_list); + spin_unlock(&bus->notify_lock); + + list_for_each_entry_safe(s, tmp, ¬ify_list, notify_entry) { + if (s->msg->dst_id != KDBUS_DST_ID_BROADCAST) { + struct kdbus_conn *conn; + + conn = kdbus_bus_find_conn_by_id(bus, s->msg->dst_id); + if (conn) { + kdbus_bus_eavesdrop(bus, NULL, s); + kdbus_conn_entry_insert(NULL, conn, s, NULL, + NULL); + kdbus_conn_unref(conn); + } + } else { + kdbus_bus_broadcast(bus, NULL, s); + } + + list_del(&s->notify_entry); + kdbus_staging_free(s); + } + + up_read(&bus->name_registry->rwlock); + mutex_unlock(&bus->notify_flush_lock); +} + +/** + * kdbus_notify_free() - free a list of collected messages + * @bus: Bus which queues the messages + */ +void kdbus_notify_free(struct kdbus_bus *bus) +{ + struct kdbus_staging *s, *tmp; + + list_for_each_entry_safe(s, tmp, &bus->notify_list, notify_entry) { + list_del(&s->notify_entry); + kdbus_staging_free(s); + } +} |