linux-api.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: arnd@arndb.de, ebiederm@xmission.com, gnomes@lxorguk.ukuu.org.uk,
	teg@jklm.no, jkosina@suse.cz, luto@amacapital.net,
	linux-api@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: daniel@zonque.or, dh.herrmann@gmail.com, tixxdz@opendz.org,
	Daniel Mack <daniel@zonque.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Subject: [PATCH 10/13] kdbus: add name registry implementation
Date: Fri, 16 Jan 2015 11:16:14 -0800	[thread overview]
Message-ID: <1421435777-25306-11-git-send-email-gregkh@linuxfoundation.org> (raw)
In-Reply-To: <1421435777-25306-1-git-send-email-gregkh@linuxfoundation.org>

From: Daniel Mack <daniel@zonque.org>

This patch adds the name registry implementation.

Each bus instantiates a name registry to resolve well-known names
into unique connection IDs for message delivery. The registry will
be queried when a message is sent with kdbus_msg.dst_id set to
KDBUS_DST_ID_NAME, or when a registry dump is requested.

It's important to have this registry implemented in the kernel to
implement lookups and take-overs in a race-free way.

Signed-off-by: Daniel Mack <daniel@zonque.org>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
 ipc/kdbus/names.c | 891 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/names.h |  82 +++++
 2 files changed, 973 insertions(+)
 create mode 100644 ipc/kdbus/names.c
 create mode 100644 ipc/kdbus/names.h

diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c
new file mode 100644
index 000000000000..f8a69c249e17
--- /dev/null
+++ b/ipc/kdbus/names.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright (C) 2013-2014 Kay Sievers
+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org>
+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
+ * Copyright (C) 2013-2014 Linux Foundation
+ * Copyright (C) 2014 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/ctype.h>
+#include <linux/fs.h>
+#include <linux/hash.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "item.h"
+#include "names.h"
+#include "notify.h"
+#include "policy.h"
+
+/**
+ * struct kdbus_name_queue_item - a queue item for a name
+ * @conn:		The associated connection
+ * @entry:		Name entry queuing up for
+ * @entry_entry:	List element for the list in @entry
+ * @conn_entry:		List element for the list in @conn
+ * @flags:		The queuing flags
+ */
+struct kdbus_name_queue_item {
+	struct kdbus_conn *conn;
+	struct kdbus_name_entry *entry;
+	struct list_head entry_entry;
+	struct list_head conn_entry;
+	u64 flags;
+};
+
+static void kdbus_name_entry_free(struct kdbus_name_entry *e)
+{
+	hash_del(&e->hentry);
+	kfree(e->name);
+	kfree(e);
+}
+
+/**
+ * kdbus_name_registry_free() - drop a name reg's reference
+ * @reg:		The name registry, may be %NULL
+ *
+ * Cleanup the name registry's internal structures.
+ */
+void kdbus_name_registry_free(struct kdbus_name_registry *reg)
+{
+	struct kdbus_name_entry *e;
+	struct hlist_node *tmp;
+	unsigned int i;
+
+	if (!reg)
+		return;
+
+	hash_for_each_safe(reg->entries_hash, i, tmp, e, hentry)
+		kdbus_name_entry_free(e);
+
+	kfree(reg);
+}
+
+/**
+ * kdbus_name_registry_new() - create a new name registry
+ *
+ * Return: a new kdbus_name_registry on success, ERR_PTR on failure.
+ */
+struct kdbus_name_registry *kdbus_name_registry_new(void)
+{
+	struct kdbus_name_registry *r;
+
+	r = kzalloc(sizeof(*r), GFP_KERNEL);
+	if (!r)
+		return ERR_PTR(-ENOMEM);
+
+	hash_init(r->entries_hash);
+	init_rwsem(&r->rwlock);
+
+	return r;
+}
+
+static struct kdbus_name_entry *
+kdbus_name_lookup(struct kdbus_name_registry *reg, u32 hash, const char *name)
+{
+	struct kdbus_name_entry *e;
+
+	hash_for_each_possible(reg->entries_hash, e, hentry, hash)
+		if (strcmp(e->name, name) == 0)
+			return e;
+
+	return NULL;
+}
+
+static void kdbus_name_queue_item_free(struct kdbus_name_queue_item *q)
+{
+	list_del(&q->entry_entry);
+	list_del(&q->conn_entry);
+	kfree(q);
+}
+
+/*
+ * The caller must hold the lock so we decrement the counter and
+ * delete the entry.
+ *
+ * The caller needs to hold its own reference, so the connection does not go
+ * away while the entry's reference is dropped under lock.
+ */
+static void kdbus_name_entry_remove_owner(struct kdbus_name_entry *e)
+{
+	if (WARN_ON(!e->conn))
+		return;
+
+	if (WARN_ON(!mutex_is_locked(&e->conn->lock)))
+		return;
+
+	atomic_dec(&e->conn->name_count);
+	list_del(&e->conn_entry);
+	e->conn = kdbus_conn_unref(e->conn);
+}
+
+static void kdbus_name_entry_set_owner(struct kdbus_name_entry *e,
+				       struct kdbus_conn *conn)
+{
+	if (WARN_ON(e->conn))
+		return;
+
+	if (WARN_ON(!mutex_is_locked(&conn->lock)))
+		return;
+
+	e->conn = kdbus_conn_ref(conn);
+	atomic_inc(&conn->name_count);
+	list_add_tail(&e->conn_entry, &e->conn->names_list);
+}
+
+static int kdbus_name_replace_owner(struct kdbus_name_entry *e,
+				    struct kdbus_conn *conn, u64 flags)
+{
+	struct kdbus_conn *conn_old = kdbus_conn_ref(e->conn);
+	int ret = 0;
+
+	if (WARN_ON(conn == conn_old))
+		return -EALREADY;
+
+	if (WARN_ON(!conn_old))
+		return -EINVAL;
+
+	kdbus_conn_lock2(conn, conn_old);
+
+	if (!kdbus_conn_active(conn)) {
+		ret = -ECONNRESET;
+		goto exit_unlock;
+	}
+
+	kdbus_notify_name_change(conn->ep->bus, KDBUS_ITEM_NAME_CHANGE,
+				 e->conn->id, conn->id,
+				 e->flags, flags, e->name);
+
+	/* hand over name ownership */
+	kdbus_name_entry_remove_owner(e);
+	kdbus_name_entry_set_owner(e, conn);
+	e->flags = flags;
+
+exit_unlock:
+	kdbus_conn_unlock2(conn, conn_old);
+	kdbus_conn_unref(conn_old);
+	return ret;
+}
+
+static int kdbus_name_entry_release(struct kdbus_name_entry *e)
+{
+	struct kdbus_conn *conn;
+	int ret;
+
+	/* give it to first active waiter in the queue */
+	while (!list_empty(&e->queue_list)) {
+		struct kdbus_name_queue_item *q;
+
+		q = list_first_entry(&e->queue_list,
+				     struct kdbus_name_queue_item,
+				     entry_entry);
+
+		ret = kdbus_name_replace_owner(e, q->conn, q->flags);
+		if (ret < 0)
+			continue;
+
+		kdbus_name_queue_item_free(q);
+		return 0;
+	}
+
+	/* hand it back to an active activator connection */
+	if (e->activator && e->activator != e->conn) {
+		u64 flags = KDBUS_NAME_ACTIVATOR;
+
+		/*
+		 * Move messages still queued in the old connection
+		 * and addressed to that name to the new connection.
+		 * This allows a race and loss-free name and message
+		 * takeover and exit-on-idle services.
+		 */
+		ret = kdbus_conn_move_messages(e->activator, e->conn,
+					       e->name_id);
+		if (ret < 0)
+			return ret;
+
+		return kdbus_name_replace_owner(e, e->activator, flags);
+	}
+
+	/* release the name */
+	kdbus_notify_name_change(e->conn->ep->bus, KDBUS_ITEM_NAME_REMOVE,
+				 e->conn->id, 0, e->flags, 0, e->name);
+
+	conn = kdbus_conn_ref(e->conn);
+	mutex_lock(&conn->lock);
+	kdbus_name_entry_remove_owner(e);
+	mutex_unlock(&conn->lock);
+	kdbus_conn_unref(conn);
+
+	kdbus_name_entry_free(e);
+
+	return 0;
+}
+
+static int kdbus_name_release(struct kdbus_name_registry *reg,
+			      struct kdbus_conn *conn,
+			      const char *name)
+{
+	struct kdbus_name_queue_item *tmp, *q;
+	struct kdbus_name_entry *e = NULL;
+	int ret = -ESRCH;
+	u32 hash;
+
+	hash = kdbus_strhash(name);
+
+	/* lock order: domain -> bus -> ep -> names -> connection */
+	mutex_lock(&conn->ep->bus->lock);
+	down_write(&reg->rwlock);
+
+	e = kdbus_name_lookup(reg, hash, name);
+	if (!e)
+		goto exit_unlock;
+
+	/* Is the connection already the real owner of the name? */
+	if (e->conn == conn) {
+		ret = kdbus_name_entry_release(e);
+	} else {
+		/*
+		 * Otherwise, walk the list of queued entries and search
+		 * for items for connection.
+		 */
+
+		list_for_each_entry_safe(q, tmp, &e->queue_list, entry_entry) {
+			if (q->conn != conn)
+				continue;
+
+			kdbus_name_queue_item_free(q);
+			ret = 0;
+			break;
+		}
+	}
+
+exit_unlock:
+	up_write(&reg->rwlock);
+	mutex_unlock(&conn->ep->bus->lock);
+
+	return ret;
+}
+
+/**
+ * kdbus_name_remove_by_conn() - remove all name entries of a given connection
+ * @reg:		The name registry
+ * @conn:		The connection which entries to remove
+ *
+ * This function removes all name entry held by a given connection.
+ */
+void kdbus_name_remove_by_conn(struct kdbus_name_registry *reg,
+			       struct kdbus_conn *conn)
+{
+	struct kdbus_name_queue_item *q_tmp, *q;
+	struct kdbus_conn *activator = NULL;
+	struct kdbus_name_entry *e_tmp, *e;
+	LIST_HEAD(names_queue_list);
+	LIST_HEAD(names_list);
+
+	/* lock order: domain -> bus -> ep -> names -> conn */
+	mutex_lock(&conn->ep->bus->lock);
+	down_write(&reg->rwlock);
+
+	mutex_lock(&conn->lock);
+	list_splice_init(&conn->names_list, &names_list);
+	list_splice_init(&conn->names_queue_list, &names_queue_list);
+	mutex_unlock(&conn->lock);
+
+	if (kdbus_conn_is_activator(conn)) {
+		activator = conn->activator_of->activator;
+		conn->activator_of->activator = NULL;
+	}
+	list_for_each_entry_safe(q, q_tmp, &names_queue_list, conn_entry)
+		kdbus_name_queue_item_free(q);
+	list_for_each_entry_safe(e, e_tmp, &names_list, conn_entry)
+		kdbus_name_entry_release(e);
+
+	up_write(&reg->rwlock);
+	mutex_unlock(&conn->ep->bus->lock);
+
+	kdbus_conn_unref(activator);
+	kdbus_notify_flush(conn->ep->bus);
+}
+
+/**
+ * kdbus_name_lock() - look up a name in a name registry and lock it
+ * @reg:		The name registry
+ * @name:		The name to look up
+ *
+ * Search for a name in a given name registry and return it with the
+ * registry-lock held. If the object is not found, the lock is not acquired and
+ * NULL is returned. The caller is responsible of unlocking the name via
+ * kdbus_name_unlock() again. Note that kdbus_name_unlock() can be safely called
+ * with NULL as name. In this case, it's a no-op as nothing was locked.
+ *
+ * The *_lock() + *_unlock() logic is only required for callers that need to
+ * protect their code against concurrent activator/implementer name changes.
+ * Multiple readers can lock names concurrently. However, you may not change
+ * name-ownership while holding a name-lock.
+ *
+ * Return: NULL if name is unknown, otherwise return a pointer to the name
+ *         entry with the name-lock held (reader lock only).
+ */
+struct kdbus_name_entry *kdbus_name_lock(struct kdbus_name_registry *reg,
+					 const char *name)
+{
+	struct kdbus_name_entry *e = NULL;
+	u32 hash = kdbus_strhash(name);
+
+	down_read(&reg->rwlock);
+	e = kdbus_name_lookup(reg, hash, name);
+	if (e)
+		return e;
+	up_read(&reg->rwlock);
+
+	return NULL;
+}
+
+/**
+ * kdbus_name_unlock() - unlock one name in a name registry
+ * @reg:		The name registry
+ * @entry:		The locked name entry or NULL
+ *
+ * This is the unlock-counterpart of kdbus_name_lock(). It unlocks a name that
+ * was previously successfully locked. You can safely pass NULL as entry and
+ * this will become a no-op. Therefore, it's safe to always call this on the
+ * return-value of kdbus_name_lock().
+ *
+ * Return: This always returns NULL.
+ */
+struct kdbus_name_entry *kdbus_name_unlock(struct kdbus_name_registry *reg,
+					   struct kdbus_name_entry *entry)
+{
+	if (entry) {
+		BUG_ON(!rwsem_is_locked(&reg->rwlock));
+		up_read(&reg->rwlock);
+	}
+
+	return NULL;
+}
+
+static int kdbus_name_queue_conn(struct kdbus_conn *conn, u64 flags,
+				 struct kdbus_name_entry *e)
+{
+	struct kdbus_name_queue_item *q;
+
+	q = kzalloc(sizeof(*q), GFP_KERNEL);
+	if (!q)
+		return -ENOMEM;
+
+	q->conn = conn;
+	q->flags = flags;
+	q->entry = e;
+
+	list_add_tail(&q->entry_entry, &e->queue_list);
+	list_add_tail(&q->conn_entry, &conn->names_queue_list);
+
+	return 0;
+}
+
+/**
+ * kdbus_name_is_valid() - check if a name is valid
+ * @p:			The name to check
+ * @allow_wildcard:	Whether or not to allow a wildcard name
+ *
+ * A name is valid if all of the following criterias are met:
+ *
+ *  - The name has two or more elements separated by a period ('.') character.
+ *  - All elements must contain at least one character.
+ *  - Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_-"
+ *    and must not begin with a digit.
+ *  - The name must not exceed KDBUS_NAME_MAX_LEN.
+ *  - If @allow_wildcard is true, the name may end on '.*'
+ */
+bool kdbus_name_is_valid(const char *p, bool allow_wildcard)
+{
+	bool dot, found_dot = false;
+	const char *q;
+
+	for (dot = true, q = p; *q; q++) {
+		if (*q == '.') {
+			if (dot)
+				return false;
+
+			found_dot = true;
+			dot = true;
+		} else {
+			bool good;
+
+			good = isalpha(*q) || (!dot && isdigit(*q)) ||
+				*q == '_' || *q == '-' ||
+				(allow_wildcard && dot &&
+					*q == '*' && *(q + 1) == '\0');
+
+			if (!good)
+				return false;
+
+			dot = false;
+		}
+	}
+
+	if (q - p > KDBUS_NAME_MAX_LEN)
+		return false;
+
+	if (dot)
+		return false;
+
+	if (!found_dot)
+		return false;
+
+	return true;
+}
+
+/**
+ * kdbus_name_acquire() - acquire a name
+ * @reg:		The name registry
+ * @conn:		The connection to pin this entry to
+ * @name:		The name to acquire
+ * @flags:		Acquisition flags (KDBUS_NAME_*)
+ *
+ * Callers must ensure that @conn is either a privileged bus user or has
+ * sufficient privileges in the policy-db to own the well-known name @name.
+ *
+ * Return: 0 success, negative error number on failure.
+ */
+int kdbus_name_acquire(struct kdbus_name_registry *reg,
+		       struct kdbus_conn *conn,
+		       const char *name, u64 *flags)
+{
+	struct kdbus_name_entry *e = NULL;
+	int ret = 0;
+	u32 hash;
+
+	/* lock order: domain -> bus -> ep -> names -> conn */
+	mutex_lock(&conn->ep->bus->lock);
+	down_write(&reg->rwlock);
+
+	hash = kdbus_strhash(name);
+	e = kdbus_name_lookup(reg, hash, name);
+	if (e) {
+		/* connection already owns that name */
+		if (e->conn == conn) {
+			ret = -EALREADY;
+			goto exit_unlock;
+		}
+
+		if (kdbus_conn_is_activator(conn)) {
+			/* An activator can only own a single name */
+			if (conn->activator_of) {
+				if (conn->activator_of == e)
+					ret = -EALREADY;
+				else
+					ret = -EINVAL;
+			} else if (!e->activator && !conn->activator_of) {
+				/*
+				 * Activator registers for name that is
+				 * already owned
+				 */
+				e->activator = kdbus_conn_ref(conn);
+				conn->activator_of = e;
+			}
+
+			goto exit_unlock;
+		}
+
+		/* take over the name of an activator connection */
+		if (e->flags & KDBUS_NAME_ACTIVATOR) {
+			/*
+			 * Take over the messages queued in the activator
+			 * connection, the activator itself never reads them.
+			 */
+			ret = kdbus_conn_move_messages(conn, e->activator, 0);
+			if (ret < 0)
+				goto exit_unlock;
+
+			ret = kdbus_name_replace_owner(e, conn, *flags);
+			goto exit_unlock;
+		}
+
+		/* take over the name if both parties agree */
+		if ((*flags & KDBUS_NAME_REPLACE_EXISTING) &&
+		    (e->flags & KDBUS_NAME_ALLOW_REPLACEMENT)) {
+			/*
+			 * Move name back to the queue, in case we take it away
+			 * from a connection which asked for queuing.
+			 */
+			if (e->flags & KDBUS_NAME_QUEUE) {
+				ret = kdbus_name_queue_conn(e->conn,
+							    e->flags, e);
+				if (ret < 0)
+					goto exit_unlock;
+			}
+
+			ret = kdbus_name_replace_owner(e, conn, *flags);
+			goto exit_unlock;
+		}
+
+		/* add it to the queue waiting for the name */
+		if (*flags & KDBUS_NAME_QUEUE) {
+			ret = kdbus_name_queue_conn(conn, *flags, e);
+			if (ret < 0)
+				goto exit_unlock;
+
+			/* tell the caller that we queued it */
+			*flags |= KDBUS_NAME_IN_QUEUE;
+
+			goto exit_unlock;
+		}
+
+		/* the name is busy, return a failure */
+		ret = -EEXIST;
+		goto exit_unlock;
+	} else {
+		/* An activator can only own a single name */
+		if (kdbus_conn_is_activator(conn) &&
+		    conn->activator_of) {
+			ret = -EINVAL;
+			goto exit_unlock;
+		}
+	}
+
+	/* new name entry */
+	e = kzalloc(sizeof(*e), GFP_KERNEL);
+	if (!e) {
+		ret = -ENOMEM;
+		goto exit_unlock;
+	}
+
+	e->name = kstrdup(name, GFP_KERNEL);
+	if (!e->name) {
+		kfree(e);
+		ret = -ENOMEM;
+		goto exit_unlock;
+	}
+
+	if (kdbus_conn_is_activator(conn)) {
+		e->activator = kdbus_conn_ref(conn);
+		conn->activator_of = e;
+	}
+
+	e->flags = *flags;
+	INIT_LIST_HEAD(&e->queue_list);
+	e->name_id = ++reg->name_seq_last;
+
+	mutex_lock(&conn->lock);
+	if (!kdbus_conn_active(conn)) {
+		mutex_unlock(&conn->lock);
+		kfree(e->name);
+		kfree(e);
+		ret = -ECONNRESET;
+		goto exit_unlock;
+	}
+	hash_add(reg->entries_hash, &e->hentry, hash);
+	kdbus_name_entry_set_owner(e, conn);
+	mutex_unlock(&conn->lock);
+
+	kdbus_notify_name_change(e->conn->ep->bus, KDBUS_ITEM_NAME_ADD,
+				 0, e->conn->id,
+				 0, e->flags, e->name);
+
+exit_unlock:
+	up_write(&reg->rwlock);
+	mutex_unlock(&conn->ep->bus->lock);
+	kdbus_notify_flush(conn->ep->bus);
+	return ret;
+}
+
+/**
+ * kdbus_cmd_name_acquire() - acquire a name from a ioctl command buffer
+ * @reg:		The name registry
+ * @conn:		The connection to pin this entry to
+ * @cmd:		The command as passed in by the ioctl
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int kdbus_cmd_name_acquire(struct kdbus_name_registry *reg,
+			   struct kdbus_conn *conn,
+			   struct kdbus_cmd_name *cmd)
+{
+	const char *name;
+	int ret;
+
+	name = kdbus_items_get_str(cmd->items, KDBUS_ITEMS_SIZE(cmd, items),
+				   KDBUS_ITEM_NAME);
+	if (IS_ERR(name))
+		return -EINVAL;
+
+	if (!kdbus_name_is_valid(name, false))
+		return -EINVAL;
+
+	/*
+	 * Do atomic_inc_return here to reserve our slot, then decrement
+	 * it before returning.
+	 */
+	if (atomic_inc_return(&conn->name_count) > KDBUS_CONN_MAX_NAMES) {
+		ret = -E2BIG;
+		goto out_dec;
+	}
+
+	if (!kdbus_conn_policy_own_name(conn, current_cred(), name)) {
+		ret = -EPERM;
+		goto out_dec;
+	}
+
+	ret = kdbus_name_acquire(reg, conn, name, &cmd->flags);
+
+out_dec:
+	/* Decrement the previous allocated slot */
+	atomic_dec(&conn->name_count);
+	return ret;
+}
+
+/**
+ * kdbus_cmd_name_release() - release a name entry from a ioctl command buffer
+ * @reg:		The name registry
+ * @conn:		The connection that holds the name
+ * @cmd:		The command as passed in by the ioctl
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int kdbus_cmd_name_release(struct kdbus_name_registry *reg,
+			   struct kdbus_conn *conn,
+			   const struct kdbus_cmd_name *cmd)
+{
+	int ret;
+	const char *name;
+
+	name = kdbus_items_get_str(cmd->items, KDBUS_ITEMS_SIZE(cmd, items),
+				   KDBUS_ITEM_NAME);
+	if (IS_ERR(name))
+		return -EINVAL;
+
+	if (!kdbus_name_is_valid(name, false))
+		return -EINVAL;
+
+	ret = kdbus_name_release(reg, conn, name);
+
+	kdbus_notify_flush(conn->ep->bus);
+	return ret;
+}
+
+static int kdbus_name_list_write(struct kdbus_conn *conn,
+				 struct kdbus_conn *c,
+				 struct kdbus_pool_slice *slice,
+				 size_t *pos,
+				 struct kdbus_name_entry *e,
+				 bool write)
+{
+	struct kvec kvec[4];
+	size_t cnt = 0;
+	int ret;
+
+	/* info header */
+	struct kdbus_name_info info = {
+		.size = 0,
+		.owner_id = c->id,
+		.conn_flags = c->flags,
+	};
+
+	/* fake the header of a kdbus_name item */
+	struct {
+		u64 size;
+		u64 type;
+		u64 flags;
+	} h = {};
+
+	if (e && !kdbus_conn_policy_see_name_unlocked(conn, current_cred(),
+						      e->name))
+		return 0;
+
+	kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &info.size);
+
+	/* append name */
+	if (e) {
+		size_t slen = strlen(e->name) + 1;
+
+		h.size = offsetof(struct kdbus_item, name.name) + slen;
+		h.type = KDBUS_ITEM_OWNED_NAME;
+		h.flags = e->flags;
+
+		kdbus_kvec_set(&kvec[cnt++], &h, sizeof(h), &info.size);
+		kdbus_kvec_set(&kvec[cnt++], e->name, slen, &info.size);
+		cnt += !!kdbus_kvec_pad(&kvec[cnt], &info.size);
+	}
+
+	if (write) {
+		ret = kdbus_pool_slice_copy_kvec(slice, *pos, kvec,
+						 cnt, info.size);
+		if (ret < 0)
+			return ret;
+	}
+
+	*pos += info.size;
+	return 0;
+}
+
+static int kdbus_name_list_all(struct kdbus_conn *conn, u64 flags,
+			       struct kdbus_pool_slice *slice,
+			       size_t *pos, bool write)
+{
+	struct kdbus_conn *c;
+	size_t p = *pos;
+	int ret, i;
+
+	hash_for_each(conn->ep->bus->conn_hash, i, c, hentry) {
+		bool added = false;
+
+		/* skip activators */
+		if (!(flags & KDBUS_NAME_LIST_ACTIVATORS) &&
+		    kdbus_conn_is_activator(c))
+			continue;
+
+		/* all names the connection owns */
+		if (flags & (KDBUS_NAME_LIST_NAMES |
+			     KDBUS_NAME_LIST_ACTIVATORS)) {
+			struct kdbus_name_entry *e;
+
+			mutex_lock(&c->lock);
+			list_for_each_entry(e, &c->names_list, conn_entry) {
+				struct kdbus_conn *a = e->activator;
+
+				if ((flags & KDBUS_NAME_LIST_ACTIVATORS) &&
+				    a && a != c) {
+					ret = kdbus_name_list_write(conn, a,
+							slice, &p, e, write);
+					if (ret < 0) {
+						mutex_unlock(&c->lock);
+						return ret;
+					}
+
+					added = true;
+				}
+
+				if (flags & KDBUS_NAME_LIST_NAMES ||
+				    kdbus_conn_is_activator(c)) {
+					ret = kdbus_name_list_write(conn, c,
+							slice, &p, e, write);
+					if (ret < 0) {
+						mutex_unlock(&c->lock);
+						return ret;
+					}
+
+					added = true;
+				}
+			}
+			mutex_unlock(&c->lock);
+		}
+
+		/* queue of names the connection is currently waiting for */
+		if (flags & KDBUS_NAME_LIST_QUEUED) {
+			struct kdbus_name_queue_item *q;
+
+			mutex_lock(&c->lock);
+			list_for_each_entry(q, &c->names_queue_list,
+					    conn_entry) {
+				ret = kdbus_name_list_write(conn, c,
+						slice, &p, q->entry, write);
+				if (ret < 0) {
+					mutex_unlock(&c->lock);
+					return ret;
+				}
+
+				added = true;
+			}
+			mutex_unlock(&c->lock);
+		}
+
+		/* nothing added so far, just add the unique ID */
+		if (!added && flags & KDBUS_NAME_LIST_UNIQUE) {
+			ret = kdbus_name_list_write(conn, c,
+					slice, &p, NULL, write);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	*pos = p;
+	return 0;
+}
+
+/**
+ * kdbus_cmd_name_list() - list names of a connection
+ * @reg:		The name registry
+ * @conn:		The connection holding the name entries
+ * @cmd:		The command as passed in by the ioctl
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int kdbus_cmd_name_list(struct kdbus_name_registry *reg,
+			struct kdbus_conn *conn,
+			struct kdbus_cmd_name_list *cmd)
+{
+	struct kdbus_pool_slice *slice = NULL;
+	struct kdbus_name_list list = {};
+	const struct kdbus_item *item;
+	struct kvec kvec;
+	size_t pos;
+	int ret;
+
+	KDBUS_ITEMS_FOREACH(item, cmd->items, KDBUS_ITEMS_SIZE(cmd, items)) {
+		/* no items supported so far */
+		switch (item->type) {
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* lock order: domain -> bus -> ep -> names -> conn */
+	down_read(&reg->rwlock);
+	down_read(&conn->ep->bus->conn_rwlock);
+	down_read(&conn->ep->policy_db.entries_rwlock);
+
+	/* size of header + records */
+	pos = sizeof(struct kdbus_name_list);
+	ret = kdbus_name_list_all(conn, cmd->flags, NULL, &pos, false);
+	if (ret < 0)
+		goto exit_unlock;
+
+	/* copy the header, specifying the overall size */
+	list.size = pos;
+	kvec.iov_base = &list;
+	kvec.iov_len = sizeof(list);
+
+	slice = kdbus_pool_slice_alloc(conn->pool, list.size, NULL, NULL, 0);
+	if (IS_ERR(slice)) {
+		ret = PTR_ERR(slice);
+		slice = NULL;
+		goto exit_unlock;
+	}
+
+	ret = kdbus_pool_slice_copy_kvec(slice, 0, &kvec, 1, kvec.iov_len);
+	if (ret < 0)
+		goto exit_unlock;
+
+	/* copy the records */
+	pos = sizeof(struct kdbus_name_list);
+	ret = kdbus_name_list_all(conn, cmd->flags, slice, &pos, true);
+	if (ret < 0)
+		goto exit_unlock;
+
+	kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->list_size);
+	ret = 0;
+
+exit_unlock:
+	kdbus_pool_slice_release(slice);
+	up_read(&conn->ep->policy_db.entries_rwlock);
+	up_read(&conn->ep->bus->conn_rwlock);
+	up_read(&reg->rwlock);
+	return ret;
+}
diff --git a/ipc/kdbus/names.h b/ipc/kdbus/names.h
new file mode 100644
index 000000000000..81a2e2ac49c1
--- /dev/null
+++ b/ipc/kdbus/names.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013-2014 Kay Sievers
+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org>
+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
+ * Copyright (C) 2013-2014 Linux Foundation
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __KDBUS_NAMES_H
+#define __KDBUS_NAMES_H
+
+#include <linux/hashtable.h>
+#include <linux/rwsem.h>
+
+/**
+ * struct kdbus_name_registry - names registered for a bus
+ * @entries_hash:	Map of entries
+ * @lock:		Registry data lock
+ * @name_seq_last:	Last used sequence number to assign to a name entry
+ */
+struct kdbus_name_registry {
+	DECLARE_HASHTABLE(entries_hash, 8);
+	struct rw_semaphore rwlock;
+	u64 name_seq_last;
+};
+
+/**
+ * struct kdbus_name_entry - well-know name entry
+ * @name:		The well-known name
+ * @name_id:		Sequence number of name entry to be able to uniquely
+ *			identify a name over its registration lifetime
+ * @flags:		KDBUS_NAME_* flags
+ * @queue_list:		List of queued waiters for the well-known name
+ * @conn_entry:		Entry in connection
+ * @hentry:		Entry in registry map
+ * @conn:		Connection owning the name
+ * @activator:		Connection of the activator queuing incoming messages
+ */
+struct kdbus_name_entry {
+	char *name;
+	u64 name_id;
+	u64 flags;
+	struct list_head queue_list;
+	struct list_head conn_entry;
+	struct hlist_node hentry;
+	struct kdbus_conn *conn;
+	struct kdbus_conn *activator;
+};
+
+struct kdbus_name_registry *kdbus_name_registry_new(void);
+void kdbus_name_registry_free(struct kdbus_name_registry *reg);
+
+int kdbus_name_acquire(struct kdbus_name_registry *reg,
+		       struct kdbus_conn *conn,
+		       const char *name, u64 *flags);
+int kdbus_cmd_name_acquire(struct kdbus_name_registry *reg,
+			   struct kdbus_conn *conn,
+			   struct kdbus_cmd_name *cmd);
+int kdbus_cmd_name_release(struct kdbus_name_registry *reg,
+			   struct kdbus_conn *conn,
+			   const struct kdbus_cmd_name *cmd);
+int kdbus_cmd_name_list(struct kdbus_name_registry *reg,
+			struct kdbus_conn *conn,
+			struct kdbus_cmd_name_list *cmd);
+
+struct kdbus_name_entry *kdbus_name_lock(struct kdbus_name_registry *reg,
+					 const char *name);
+struct kdbus_name_entry *kdbus_name_unlock(struct kdbus_name_registry *reg,
+					   struct kdbus_name_entry *entry);
+
+void kdbus_name_remove_by_conn(struct kdbus_name_registry *reg,
+			       struct kdbus_conn *conn);
+
+bool kdbus_name_is_valid(const char *p, bool allow_wildcard);
+
+#endif
-- 
2.2.1

  parent reply	other threads:[~2015-01-16 19:16 UTC|newest]

Thread overview: 86+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-01-16 19:16 [PATCH v3 00/13] Add kdbus implementation Greg Kroah-Hartman
2015-01-16 19:16 ` [PATCH 01/13] kdbus: add documentation Greg Kroah-Hartman
     [not found]   ` <1421435777-25306-2-git-send-email-gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
2015-01-20 13:53     ` Michael Kerrisk (man-pages)
     [not found]       ` <54BE5DC8.70706-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-20 14:31         ` David Herrmann
     [not found]           ` <CANq1E4SjfZOKqTsdkt519vKc1Poeah5McVJBb_spdHjbKv4=7g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-01-20 14:42             ` Josh Boyer
     [not found]               ` <CA+5PVA46wVa-L9K1zgHzt=Fwn6YFBYhxJUjiozSH4XBY03WMVw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-01-20 14:53                 ` Djalal Harouni
2015-01-20 16:08                   ` Johannes Stezenbach
     [not found]                     ` <20150120160842.GA9474-FF7aIK3TAVNeoWH0uzbU5w@public.gmane.org>
2015-01-20 17:00                       ` David Herrmann
     [not found]                         ` <CANq1E4Q9m7JRXNYFY4okh3brM-kHVoVGOdF_SWjNoNURX930HQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-01-20 22:00                           ` Johannes Stezenbach
2015-01-21 10:28             ` Michael Kerrisk (man-pages)
2015-01-20 18:23         ` Daniel Mack
     [not found]           ` <54BE9D08.7010804-cYrQPVfZoowdnm+yROfE0A@public.gmane.org>
2015-01-21 10:32             ` Michael Kerrisk (man-pages)
     [not found]               ` <54BF805B.4000609-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-21 15:19                 ` Theodore Ts'o
2015-01-21 16:58                 ` Daniel Mack
     [not found]                   ` <54BFDAAA.50203-cYrQPVfZoowdnm+yROfE0A@public.gmane.org>
2015-01-22 10:18                     ` Michael Kerrisk (man-pages)
     [not found]                       ` <54C0CE8A.5080805-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-22 13:46                         ` David Herrmann
2015-01-22 14:49                           ` Austin S Hemmelgarn
2015-01-23 16:08                             ` Greg Kroah-Hartman
     [not found]                               ` <20150123160854.GA5210-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2015-01-26 14:46                                 ` Michael Kerrisk (man-pages)
     [not found]                                   ` <54C65346.5070504-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-27 15:05                                     ` David Herrmann
     [not found]                                       ` <CANq1E4SkHhs1pWUe-TzG7bzk1M-Q++mB2vmQGuYx0RMF53wg4Q-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-01-27 16:03                                         ` Andy Lutomirski
     [not found]                                           ` <CALCETrU1O6LjhrXSH2aXWFt2WGh=ssrq4K9HD10f_Z55iT=Otw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-01-29  8:53                                             ` Daniel Mack
2015-01-29 11:25                                               ` Andy Lutomirski
2015-01-29 11:42                                                 ` Daniel Mack
2015-01-29 12:09                                                   ` Andy Lutomirski
     [not found]                                                     ` <CALCETrXD41=ohFSkCmBD8zPHFVUtr49QXMhYnChAxqQtmUjJYw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-02-02  9:34                                                       ` Daniel Mack
     [not found]                                                         ` <54CF44B9.8000005-cYrQPVfZoowdnm+yROfE0A@public.gmane.org>
2015-02-02 20:12                                                           ` Andy Lutomirski
     [not found]                                                             ` <CALCETrUh1Mse4CBQ4bfkJf+ew=kdpn46hMLS2QafLhfRTzQoBQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-02-03 10:09                                                               ` Daniel Mack
     [not found]                                                                 ` <54D09E6B.2020903-cYrQPVfZoowdnm+yROfE0A@public.gmane.org>
2015-02-04  0:41                                                                   ` Andy Lutomirski
     [not found]                                                                     ` <CALCETrV_q9Y4OWC6fA78WsD+XFhsdGHrqH6OK-hc=Vvj2F5C5w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-02-04  2:47                                                                       ` Eric W. Biederman
     [not found]                                                                         ` <87siemgzoo.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
2015-02-04  3:14                                                                           ` Greg Kroah-Hartman
     [not found]                                                                             ` <20150204031437.GB32207-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2015-02-04  6:30                                                                               ` Eric W. Biederman
2015-02-04 23:03                                                                   ` Andy Lutomirski
2015-02-05  0:16                                                                     ` David Herrmann
     [not found]                                                                       ` <CANq1E4ShseFuTp0wPrHM9mFmgA-y9Kqz1m0-FmU9qALuxQ8Qvg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-02-08 16:54                                                                         ` Andy Lutomirski
2015-01-27 18:03                                         ` Michael Kerrisk (man-pages)
     [not found]                           ` <CANq1E4S=3qNw599L85uj-8aXwxeV+mcurB_Nu_rHH8opAeePjw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-01-23 11:47                             ` Michael Kerrisk (man-pages)
2015-01-23 15:54                         ` Greg Kroah-Hartman
     [not found]                           ` <20150123155402.GB2159-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2015-01-26 14:42                             ` Michael Kerrisk (man-pages)
2015-01-26 15:26                               ` Tom Gundersen
     [not found]                                 ` <CAG-2HqUCZjc4Ucc-L21uG6e-di4UcLJAT5mW_-5-c-uqmoJyzA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-01-26 16:44                                   ` christoph Hellwig
2015-01-26 16:45                                 ` Michael Kerrisk (man-pages)
2015-01-27 15:23                                   ` David Herrmann
2015-01-27 17:53                                     ` Michael Kerrisk (man-pages)
     [not found]                                       ` <54C7D0A3.4000900-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-27 18:14                                         ` Daniel Mack
2015-01-28 10:46                                           ` Michael Kerrisk (man-pages)
2015-01-20 13:58     ` Michael Kerrisk (man-pages)
2015-01-20 17:50       ` Daniel Mack
     [not found]         ` <54BE957A.60305-cYrQPVfZoowdnm+yROfE0A@public.gmane.org>
2015-01-21  8:57           ` Michael Kerrisk (man-pages)
2015-01-21  9:07             ` Daniel Mack
     [not found]       ` <54BE5F1F.8070703-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-21  9:07         ` Michael Kerrisk (man-pages)
     [not found]           ` <54BF6C67.7090909-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2015-01-21  9:12             ` Daniel Mack
2015-01-23  6:28     ` Ahmed S. Darwish
2015-01-23 13:19       ` Greg Kroah-Hartman
     [not found]         ` <20150123131946.GA26302-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2015-01-23 13:29           ` Greg Kroah-Hartman
2015-01-25  3:30           ` Ahmed S. Darwish
2015-01-16 19:16 ` [PATCH 02/13] kdbus: add header file Greg Kroah-Hartman
2015-01-16 19:16 ` [PATCH 04/13] kdbus: add connection pool implementation Greg Kroah-Hartman
2015-01-16 19:16 ` [PATCH 06/13] kdbus: add node and filesystem implementation Greg Kroah-Hartman
2015-01-16 19:16 ` [PATCH 07/13] kdbus: add code to gather metadata Greg Kroah-Hartman
2015-01-16 19:16 ` [PATCH 08/13] kdbus: add code for notifications and matches Greg Kroah-Hartman
2015-01-16 19:16 ` Greg Kroah-Hartman [this message]
2015-01-16 19:16 ` [PATCH 13/13] kdbus: add selftests Greg Kroah-Hartman
     [not found] ` <1421435777-25306-1-git-send-email-gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
2015-01-16 19:16   ` [PATCH 03/13] kdbus: add driver skeleton, ioctl entry points and utility functions Greg Kroah-Hartman
2015-01-16 19:16   ` [PATCH 05/13] kdbus: add connection, queue handling and message validation code Greg Kroah-Hartman
2015-01-16 19:16   ` [PATCH 09/13] kdbus: add code for buses, domains and endpoints Greg Kroah-Hartman
2015-01-16 19:16   ` [PATCH 11/13] kdbus: add policy database implementation Greg Kroah-Hartman
2015-01-16 19:16   ` [PATCH 12/13] kdbus: add Makefile, Kconfig and MAINTAINERS entry Greg Kroah-Hartman
2015-01-16 22:07   ` [PATCH v3 00/13] Add kdbus implementation Josh Boyer
2015-01-16 22:18     ` Greg Kroah-Hartman
     [not found]       ` <20150116221824.GA512-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2015-01-17  0:26         ` Daniel Mack
     [not found]           ` <54B9AC3E.9070105-cYrQPVfZoowdnm+yROfE0A@public.gmane.org>
2015-01-17  0:41             ` Josh Boyer
2015-01-19 18:06   ` Johannes Stezenbach
2015-01-19 18:38     ` Greg Kroah-Hartman
     [not found]       ` <20150119183806.GA8479-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2015-01-19 20:19         ` Johannes Stezenbach
2015-01-19 20:31           ` Greg Kroah-Hartman
     [not found]             ` <20150119203155.GA15441-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2015-01-19 23:38               ` Johannes Stezenbach
     [not found]                 ` <20150119233812.GA1874-FF7aIK3TAVNeoWH0uzbU5w@public.gmane.org>
2015-01-20  1:13                   ` Greg Kroah-Hartman
2015-01-20 10:57                     ` Johannes Stezenbach
     [not found]                       ` <20150120105712.GA6260-FF7aIK3TAVNeoWH0uzbU5w@public.gmane.org>
2015-01-20 11:26                         ` Greg Kroah-Hartman
     [not found]                           ` <20150120112609.GA17198-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2015-01-20 13:24                             ` Johannes Stezenbach
2015-01-20 14:12                               ` Michael Kerrisk (man-pages)
     [not found]                     ` <20150120011359.GE865-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2015-01-26 21:32                       ` One Thousand Gnomes
2015-01-19 18:33   ` Johannes Stezenbach
2015-01-20 14:05   ` Michael Kerrisk (man-pages)
2015-01-20 14:15 ` Michael Kerrisk (man-pages)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1421435777-25306-11-git-send-email-gregkh@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=arnd@arndb.de \
    --cc=daniel@zonque.or \
    --cc=daniel@zonque.org \
    --cc=dh.herrmann@gmail.com \
    --cc=ebiederm@xmission.com \
    --cc=gnomes@lxorguk.ukuu.org.uk \
    --cc=jkosina@suse.cz \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luto@amacapital.net \
    --cc=teg@jklm.no \
    --cc=tixxdz@opendz.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).